Simple ICA
Igor
#pragma rtGlobals=3 // Use modern global access method.
#pragma IgorVersion=6.2 // Requires Igor 6.2
//
// simpleICA(inX,reqComponents,w_init)
// Parameters:
// inX is a 2D wave where columns contain a finite mix of independent components.
// reqComponents is the number of independent components that you want to extract.
// This number must be less than or equal to the number of columns of inX.
// w_init is a 2D wave of dimensions (reqComponents x reqComponents) and contains
// an estimate of the mixing matrix. You can simply pass $"" for this parameter
// so the algorithm will use an equivalent size matrix filled with enoise().
//
// The results of the function are the waves ICARes and WunMixing. ICARes is a 2D
// wave in which each column contains an independent component. WunMixing is a 2D
// wave that can be used to multiply the (re)conditioned input in order to obtain the unmixed
// components.
//
// The code below implements the "deflation" approach for fastICA. It is based on the
// fastICA algorithm: Hyvärinen,A (1999). Fast and Robust Fixed-Point Algorithms
// for Independent Component Analysis. IEEE Transactions on Neural Networks, 10(3),626-634.
//
// See testing example below the main function.
Function simpleICA(inX,reqComponents,w_init)
Wave inX,w_init
Variable reqComponents
// The following 3 variables can be converted into function arguments.
Variable maxNumIterations=1000
Variable tolerance=1e-5
Variable alpha=1
Variable i,ii
Variable iteration
Variable nRows=DimSize(inX,0)
Variable nCols=DimSize(inX,1)
// check the number of requested components:
if(reqComponents>min(dimSize(inX,0),dimSize(inX,1)))
doAlert 0,"Bad requested number of components"
return 0
endif
// Never mess up the original data
Duplicate/O/Free inX,xx
// Initialize the w matrix if it is not provided.
if(WaveExists(w_init)==0)
Make/O/N=(reqComponents,reqComponents) w_init=enoise(1)
endif
// condition and transpose the input:
MatrixOP/O xx=(NormalizeCols(subtractMean(xx,1)))^t
// Just like PCA:
MatrixOP/O/Free V=(xx x (xx^t))/nRows
MatrixSVD V
// M_VT is not used here.
Wave M_U,W_W,M_VT
W_W=1.0/sqrt(w_w)
MatrixOP/O/Free D=diagonal(W_W)
MatrixOP/O/FREE K=D x (M_U^t)
KillWaves/z W_W,M_U,M_VT
Duplicate/Free/R=[0,reqComponents-1][] k,kk
Duplicate/O/FREE kk,k
// X1 could be output as PCA result.
MatrixOP/O/FREE X1=K x xx
// create and initialize working W; this is not an output!
Make/O/Free/N=(reqComponents,reqComponents) W=0
for(i=1;i<=reqComponents;i+=1)
MatrixOP/O/FREE lcw=row(w_init,i-1)
// decorrelating
if(i>1)
Duplicate/O/Free lcw,tt
tt=0
for(ii=0;ii<i;ii+=1)
MatrixOP/O/Free r_ii=row(W,ii) // row ii of matrix W
MatrixOP/O/FREE ru=sum(lcw*r_ii) // dot product
Variable ks=ru[0]
MatrixOP/O/Free tt=tt+ks*r_ii
endfor
MatrixOP/O/FREE lcw=lcw-tt
endif
MatrixOP/O/Free lcw=normalize(lcw)
// iterate till convergence:
for(iteration=1;iteration<maxNumIterations;iteration+=1)
MatrixOP/O/Free wxProduct=lcw x x1
// should be supported by matrixop :(
Duplicate/O/Free wxProduct,gwx
gwx=tanh(alpha*wxProduct)
Duplicate/Free/R=[reqComponents,nRows] gwx,gwxf
Make/O/Free/N=(reqComponents,nRows) gwxf
// repeat the values from the first row on.
gwxf=gwx[q]
Duplicate/O/FREE gwxf,gwx
MatrixOP/O/Free x1gwxProd=x1*gwx
Duplicate/O/FREE wxProduct,gwx2
gwx2=alpha*(1-(tanh(alpha*wxProduct))^2)
Variable theMean=mean(gwx2)
MatrixOP/O/Free wPlus=(sumRows(x1gwxProd)/numCols(x1gwxProd))^t-theMean*lcw
// reduce components
Redimension/N=(1,reqComponents) wPlus
// starting from the second component;
if(i>1)
Duplicate/O/FREE wPlus,tt
tt=0
for(ii=0;ii<i;ii+=1)
MatrixOP/O/Free r_ii=row(W,ii)
MatrixOP/O/FREE ru=wPlus.(r_ii^t)
ks=ru[0]
MatrixOP/O tt=tt+ks*r_ii
endfor
wPlus=wPlus-tt
endif
MatrixOP/O/FREE wPlus=normalize(wPlus)
MatrixOP/O/Free limV=abs(mag(sum(wPlus*lcw))-1)
printf "Iteration %d, diff=%g\r",iteration,limV[0]
lcw=wPlus
if(limV[0]<tolerance)
break
endif
endfor
// store the computed row in final W.
W[i-1][]=lcw[q]
endfor // loop over components
// Calculate the un-mixing matrix
MatrixOP/O WunMixing=W x K
// Un-mix;
MatrixOP/O ICARes=(WunMixing x xx)^t
End
Function testICA()
// Create distinct frequencies:
Make/O/N=(1000,3) ddd
ddd[][0]=sin(2*pi*x/13)
ddd[][1]=sin(2*pi*x/17)
ddd[][2]=sin(2*pi*x/23)
// Create mixing matrix:
Make/O/N=(3,3) AA
AA[0][0]= {0.291,0.6557,-0.5439}
AA[0][1]= {0.5572,0.3,-0.2}
AA[0][2]= {-0.1,-0.7,0.4}
// Do your mixing:
MatrixOP/O xx=ddd x AA
// Try the ICA
simpleICA(xx,3,$"")
// Plot the results:
Wave ICARes
Display ICARes[][0]
Display ICARes[][1]
Display ICARes[][2]
End
#pragma IgorVersion=6.2 // Requires Igor 6.2
//
// simpleICA(inX,reqComponents,w_init)
// Parameters:
// inX is a 2D wave where columns contain a finite mix of independent components.
// reqComponents is the number of independent components that you want to extract.
// This number must be less than or equal to the number of columns of inX.
// w_init is a 2D wave of dimensions (reqComponents x reqComponents) and contains
// an estimate of the mixing matrix. You can simply pass $"" for this parameter
// so the algorithm will use an equivalent size matrix filled with enoise().
//
// The results of the function are the waves ICARes and WunMixing. ICARes is a 2D
// wave in which each column contains an independent component. WunMixing is a 2D
// wave that can be used to multiply the (re)conditioned input in order to obtain the unmixed
// components.
//
// The code below implements the "deflation" approach for fastICA. It is based on the
// fastICA algorithm: Hyvärinen,A (1999). Fast and Robust Fixed-Point Algorithms
// for Independent Component Analysis. IEEE Transactions on Neural Networks, 10(3),626-634.
//
// See testing example below the main function.
Function simpleICA(inX,reqComponents,w_init)
Wave inX,w_init
Variable reqComponents
// The following 3 variables can be converted into function arguments.
Variable maxNumIterations=1000
Variable tolerance=1e-5
Variable alpha=1
Variable i,ii
Variable iteration
Variable nRows=DimSize(inX,0)
Variable nCols=DimSize(inX,1)
// check the number of requested components:
if(reqComponents>min(dimSize(inX,0),dimSize(inX,1)))
doAlert 0,"Bad requested number of components"
return 0
endif
// Never mess up the original data
Duplicate/O/Free inX,xx
// Initialize the w matrix if it is not provided.
if(WaveExists(w_init)==0)
Make/O/N=(reqComponents,reqComponents) w_init=enoise(1)
endif
// condition and transpose the input:
MatrixOP/O xx=(NormalizeCols(subtractMean(xx,1)))^t
// Just like PCA:
MatrixOP/O/Free V=(xx x (xx^t))/nRows
MatrixSVD V
// M_VT is not used here.
Wave M_U,W_W,M_VT
W_W=1.0/sqrt(w_w)
MatrixOP/O/Free D=diagonal(W_W)
MatrixOP/O/FREE K=D x (M_U^t)
KillWaves/z W_W,M_U,M_VT
Duplicate/Free/R=[0,reqComponents-1][] k,kk
Duplicate/O/FREE kk,k
// X1 could be output as PCA result.
MatrixOP/O/FREE X1=K x xx
// create and initialize working W; this is not an output!
Make/O/Free/N=(reqComponents,reqComponents) W=0
for(i=1;i<=reqComponents;i+=1)
MatrixOP/O/FREE lcw=row(w_init,i-1)
// decorrelating
if(i>1)
Duplicate/O/Free lcw,tt
tt=0
for(ii=0;ii<i;ii+=1)
MatrixOP/O/Free r_ii=row(W,ii) // row ii of matrix W
MatrixOP/O/FREE ru=sum(lcw*r_ii) // dot product
Variable ks=ru[0]
MatrixOP/O/Free tt=tt+ks*r_ii
endfor
MatrixOP/O/FREE lcw=lcw-tt
endif
MatrixOP/O/Free lcw=normalize(lcw)
// iterate till convergence:
for(iteration=1;iteration<maxNumIterations;iteration+=1)
MatrixOP/O/Free wxProduct=lcw x x1
// should be supported by matrixop :(
Duplicate/O/Free wxProduct,gwx
gwx=tanh(alpha*wxProduct)
Duplicate/Free/R=[reqComponents,nRows] gwx,gwxf
Make/O/Free/N=(reqComponents,nRows) gwxf
// repeat the values from the first row on.
gwxf=gwx[q]
Duplicate/O/FREE gwxf,gwx
MatrixOP/O/Free x1gwxProd=x1*gwx
Duplicate/O/FREE wxProduct,gwx2
gwx2=alpha*(1-(tanh(alpha*wxProduct))^2)
Variable theMean=mean(gwx2)
MatrixOP/O/Free wPlus=(sumRows(x1gwxProd)/numCols(x1gwxProd))^t-theMean*lcw
// reduce components
Redimension/N=(1,reqComponents) wPlus
// starting from the second component;
if(i>1)
Duplicate/O/FREE wPlus,tt
tt=0
for(ii=0;ii<i;ii+=1)
MatrixOP/O/Free r_ii=row(W,ii)
MatrixOP/O/FREE ru=wPlus.(r_ii^t)
ks=ru[0]
MatrixOP/O tt=tt+ks*r_ii
endfor
wPlus=wPlus-tt
endif
MatrixOP/O/FREE wPlus=normalize(wPlus)
MatrixOP/O/Free limV=abs(mag(sum(wPlus*lcw))-1)
printf "Iteration %d, diff=%g\r",iteration,limV[0]
lcw=wPlus
if(limV[0]<tolerance)
break
endif
endfor
// store the computed row in final W.
W[i-1][]=lcw[q]
endfor // loop over components
// Calculate the un-mixing matrix
MatrixOP/O WunMixing=W x K
// Un-mix;
MatrixOP/O ICARes=(WunMixing x xx)^t
End
Function testICA()
// Create distinct frequencies:
Make/O/N=(1000,3) ddd
ddd[][0]=sin(2*pi*x/13)
ddd[][1]=sin(2*pi*x/17)
ddd[][2]=sin(2*pi*x/23)
// Create mixing matrix:
Make/O/N=(3,3) AA
AA[0][0]= {0.291,0.6557,-0.5439}
AA[0][1]= {0.5572,0.3,-0.2}
AA[0][2]= {-0.1,-0.7,0.4}
// Do your mixing:
MatrixOP/O xx=ddd x AA
// Try the ICA
simpleICA(xx,3,$"")
// Plot the results:
Wave ICARes
Display ICARes[][0]
Display ICARes[][1]
Display ICARes[][2]
End
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More