/* ============================================================== Main MEX function - interface to Matlab. ============================================================== */ void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) { long i, j, k, m; long nsv, new_dim, num_data; double *Alpha; double *b; double *Y; double k_ij; ker_cnt = 0; /* Y = kernelproj_mex(X, Alpha, b, sv_X, ker, arg ) */ /* ------------------------------------------- */ if( nrhs == 6) { /* data matrix [dim x num_data] */ if( !mxIsNumeric(prhs[0]) || !mxIsDouble(prhs[0]) || mxIsEmpty(prhs[0]) || mxIsComplex(prhs[0]) ) mexErrMsgTxt("Input data must be a real matrix."); /* multipliers Alpha [nsv x new_dim] */ if( !mxIsNumeric(prhs[1]) || !mxIsDouble(prhs[1]) || mxIsEmpty(prhs[1]) || mxIsComplex(prhs[1]) ) mexErrMsgTxt("Input Alpha must be a real matrix."); /* vector b [nsv x 1] */ if( !mxIsNumeric(prhs[2]) || !mxIsDouble(prhs[2]) || mxIsEmpty(prhs[2]) || mxIsComplex(prhs[2]) ) mexErrMsgTxt("Input b must be a real vector."); /* kernel identifier */ ker = kernel_id( prhs[4] ); if( ker == -1 ) mexErrMsgTxt("Improper kernel identifier."); /* get pointer to arguments */ arg1 = mxGetPr(prhs[5]); /* get pointer at input vectors */ dataA = mxGetPr(prhs[0]); Alpha = mxGetPr(prhs[1]); b = mxGetPr(prhs[2]); dataB = mxGetPr(prhs[3]); /* get data dimensions */ dim = mxGetM(prhs[0]); num_data = mxGetN(prhs[0]); nsv = mxGetM(prhs[1]); new_dim = mxGetN(prhs[1]); if( mxGetM(prhs[2]) != new_dim) mexErrMsgTxt("Number of rows of Alpha must equal to size of vector b."); /* creates output kernel matrix. */ plhs[0] = mxCreateDoubleMatrix(new_dim,num_data,mxREAL); Y = mxGetPr(plhs[0]); /* computes kernel projection */ for( i = 0; i < num_data; i++ ) { for( k = 0; k < new_dim; k++) { Y[k+i*new_dim] = b[k]; } for( j = 0; j < nsv; j++ ) { k_ij = kernel(i,j); for( k = 0; k < new_dim; k++) { if(Alpha[j+k*nsv] != 0 ) Y[k+i*new_dim] += k_ij*Alpha[j+k*nsv]; } } } } else { mexErrMsgTxt("Wrong number of input arguments."); } return; }
/* -------------------------------------------------------------- Finds the second Lagrange multiplayer to be optimize. -------------------------------------------------------------- */ long examineExample( long i1 ) { double y1, alpha1, E1, r1; double tmax; double E2, temp; long k, i2; long k0; y1 = target[i1]; alpha1 = alpha[i1]; if( alpha1 > 0 && alpha1 < C(i1) ) E1 = error_cache[i1]; else E1 = learned_func(i1) - y1; r1 = y1 * E1; if(( r1 < -tolerance && alpha1 < C(i1) ) || (r1 > tolerance && alpha1 > 0)) { /* Try i2 by three ways; if successful, then immediately return 1; */ for( i2 = (-1), tmax = 0, k = 0; k < N; k++ ) { if( alpha[k] > 0 && alpha[k] < C(k) ) { E2 = error_cache[k]; temp = fabs(E1 - E2); if( temp > tmax ) { tmax = temp; i2 = k; } } } if( i2 >= 0 ) { if( takeStep(i1,i2) ) return( 1 ); } #ifdef RANDOM for( k0 = rand(), k = k0; k < N + k0; k++ ) { i2 = k % N; #else for( k = 0; k < N; k++) { i2 = k; #endif if( alpha[i2] > 0 && alpha[i2] < C(i2) ) { if( takeStep(i1,i2) ) return( 1 ); } } #ifdef RANDOM for( k0 = rand(), k = k0; k < N + k0; k++ ) { i2 = k % N; #else for( k = 0; k < N; k++) { i2 = k; #endif if( takeStep(i1,i2) ) return( 1 ); } } /* if( ... ) */ return( 0 ); } /* -------------------------------------------------------------- Main SMO optimization cycle. -------------------------------------------------------------- */ void runSMO( void ) { long numChanged = 0; long examineAll = 1; long k; while( numChanged > 0 || examineAll ) { numChanged = 0; if( examineAll ) { for( k = 0; k < N; k++ ) { numChanged += examineExample( k ); } } else { for( k = 0; k < N; k++ ) { if( alpha[k] != 0 && alpha[k] != C(k) ) numChanged += examineExample( k ); } } if( examineAll == 1 ) examineAll = 0; else if( numChanged == 0 ) examineAll = 1; } } /* ============================================================== Main MEX function - interface to Matlab. ============================================================== */ void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray*prhs[] ) { long i,j ; double *labels12, *initAlpha, *nsv, *tmp, *trn_err, *margin; double nerr; double C1, C2; /* ---- get input arguments ----------------------- */ if(nrhs < 5) mexErrMsgTxt("Not enough input arguments."); /* data matrix [dim x N ] */ if( !mxIsNumeric(prhs[0]) || !mxIsDouble(prhs[0]) || mxIsEmpty(prhs[0]) || mxIsComplex(prhs[0]) ) mexErrMsgTxt("Input X must be a real matrix."); /* vector of labels (1,2) */ if( !mxIsNumeric(prhs[1]) || !mxIsDouble(prhs[1]) || mxIsEmpty(prhs[1]) || mxIsComplex(prhs[1]) || (mxGetN(prhs[1]) != 1 && mxGetM(prhs[1]) != 1)) mexErrMsgTxt("Input I must be a real vector."); labels12 = mxGetPr(prhs[1]); /* labels (1,2) */ dataA = mxGetPr(prhs[0]); /* pointer at patterns */ dataB = dataA; dim = mxGetM(prhs[0]); /* data dimension */ N = mxGetN(prhs[0]); /* number of data */ /* kernel identifier */ ker = kernel_id( prhs[2] ); if( ker == -1 ) mexErrMsgTxt("Improper kernel identifier."); /* get pointer to arguments */ arg1 = mxGetPr(prhs[3]); /* one or two real trade-off constant(s) */ if( !mxIsNumeric(prhs[4]) || !mxIsDouble(prhs[4]) || mxIsEmpty(prhs[4]) || mxIsComplex(prhs[4]) || (mxGetN(prhs[4]) != 1 && mxGetM(prhs[4]) != 1 )) mexErrMsgTxt("Improper input argument C."); else { /* allocate memory for constant C */ if( (const_C = mxCalloc(N, sizeof(double) )) == NULL) { mexErrMsgTxt("Not enough memory."); } if( MAX( mxGetN(prhs[4]), mxGetM(prhs[4])) == 1 ) { C1 = mxGetScalar(prhs[4]); for( i=0; i < N; i++ ) const_C[i] = C1; } else if( MAX( mxGetN(prhs[4]), mxGetM(prhs[4])) == 2 ) { tmp = mxGetPr(prhs[4]); C1 = tmp[0]; C2 = tmp[1]; for( i=0; i < N; i++ ) { if( labels12[i]==1) const_C[i] = C1; else const_C[i] = C2; } } else if( MAX( mxGetN(prhs[4]), mxGetM(prhs[4])) == N ) { tmp = mxGetPr(prhs[4]); for( i=0; i < N; i++ ) const_C[i] = tmp[i]; } else { mexErrMsgTxt("Improper argument C."); } } /* real parameter eps */ if( nrhs >= 6 ) { if( !mxIsNumeric(prhs[5]) || !mxIsDouble(prhs[5]) || mxIsEmpty(prhs[5]) || mxIsComplex(prhs[5]) || mxGetN(prhs[5]) != 1 || mxGetM(prhs[5]) != 1 ) mexErrMsgTxt("Input eps must be a scalar."); else eps = mxGetScalar(prhs[5]); /* take eps argument */ } /* real parameter tol */ if(nrhs >= 7) { if( !mxIsNumeric(prhs[6]) || !mxIsDouble(prhs[6]) || mxIsEmpty(prhs[6]) || mxIsComplex(prhs[6]) || mxGetN(prhs[6]) != 1 || mxGetM(prhs[6]) != 1 ) mexErrMsgTxt("Input tol must be a scalar."); else tolerance = mxGetScalar(prhs[6]); /* take tolerance argument */ } /* real vector of Lagrangeian multipliers */ if(nrhs >= 8) { if( !mxIsNumeric(prhs[7]) || !mxIsDouble(prhs[7]) || mxIsEmpty(prhs[7]) || mxIsComplex(prhs[7]) || (mxGetN(prhs[7]) != 1 && mxGetM(prhs[7]) != 1 )) mexErrMsgTxt("Input Alpha must be a vector."); } /* real scalar - bias */ if( nrhs >= 9 ) { if( !mxIsNumeric(prhs[8]) || !mxIsDouble(prhs[8]) || mxIsEmpty(prhs[8]) || mxIsComplex(prhs[8]) || mxGetN(prhs[8]) != 1 || mxGetM(prhs[8]) != 1 ) mexErrMsgTxt("Input bias must be a scalar."); } /* ---- init variables ------------------------------- */ ker_cnt = 0; /* allocate memory for targets (labels) (1,-1) */ if( (target = mxCalloc(N, sizeof(double) )) == NULL) { mexErrMsgTxt("Not enough memory."); } /* transform labels12 (1,2) from to targets (1,-1) */ for( i = 0; i < N; i++ ) { target[i] = - labels12[i]*2 + 3; } /* create output variable for bias */ plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL); b = mxGetPr(plhs[1]); /* take init value of bias if given */ if( nrhs >= 9 ) { *b = -mxGetScalar(prhs[8]); } /* allocate memory for error_cache */ if( (error_cache = mxCalloc(N, sizeof(double) )) == NULL) { mexErrMsgTxt("Not enough memory for error cache."); } /* create vector for Lagrangeians */ plhs[0] = mxCreateDoubleMatrix(N,1,mxREAL); alpha = mxGetPr(plhs[0]); /* if Lagrangeians given then use them as initial values */ if( nrhs >= 8 ) { initAlpha = mxGetPr(prhs[7]); for( i = 0; i < N; i++ ) { alpha[i] = initAlpha[i]; } /* Init error cache for non-bound multipliers. */ for( i = 0; i < N; i++ ) { if( alpha[i] != 0 && alpha[i] != C(i) ) { error_cache[i] = learned_func(i) - target[i]; } } } /* ---- run SMO ------------------------------------------- */ runSMO(); /* ---- outputs --------------------------------- */ if( nlhs >= 3 ) { /* count number of support vectors */ plhs[2] = mxCreateDoubleMatrix(1,1,mxREAL); nsv = mxGetPr(plhs[2]); *nsv = 0; for( i = 0; i < N; i++ ) { if( alpha[i] > ZERO_LIM ) (*nsv)++; else alpha[i] = 0; } } if( nlhs >= 4 ) { plhs[3] = mxCreateDoubleMatrix(1,1,mxREAL); (*mxGetPr(plhs[3])) = (double)ker_cnt; } if( nlhs >= 5) { /* evaluates classification error on traning patterns */ plhs[4] = mxCreateDoubleMatrix(1,1,mxREAL); trn_err = mxGetPr(plhs[4]); nerr = 0; for( i = 0; i < N; i++ ) { if( target[i] == 1 ) { if( learned_func(i) < 0 ) nerr++; } else if( learned_func(i) >= 0 ) nerr++; } *trn_err = nerr/N; } if( nlhs >= 6) { /* compute margin */ plhs[5] = mxCreateDoubleMatrix(1,1,mxREAL); margin = mxGetPr(plhs[5]); *margin = 0; for( i = 0; i < N; i++ ) { for( j = 0; j < N; j++ ) { if( alpha[i] > 0 && alpha[j] > 0 ) *margin += alpha[i]*alpha[j]*target[i]*target[j]*kernel(i,j); } } *margin = 1/sqrt(*margin); } /* decision function of type <w,x>+b is used */ *b = -*b; /* ----- free memory --------------------------------------- */ mxFree( error_cache ); mxFree( target ); }