#include #include "mex.h" /* External Interfaces: * http://www.mathworks.com/access/helpdesk/help/techdoc/matlab_external/index.html * * API Reference: * http://www.mathworks.com/access/helpdesk/help/techdoc/apiref/ * * Handling Sparse Arrays: * http://www.mathworks.com/access/helpdesk/help/techdoc/matlab_external/f24370.html * http://www.ccr.jussieu.fr/ccr/Documentation/Calcul/matlab5v11/docs/00009/009a1.htm */ /* If you are using a compiler that equates NaN to be zero, you * must compile this example using the flag -DNAN_EQUALS_ZERO. * For example: * mex -DNAN_EQUALS_ZERO fulltosparse.c * This will correctly define the IsNonZero macro for your C * compiler. IEEE "NaN" is the only numeric constant that is not * equivalent with itself. */ #if defined(NAN_EQUALS_ZERO) #define isNonZero(d) ((d) != 0.0 || (d)!=(d)) #else #define isNonZero(d) ((d) != 0.0) #endif /* Metrics: */ double fisher ( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ); double pwmi ( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ); double expmi ( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ); double lrt ( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ); double chi2 ( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ); double yuleq ( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ); double cooccur( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ); typedef double (*Metric)( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ); /* jvd: parameters used by metrics */ double smooth; /* jvd: expmi, pwmi */ void allPairsColumns ( int m, int n, Metric metric, int Bi[], int Bj[], double Bx[], int Ai[], int Aj[], double Ax[] ); int* contingencyTable ( int c[], int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ); /* jvd: Matlab entry point. "mexFunction" is the only function that makes * (extensive) use of "mex.h" */ void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) { int m,n,nzmax,*Ai,*Aj; double *Ax; int *Bi,*Bj; double *Bx; Metric metric; void *newptr; char metname[32]; /* jvd: provide brief explanation if no input arguments are given */ if (nrhs < 1){ mexPrintf( "allcolsmetric Applies the symmetric metric between all pairs of sparse\n" " columns and returns these results as a lower triangular sparse\n" " matrix. Choices are:\n" " 'fisher' Fisher distance\n" " 'expmi' Expected mutual information\n" " (optional smoothing parameter)\n" " 'pwmi' Point-wise mutual information\n" " (optional smoothing parameter)\n" " 'lrt' Likelihood ratio test\n" " 'chi2' Chi2 statistic\n" " 'yuleq' Yule's Q\n" " 'cooccur' Lower tri (df) coocur\n\n" " For example:\n" " X=sparse((rand(5,5)>.5).*rand(5,5));\n" " allcolsmetric(X,'expmi',1.0)\n" ); return; } /* jvd: Check for proper number of input and output arguments.*/ if( nrhs < 2 || nrhs>3 ) mexErrMsgTxt("At least two input arguments required."); /* jvd: Check data type of input argument. */ if( !(mxIsSparse(prhs[0])) ) mexErrMsgTxt("First input argument must be sparse."); if( !(mxIsDouble(prhs[0])) ) mexErrMsgTxt("First input argument must be of type double."); if( mxGetNumberOfDimensions(prhs[0])!=2 ) mexErrMsgTxt("First input argument must be two dimensional."); if( mxGetPi(prhs[0])!=NULL ) mexErrMsgTxt("First input argument must be real."); if( !mxIsChar(prhs[1]) ) mexErrMsgTxt("Second input argument must be name of valid metric."); mxGetString(prhs[1],metname,31); if( strncmp(metname,"fisher",32)==0 ) { metric = (Metric)fisher; } else if( strncmp(metname,"expmi",32)==0 ) { if( nrhs==3 ) { if( mxGetN(prhs[2])!=1 || mxGetM(prhs[2])!=1 || mxGetPi(prhs[2])!=NULL ) { mexErrMsgTxt("Optional paramter must be a real-valued scalar."); return; } smooth = mxGetScalar(prhs[2]); } else { smooth = 0.0; } metric = (Metric)expmi; } else if( strncmp(metname,"pwmi",32)==0 ) { if( nrhs==3 ) { if( mxGetN(prhs[2])!=1 || mxGetM(prhs[2])!=1 || mxGetPi(prhs[2])!=NULL ) { mexErrMsgTxt("Optional paramter must be a real-valued scalar."); return; } smooth = mxGetScalar(prhs[2]); } else { smooth = 0.0; } metric = (Metric)pwmi; } else if( strncmp(metname,"lrt",32)==0 ) { metric = (Metric)lrt; } else if( strncmp(metname,"chi2",32)==0 ) { metric = (Metric)chi2; } else if( strncmp(metname,"yuleq",32)==0 ) { metric = (Metric)yuleq; } else if( strncmp(metname,"cooccur",32)==0 ) { metric = (Metric)cooccur; } else { mexErrMsgTxt("Unrecognized metric."); } /* jvd: Get the size and pointers to input data. */ m = mxGetM ( prhs[0] ); n = mxGetN ( prhs[0] ); Ax = mxGetPr( prhs[0] ); Ai = mxGetIr( prhs[0] ); Aj = mxGetJc( prhs[0] ); /*cmplx = (Ai == NULL ? 0 : 1);*/ /* jvd: Allocate space for upper triangular sparse matrix. */ nzmax = n*(n+1)/2; plhs[0] = mxCreateSparse(n,n,nzmax,mxREAL); if( plhs[0]==NULL ) { mexErrMsgTxt("Unable to allocate sufficient heap memory for output. " "Try reducing input size."); return; } /* jvd: Get the pointers to output data. */ Bx = mxGetPr( plhs[0] ); Bi = mxGetIr( plhs[0] ); Bj = mxGetJc( plhs[0] ); allPairsColumns(m,n,metric,Bi,Bj,Bx,Ai,Aj,Ax); /* jvd: cleanup unused memory. Bj[n] is the actual number of non-zeros */ if( Bj[n]==nzmax ) { /* jvd: do nothing */ } else if( Bj[n]==0 ) { mxDestroyArray( plhs[0] ); plhs[0] = mxCreateSparse(n,n,0,mxREAL); } else if( Bj[n]1.0 ) { sum = 1.0; } else if( sum<0.0 ) { sum = 0.0; } else { /* jvd: do nothing */ } return acos(sum); } double chi2 ( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ) { int contTable[] = {0,0,0,0}; /* jvd: contingency table (histogram) */ /* jvd: jt density has four events, hence add 4*c to normalization */ double a,b,c,d; double n; double num,den; /* jvd: 0:XY=(0,0), 1:XY=(0,1), 2:XY=(1,0), 3:XY=(1,1) */ contingencyTable( contTable, m, pc, pi, px, qc, qi, qx ); /* Yi: convential way of naming variables */ a = contTable[3]; b = contTable[2]; c = contTable[1]; d = contTable[0]; n = a + b + c + d; /* jvd: equivalent to m */ /* mexPrintf("%d %d %d %d %d\n", a,b,c,d,n); */ /* Yi: chi2 statistics */ num = n*(a*d-c*b)*(a*d-c*b); den = (a+c)*(b+d)*(a+b)*(c+d); if( den==0 ) { mexWarnMsgTxt("Warning: divide by zero."); } return num/den; } double yuleq( int m, int pc, int pi[], double px[], int qc, int qi[], double qx[] ) { int contTable[] = {0,0,0,0}; /* jvd: contingency table (histogram) */ /* jvd: jt density has four events, hence add 4*c to normalization */ double a,b,c,d; double num,den; /* jvd: 0:XY=(0,0), 1:XY=(0,1), 2:XY=(1,0), 3:XY=(1,1) */ contingencyTable( contTable, m, pc, pi, px, qc, qi, qx ); /* Yi: convential way of naming variables */ a = contTable[3]; b = contTable[2]; c = contTable[1]; d = contTable[0]; /* mexPrintf("%d %d %d %d %d\n", a,b,c,d,n); */ /* Yi: Yule's Q statistics */ num = a*d-b*c; den = a*d+b*c; if( den==0 ) { mexWarnMsgTxt("Warning: divide by zero."); } return num/den; }