% BINICA - Run stand-alone binary version of RUNICA from the
% Matlab command line. Saves time and memory relative
% to RUNICA. If stored in a float file, data are not
% read into Matlab, and so may be larger than Matlab
% can handle owing to memory limitations.
%
% Download the binary version for your system at
% https://sccn.ucsd.edu/wiki/Binica
%
% Usage:
% >> [wts,sph] = binica( datavar, 'key1', arg1, 'key2', arg2 ...);
% else
% >> [wts,sph] = binica('datafile', chans, frames, 'key1', arg1, ...);
%
% Inputs:
% datavar - (chans,frames) data matrix in the Matlab workspace
% datafile - quoted 'filename' of float data file multiplexed by channel
% channels - number of channels in datafile (not needed for datavar)
% frames - number of frames (time points) in datafile (only)
%
% Optional flag,argument pairs:
% 'extended' - int>=0 [0 default: assume no subgaussian comps]
% Search for subgaussian comps: 'extended',1 is recommended
% 'pca' - int>=0 [0 default: don't reduce data dimension]
% NB: 'pca' reduction not recommended unless necessary
% 'sphering' - 'on'/'off' first 'sphere' the data {default: 'on'}
% 'lrate' - (0<float<<1) starting learning rate {default: 1e-4}
% 'blocksize' - int>=0 [0 default: heuristic, from data size]
% 'maxsteps' - int>0 {default: 512}
% 'stop' - (0<float<<<1) stopping learning rate {default: 1e-7}
% NB: 'stop' <= 1e-7 recommended
% 'weightsin' - Filename string of initial weight matrix of size
% (comps,chans) floats, else a weight matrix variable
% in the current Matlab workspace (copied to a local
% .inwts files). You may want to reduce the starting
% 'lrate' arg (above) when resuming training, and/or
% to reduce the 'stop' arg (above). By default, binary
% ica begins with the identity matrix after sphering.
% 'verbose' - 'on'/'off' {default: 'off'}
% 'filenum' - the number to be used in the name of the output files.
% Otherwise chosen randomly. Will choose random number
% if file with that number already exists.
%
% Less frequently used input flags:
% 'posact' - ('on'/'off') Make maximum value for each comp positive.
% NB: 'off' recommended. {default: 'off'}
% 'annealstep' - (0<float<1) {default: 0.98}
% 'annealdeg' - (0<n<360) {default: 60}
% 'bias' - 'on'/'off' {default: 'on'}
% 'momentum' - (0<float<1) {default: 0 = off]
%
% Outputs:
% wts - output weights matrix, size (ncomps,nchans)
% sph - output sphere matrix, size (nchans,nchans)
% Both files are read from float files left on disk
% stem - random integer used in the names of the .sc, .wts,
% .sph, and if requested, .intwts files
%
% Author: Scott Makeig, SCCN/INC/UCSD, La Jolla, 2000
%
% See also: RUNICA
% Calls binary translation of RUNICA by Sigurd Enghoff
% Copyright (C) 2000 Scott Makeig, SCCN/INC/UCSD, scott@sccn.ucsd.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.
% 08/07/00 Added warning to update icadefs.m -sm
% 09/08/00 Added tmpint to script, weights and sphere files to avoid
% conflicts when multiple binica sessions run in the same pwd -sm
% 10/07/00 Fixed bug in reading wts when 'pca' ncomps < nchans -sm
% 07/18/01 replaced var ICA with ICABINARY to try to avoid Matlab6 bug -sm
% 11/06/01 add absolute path of files (lines 157-170 & 198) -ad
% 01-25-02 reformated help & license, added links -ad
function [wts,sph,tmpint] = binica(data,var2,var3,var4,var5,var6,var7,var8,var9,var10,var11,var12,var13,var14,var15,var16,var17,var18,var19,var20,var21,var22,var23,var24,var25)
if nargin < 1 || nargin > 25
more on
help binica
more off
return
end
if size(data,3) > 1, data = reshape(data, size(data,1), size(data,2)*size(data,3) ); end
if any(pwd == ' ')
error('The current path cannot contain spaces');
end
icadefs % import ICABINARY and SC
if ~contains(ICABINARY, 'eeglab')
eeglab_p = fileparts(which('eeglab'));
ICABINARY = fullfile(eeglab_p, 'functions', 'supportfiles', ICABINARY); % done here and not icadefs because slow
end
if ~exist('SC')
fprintf('binica(): You need to update your icadefs file to include ICABINARY and SC.\n')
return
end
if exist(SC) ~= 2
fprintf('binica(): No ica source file ''%s'' is in your Matlab path, check...\n', SC);
return
else
SC = which(SC);
fprintf('binica: using source file ''%s''\n', SC);
end
if exist(ICABINARY) ~= 2
fprintf('binica(): ica binary ''%s'' is not in your Matlab path, check\n', ICABINARY);
return
else
ICABINARYdir = which(ICABINARY);
if ~isempty(ICABINARYdir)
fprintf('binica(): using binary ica file ''%s''\n', ICABINARYdir);
else
fprintf('binica(): using binary ica file ''\?/%s''\n', ICABINARY);
end
end
if any(ICABINARYdir == ' ')
error('The ICABINARY path defined in icadefs.m cannot contain spaces');
end
[flags,args] = read_sc(SC); % read flags and args in master SC file
%
% substitute the flags/args pairs in the .sc file
%
tmpint=[];
if ~ischar(data) % data variable given
firstarg = 2;
else % data filename given
firstarg = 4;
end
arg = firstarg;
if arg > nargin
fprintf('binica(): no optional (flag, argument) pairs received.\n');
else
if (nargin-arg+1)/2 > 1
fprintf('binica(): processing %d (flag, arg) pairs.\n',(nargin-arg+1)/2);
else
fprintf('binica(): processing one (flag, arg) pair.\n');
end
while arg <= nargin %%%%%%%%%%%% process flags & args %%%%%%%%%%%%%%%%
eval(['OPTIONFLAG = var' int2str(arg) ';']);
% NB: Found that here Matlab var 'FLAG' is (64,3) why!?!?
if arg == nargin
fprintf('\nbinica(): Flag %s needs an argument.\n',OPTIONFLAG)
return
end
eval(['Arg = var' int2str(arg+1) ';']);
if strcmpi(OPTIONFLAG,'pca')
ncomps = Arg; % get number of components out for reading wts.
end
if strcmpi(OPTIONFLAG,'weightsin')
wtsin = Arg;
if exist('wtsin') == 2 % file
fprintf(' setting %s, %s\n','weightsin',Arg);
elseif exist('wtsin') == 1 % variable
nchans = size(data,1); % by nima
if size(wtsin,2) ~= nchans
fprintf('weightsin variable must be of width %d\n',nchans);
return
end
else
fprintf('weightsin variable not found.\n');
return
end
end
if strcmpi(OPTIONFLAG,'filenum')
tmpint = Arg; % get number for name of output files
if ~isnumeric(tmpint)
fprintf('\nbinica(): FileNum argument needs to be a number. Will use random number instead.\n')
tmpint=[];
end
tmpint=int2str(tmpint);
end
arg = arg+2;
nflags = length(flags);
for f=1:nflags % replace SC arg with Arg passed from commandline
if strcmp(OPTIONFLAG,flags{f})
args{f} = num2str(Arg);
fprintf(' setting %s, %s\n',flags{f},args{f});
end
end
end
end
%
% select random integer 1-10000 to index the binica data files
% make sure no such script file already exists in the pwd
%
scriptfile = ['binica' tmpint '.sc'];
while exist(scriptfile)
tmpint = int2str(round(rand*10000));
scriptfile = ['binica' tmpint '.sc'];
end
fprintf('scriptfile = %s\n',scriptfile);
nchans = 0;
tmpdata = [];
if ~ischar(data) % data variable given
if ~exist('data')
fprintf('\nbinica(): Variable name data not found.\n');
return
end
nchans = size(data,1);
nframes = size(data,2);
tmpdata = ['binica' tmpint '.fdt'];
if strcmpi(computer, 'MAC')
floatwrite(data,tmpdata,'ieee-be');
else
floatwrite(data,tmpdata);
end
datafile = tmpdata;
else % data filename given
if ~exist(data)
fprintf('\nbinica(): File data not found.\n')
return
end
datafile = data;
if nargin<3
fprintf(...
'\nbinica(): Data file name must be followed by chans, frames\n');
return
end
nchans = var2;
nframes = var3;
if ischar(nchans) || ischar(nframes)
fprintf(...
'\nbinica(): chans, frames args must be given after data file name\n');
return
end
end
%
% insert name of data files, chans and frames
%
for x=1:length(flags)
if strcmp(flags{x},'DataFile')
datafile = fullfile(pwd, datafile);
args{x} = datafile;
elseif strcmp(flags{x},'WeightsOutFile')
weightsfile = ['binica' tmpint '.wts'];
weightsfile = fullfile(pwd, weightsfile);
args{x} = weightsfile;
elseif strcmp(flags{x},'WeightsTempFile')
weightsfile = ['binicatmp' tmpint '.wts'];
weightsfile = fullfile(pwd, weightsfile);
args{x} = weightsfile;
elseif strcmp(flags{x},'SphereFile')
spherefile = ['binica' tmpint '.sph'];
spherefile = fullfile(pwd, spherefile);
args{x} = spherefile;
elseif strcmp(flags{x},'chans')
args{x} = int2str(nchans);
elseif strcmp(flags{x},'frames')
args{x} = int2str(nframes);
end
end
%
% write the new .sc file
%
fid = fopen(scriptfile,'w');
for x=1:length(flags)
fprintf(fid,'%s %s\n',flags{x},args{x});
end
if exist('wtsin') % specify WeightsInfile from 'weightsin' flag, arg
if exist('wtsin') == 1 % variable
winfn = fullfile(pwd, [ 'binica' tmpint '.inwts']);
if strcmpi(computer, 'MAC')
floatwrite(wtsin,winfn,'ieee-be');
else
floatwrite(wtsin,winfn);
end
fprintf(' saving input weights:\n ');
weightsinfile = winfn; % weights in file name
elseif exist(wtsin) == 2 % file
weightsinfile = wtsin;
weightsinfile = fullfile(pwd, weightsinfile);
else
fprintf('binica(): weightsin file|variable not found.\n');
return
end
try
eval(['!ls -l ' weightsinfile]);
catch
end
fprintf(fid,'%s %s\n','WeightsInFile',weightsinfile);
end
fclose(fid);
if ~exist(scriptfile)
fprintf('\nbinica(): ica script file %s not written.\n',...
scriptfile);
return
end
%
% %%%%%%%%%%%%%%%%%%%%%% run binary ica %%%%%%%%%%%%%%%%%%%%%%%%%
%
fprintf('\nRunning ica from script file %s\n',scriptfile);
if exist('ncomps')
fprintf(' Finding %d components.\n',ncomps);
end
eval_call = ['!' ICABINARY '<' fullfile(pwd, scriptfile) ];
eval(eval_call);
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
% read in wts and sph results.
%
if ~exist('ncomps')
ncomps = nchans;
end
if strcmpi(computer, 'MAC')
wts = floatread(weightsfile,[ncomps Inf],'ieee-be',0);
sph = floatread(spherefile,[nchans Inf],'ieee-be',0);
else
wts = floatread(weightsfile,[ncomps Inf],[],0);
sph = floatread(spherefile,[nchans Inf],[],0);
end
if isempty(wts)
fprintf('\nbinica(): weight matrix not read.\n')
return
end
if isempty(sph)
fprintf('\nbinica(): sphere matrix not read.\n')
return
end
fprintf('\nbinary ica files left in pwd:\n');
try
eval(['!ls -l ' scriptfile ' ' weightsfile ' ' spherefile]);
catch
end
if exist('wtsin')
try
eval(['!ls -l ' weightsinfile]);
catch
end
end
fprintf('\n');
if ischar(data)
whos wts sph
else
whos data wts sph
end
%
% If created by BINICA, rm temporary data file
% NOTE: doesn't remove the .sc .wts and .fdt files
if ~isempty(tmpdata)
try
delete(datafile);
catch
end
end
%
%%%%%%%%%%%%%%%%%%% included functions %%%%%%%%%%%%%%%%%%%%%%
%
function sout = rmcomment(s,symb)
n =1;
while s(n)~=symb % discard # comments
n = n+1;
end
if n == 1
sout = [];
else
sout = s(1:n-1);
end
function sout = rmspace(s)
n=1; % discard leading whitespace
while n<length(s) & isspace(s(n))
n = n+1;
end
if n<length(s)
sout = s(n:end);
else
sout = [];
end
function [word,sout] = firstword(s)
n=1;
while n<=length(s) & ~isspace(s(n))
n = n+1;
end
if n>length(s)
word = [];
sout = s;
else
word = s(1:n-1);
sout = s(n:end);
end
function [flags,args] = read_sc(master_sc)
%
% read in the master ica script file SC
%
flags = [];
args = [];
fid = fopen(master_sc,'r');
if fid < 0
fprintf('\nbinica(): Master .sc file %s not read!\n',master_sc)
return
end
%
% read SC file info into flags and args lists
%
s = [];
f = 0; % flag number in file
while isempty(s) | s ~= -1
s = fgetl(fid);
if s ~= -1
if ~isempty(s)
s = rmcomment(s,'#');
if ~isempty(s)
f = f+1;
s = rmspace(s);
[w s]=firstword(s);
if ~isempty(s)
flags{f} = w;
s = rmspace(s);
[w s]=firstword(s);
args{f} = w;
end
end
end
end
end
fclose(fid);