Newer
Older
/* MELODIC - Multivariate exploratory linear optimized decomposition into
independent components
Christian F. Beckmann, FMRIB Analysis Group
Copyright (C) 1999-2013 University of Oxford */
/* CCOPYRIGHT */
#include <stdlib.h>
#include "utils/log.h"
#include "meloptions.h"
#include "meldata.h"
#include "melodic.h"
#include "melica.h"
#include "melpca.h"
#include "melhlprfns.h"
#include "miscmaths/miscprob.h"
using namespace Utilities;
using namespace NEWIMAGE;
namespace Melodic {
void MelodicICA::ica_fastica_symm(const Matrix &Data){
// based on Aapo Hyvrinen's fastica method
// see www.cis.hut.fi/projects/ica/fastica/
//initialize matrices
Matrix redUMM_old, rank1_old;
//srand((unsigned int)timer(NULL));
redUMM = melodat.get_white()*
unifrnd(melodat.get_white().Ncols(),dim); // got to start somewhere
if(opts.debug.value())
cerr << "redUMM init submatrix : " << endl << redUMM.SubMatrix(1,2,1,2) << endl;
if(opts.guessfname.value().size()>1){
<< " as initial values for the mixing matrix " <<endl);
Matrix guess ;
guess = melodat.get_white()*read_ascii_matrix(opts.guessfname.value());
redUMM.Columns(1,guess.Ncols()) = guess;
}
symm_orth(redUMM);
int itt_ctr,itt_ctr2=1,cum_itt=0,newmaxitts = opts.maxNumItt.value();
double minAbsSin = 1.0, minAbsSin2 = 1.0;
if(opts.approach.value() == string("tica")) {
opts.maxNumItt.set_T(opts.rank1interval.value());
rank1_old = melodat.get_dewhite()*redUMM;
rank1_old = melodat.expand_dimred(rank1_old);
rank1_old = krapprox(rank1_old,int(rank1_old.Nrows()/melodat.get_numfiles()));
}
do{// TICA loop
itt_ctr = 1;
do{ // da loop!!!
//calculate IC estimates
tmpU = Data.t() * redUMM;
//update redUMM depending on nonlinearity
if(opts.nonlinearity.value()=="pow4"){
redUMM = (Data * pow(tmpU,3.0)) / samples - 3 * redUMM;
}
if(opts.nonlinearity.value()=="pow3"){
tmpU /= opts.nlconst1.value();
(SP(ones(dim,1)*sum(tmpU,1),redUMM))/ samples;
}
if(opts.nonlinearity.value()=="tanh"){
Matrix hyptanh;
hyptanh = tanh(opts.nlconst1.value()*tmpU);
redUMM = (Data * hyptanh - opts.nlconst1.value()*SP(ones(dim,1)*
}
if(opts.nonlinearity.value()=="gauss"){
Matrix tmpUsq;
Matrix tmpU2;
Matrix gauss;
Matrix dgauss;
tmpUsq = pow(tmpU,2);
tmpU2 = exp(-(opts.nlconst2.value()/2) * tmpUsq);
gauss = SP(tmpU,tmpU2);
dgauss = SP(1-opts.nlconst2.value()*tmpUsq,tmpU2);
redUMM = (Data * gauss - SP(ones(dim,1)*
sum(dgauss,1),redUMM))/samples;
}
symm_orth(redUMM);
if(opts.approach.value() == string("tica")){
message("");
}
//termination condition : angle between old and new < epsilon
minAbsSin = 1 - diag(abs(redUMM.t()*redUMM_old)).Minimum();
message(" Step no. " << cum_itt + itt_ctr << " change : " << minAbsSin << endl);
// if((abs(minAbsSin) < opts.epsilon.value())&&
// (opts.approach.value()!=string("tica"))){ break;}
if((abs(minAbsSin) < opts.epsilon.value())){ break;}
itt_ctr++;
} while(itt_ctr < opts.maxNumItt.value());
cum_itt += itt_ctr;
itt_ctr2++;
if(opts.approach.value() == string("tica")){
message(" Rank-1 approximation of the time courses; ");
Matrix temp(melodat.get_dewhite() * redUMM);
temp = melodat.expand_dimred(temp);
temp = krapprox(temp,int(temp.Nrows()/melodat.get_numfiles()));
minAbsSin2 = 1 - diag(abs(corrcoef(temp,rank1_old))).Minimum();
rank1_old = temp;
temp = melodat.reduce_dimred(temp);
redUMM = melodat.get_white() * temp;
if(abs(minAbsSin2) < opts.epsilonS.value() && abs(minAbsSin) < opts.epsilon.value()){ break;}
}
} while(
(itt_ctr2 < newmaxitts/opts.maxNumItt.value()) &&
(opts.approach.value() == string("tica")) &&
cum_itt < newmaxitts);
if((itt_ctr>=opts.maxNumItt.value() && (opts.approach.value()!=string("tica")))
|| (cum_itt >= newmaxitts && opts.approach.value()==string("tica"))){
cerr << " No convergence after " << cum_itt <<" steps "<<endl;
} else {
message(" Convergence after " << cum_itt <<" steps " << endl << endl);
no_convergence = false;
{Matrix temp(melodat.get_dewhite() * redUMM);
melodat.set_mix(temp);}
{Matrix temp(redUMM.t()*melodat.get_white());
melodat.set_unmix(temp);}
void MelodicICA::ica_fastica_defl(const Matrix &Data){
if(!opts.explicitnums || opts.numICs.value()>dim){
message(" Using numICs:" << opts.numICs.value() << endl);
}
//redUMM = zeros(dim); // got to start somewhere
redUMM = melodat.get_white()*
unifrnd(melodat.get_white().Ncols(),opts.numICs.value());
redUMM = zeros(melodat.get_white().Nrows(),opts.numICs.value());
Matrix guess;
int guesses=0;
if(opts.guessfname.value().size()>1){
message(" Use columns in " << opts.guessfname.value() << " as initial values for the mixing matrix " <<endl);
guess = melodat.get_white()*read_ascii_matrix(opts.guessfname.value());
guesses = guess.Ncols();
}
int ctrIC = 1;
int numRestart = 0;
message(" Extracting IC " << ctrIC << " ... ");
ColumnVector w;
ColumnVector tmpU;
if(ctrIC <= guesses){
w = w - redUMM * redUMM.t() * w;
w_old = zeros(w.Nrows(),1);
do{
w_old = w;
if(opts.nonlinearity.value()=="pow4"){
w = (Data * pow(tmpU,3.0)) / samples - 3 * w;
}
if(opts.nonlinearity.value()=="tanh"){
ColumnVector hyptanh;
hyptanh = tanh(opts.nlconst1.value()*tmpU);
w = (Data * hyptanh - opts.nlconst1.value()*SP(ones(dim,1)*
sum(1-pow(hyptanh,2),1),w))/samples;
if(opts.nonlinearity.value()=="pow3"){
tmpU /= opts.nlconst1.value();
w = 3*(Data * pow(tmpU,2.0)) / samples - 2*(SP(ones(dim,1)*
sum(tmpU,1),w))/samples;
if(opts.nonlinearity.value()=="gauss"){
ColumnVector tmpUsq;
ColumnVector tmpU2;
ColumnVector gauss;
ColumnVector dgauss;
tmpUsq = pow(tmpU,2);
tmpU2 = exp(-(opts.nlconst2.value()/2) * tmpUsq);
gauss = SP(tmpU,tmpU2);
dgauss = SP(1-opts.nlconst2.value()*tmpUsq,tmpU2);
w = (Data * gauss - SP(ones(dim,1)*
sum(dgauss,1),w))/samples;
}
// orthogonalize w
w = w - redUMM * redUMM.t() * w;
//termination condition : angle between old and new < epsilon
if(((norm2(w-w_old) < 0.001*opts.epsilon.value())&&(itt_ctr>10)) ||
((norm2(w+w_old) < 0.001*opts.epsilon.value())&&(itt_ctr>10))){
break;
}
//cout << norm2(w-w_old) << " " << norm2(w+w_old) << endl;
itt_ctr++;
} while(itt_ctr <= opts.maxNumItt.value());
if(itt_ctr<opts.maxNumItt.value()){
redUMM.Column(ctrIC) = w;
message(" estimated using " << itt_ctr << " iterations " << endl);
numRestart = 0;
} else{
if(numRestart > opts.maxRestart.value()){
message(endl << " Estimation failed after "
<< numRestart << " attempts "
<< " giving up " << endl);
break;
}else{
numRestart++;
<< numRestart << endl);
}
}
}
if(numRestart < opts.maxRestart.value()){
no_convergence = false;
{Matrix temp(melodat.get_dewhite() * redUMM);
melodat.set_mix(temp);}
{Matrix temp(redUMM.t()*melodat.get_white());
melodat.set_unmix(temp);}
}
}
}
void MelodicICA::ica_maxent(const Matrix &Data){
// based on Aapo Hyvrinen's fastica method
message(" MAXENT " << endl);
//initialize matrices
Matrix redUMM_old;
Matrix gtmpU;
double lambda = 0.015/std::log((double)melodat.get_white().Ncols());
//srand((unsigned int)timer(NULL));
redUMM = melodat.get_white()*
unifrnd(melodat.get_white().Ncols(),dim); // got to start somewhere
if(opts.guessfname.value().size()>1){
<< " as initial values for the mixing matrix " <<endl);
Matrix guess ;
guess = melodat.get_white()*read_ascii_matrix(opts.guessfname.value());
redUMM.Columns(1,guess.Ncols()) = guess;
}
// symm_orth(redUMM);
double minAbsSin = 1.0;
Matrix Id;
Id = IdentityMatrix(redUMM.Ncols());
//cerr << " nonlinearity : " << opts.nonlinearity.value() << endl;
do{ // da loop!!!
//calculate IC estimates
tmpU = Data.t() * redUMM;
if(opts.nonlinearity.value()=="tanh"){
//Matrix hyptanh;
//hyptanh = tanh(opts.nlconst1.value()*tmpU);
//redUMM = (Data * hyptanh - opts.nlconst1.value()*SP(ones(dim,1)*
//sum(1-pow(hyptanh,2),1),redUMM))/samples;
gtmpU = tanh(opts.nlconst1.value()*tmpU);
redUMM = redUMM + lambda*(Id+(1-2*gtmpU.t()*tmpU))*redUMM;
}
if(opts.nonlinearity.value()=="gauss"){
gtmpU = pow(1+exp(-(opts.nlconst2.value()/2) * tmpU),-1);
redUMM = redUMM + lambda*(Id - (gtmpU.t()-tmpU.t())*tmpU)*redUMM;
}
//termination condition : angle between old and new < epsilon
minAbsSin = abs(1 - diag(abs(redUMM.t()*redUMM_old)).Minimum());
message(" Step no. " << itt_ctr << " change : " << minAbsSin << endl);
if(abs(minAbsSin) < opts.epsilon.value()){ break;}
itt_ctr++;
} while(itt_ctr < opts.maxNumItt.value());
if(itt_ctr>=opts.maxNumItt.value()){
cerr << " No convergence after " << itt_ctr <<" steps "<<endl;
} else {
message(" Convergence after " << itt_ctr <<" steps " << endl << endl);
no_convergence = false;
{Matrix temp(melodat.get_dewhite() * redUMM);
melodat.set_mix(temp);}
{Matrix temp(redUMM.t()*melodat.get_white());
melodat.set_unmix(temp);}
void MelodicICA::ica_jade(const Matrix &Data){
int dim_sym = (int) (dim*(dim+1))/2;
int num_CM = dim_sym;
Matrix CM;
Matrix R; R = IdentityMatrix(dim);
Matrix Qij; Qij = zeros(dim);
Matrix Xim;
Matrix Xjm;
Matrix scale; scale = ones(dim,1)/samples;
for (int im =1; im <= dim; im++){
Xim = Data.Row(im);
write_ascii_matrix("Xim",Data.Row(1));
//Qij = SP((scale * pow(Xim,2)),Data) * Data.t();//- R - 2*R.Column(im)*R.Column(im).t();
Qij = (pow(Xim,2)) * Data.t();//- R - 2*R.Column(im)*R.Column(im).t();
if(im==1){CM = Qij; write_ascii_matrix("CM",CM);exit(2);}else{CM |= Qij;}
for (int jm = 1; jm < im; jm++){
Xjm = Data.Row(jm);
R.Column(im)*R.Column(jm).t() - R.Column(jm)*R.Column(im).t());
Qij *= sqrt(2);
CM |= Qij;
}
}
write_ascii_matrix("CM",CM);
Matrix redUMM; redUMM = IdentityMatrix(dim);
bool exitloop = false;
int ctr_itt = 0;
int ctr_updates = 0;
Matrix Givens; Givens = zeros(2,num_CM);
Matrix Givens_ip; Givens_ip = zeros(2);
Matrix Givens_ro; Givens_ro = zeros(2);
double det_on, det_off;
double cos_theta, sin_theta, theta;
while(!exitloop && ctr_itt <= opts.maxNumItt.value()){
ctr_itt++;
cout << "loop" <<endl;
for(int ctr_p = 1; ctr_p < dim; ctr_p++){
for(int ctr_q = ctr_p+1; ctr_q <= dim; ctr_q++){
for(int ctr_i = 0; ctr_i < num_CM; ctr_i++){
int Ip = ctr_p + ctr_i * dim;
int Iq = ctr_q + ctr_i * dim;
Givens(1,ctr_i + 1) = CM(ctr_p,Ip) - CM(ctr_q,Iq);
Givens(2,ctr_i + 1) = CM(ctr_p,Iq) - CM(ctr_q,Ip);
}
Givens_ip = Givens * Givens.t();
det_on = Givens_ip(1,1) - Givens_ip(2,2);
det_off = Givens_ip(1,2) + Givens_ip(2,1);
theta = 0.5 * atan2(det_off, det_on + sqrt(det_on*det_on + det_off*det_off));
cout << theta << endl;
if(abs(theta) > opts.epsilon.value()){
ctr_updates++;
message(" Step No. "<< ctr_itt << " change : " << theta << endl);
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
//create Givens rotation matrix
cos_theta = cos(theta);
sin_theta = sin(theta);
Givens_ro(1,1) = cos_theta;
Givens_ro(1,2) = -sin_theta;
Givens_ro(2,1) = sin_theta;
Givens_ro(2,2) = cos_theta;
//update 2 entries of redUMM
{Matrix tmp_redUMM;
tmp_redUMM = redUMM.Column(ctr_p);
tmp_redUMM |= redUMM.Column(ctr_q);
tmp_redUMM *= Givens_ro;
redUMM.Column(ctr_p) = tmp_redUMM.Column(1);
redUMM.Column(ctr_q) = tmp_redUMM.Column(2);}
//update Cumulant matrix
{Matrix tmp_CM;
tmp_CM = CM.Row(ctr_p);
tmp_CM &= CM.Row(ctr_q);
tmp_CM = Givens_ro.t() * tmp_CM;
CM.Row(ctr_p) = tmp_CM.Row(1);
CM.Row(ctr_q) = tmp_CM.Row(2);}
//update Cumulant matrices
for(int ctr_i = 0; ctr_i < num_CM; ctr_i++){
int Ip = ctr_p + ctr_i * dim;
int Iq = ctr_q + ctr_i * dim;
CM.Column(Ip) = cos_theta*CM.Column(Ip)+sin_theta*CM.Column(Iq);
CM.Column(Iq) = cos_theta*CM.Column(Iq)-sin_theta*CM.Column(Ip);
}
}else{
exitloop = true;
}
}
}
}//while loop
if(ctr_itt > opts.maxNumItt.value()){
cerr << " No convergence after " << ctr_itt <<" steps "<<endl;
} else {
message(" Convergence after " << ctr_itt <<" steps " << endl << endl);
no_convergence = false;
{Matrix temp(melodat.get_dewhite() * redUMM);
melodat.set_mix(temp);}
{Matrix temp(redUMM.t()*melodat.get_white());
melodat.set_unmix(temp);}
}
}
Matrix MelodicICA::sign(const Matrix &Inp){
Matrix Res = Inp;
Res = 1;
for(int ctr_i = 1; ctr_i <= Inp.Ncols(); ctr_i++){
for(int ctr_j = 1; ctr_j <= Inp.Nrows(); ctr_j++){
if(Inp(ctr_j,ctr_i)<0){Res(ctr_j,ctr_i)=-1;}
}
return Res;
}
void MelodicICA::perf_ica(const Matrix &Data){
message("Starting ICA estimation using " << opts.approach.value()
<< endl << endl);
dim = Data.Nrows();
samples = Data.Ncols();
no_convergence = true;
if(opts.approach.value()==string("symm") ||
opts.approach.value()==string("tica") ||
opts.approach.value()==string("parafac") ||
opts.approach.value()==string("concat"))
ica_fastica_symm(Data);
if(opts.approach.value()==string("defl"))
ica_fastica_defl(Data);
if(opts.approach.value()==string("jade"))
ica_jade(Data);
if(opts.approach.value()==string("maxent"))
ica_maxent(Data);
if(!no_convergence){//calculate the IC
Matrix temp(melodat.get_unmix()*melodat.get_Data());
// temp += melodat.get_unmix()*melodat.get_meanC()*ones(1,temp.Ncols());
//re-normalise the decomposition to std(mix)=1
Matrix scales;
//cerr << " SCALES 1 " << scales << endl;
Matrix tmp, tmp2;
tmp = SP(melodat.get_mix(),ones(melodat.get_mix().Nrows(),1)*pow(scales,-1));
temp = SP(temp,scales.t()*ones(1,temp.Ncols()));
scales = scales.t();
melodat.set_mix(tmp);
melodat.set_IC(temp);
melodat.set_ICstats(scales);
melodat.sort();
//message("Calculating T- and S-modes " << endl);
melodat.set_TSmode();