/************************************************************************* Internal ranking subroutine *************************************************************************/ public static void rankx(ref double[] x, int n, apserv.apbuffers buf) { int i = 0; int j = 0; int k = 0; int t = 0; double tmp = 0; int tmpi = 0; // // Prepare // if( n<1 ) { return; } if( n==1 ) { x[0] = 1; return; } if( ap.len(buf.ra1)<n ) { buf.ra1 = new double[n]; } if( ap.len(buf.ia1)<n ) { buf.ia1 = new int[n]; } for(i=0; i<=n-1; i++) { buf.ra1[i] = x[i]; buf.ia1[i] = i; } // // sort {R, C} // if( n!=1 ) { i = 2; do { t = i; while( t!=1 ) { k = t/2; if( (double)(buf.ra1[k-1])>=(double)(buf.ra1[t-1]) ) { t = 1; } else { tmp = buf.ra1[k-1]; buf.ra1[k-1] = buf.ra1[t-1]; buf.ra1[t-1] = tmp; tmpi = buf.ia1[k-1]; buf.ia1[k-1] = buf.ia1[t-1]; buf.ia1[t-1] = tmpi; t = k; } } i = i+1; } while( i<=n ); i = n-1; do { tmp = buf.ra1[i]; buf.ra1[i] = buf.ra1[0]; buf.ra1[0] = tmp; tmpi = buf.ia1[i]; buf.ia1[i] = buf.ia1[0]; buf.ia1[0] = tmpi; t = 1; while( t!=0 ) { k = 2*t; if( k>i ) { t = 0; } else { if( k<i ) { if( (double)(buf.ra1[k])>(double)(buf.ra1[k-1]) ) { k = k+1; } } if( (double)(buf.ra1[t-1])>=(double)(buf.ra1[k-1]) ) { t = 0; } else { tmp = buf.ra1[k-1]; buf.ra1[k-1] = buf.ra1[t-1]; buf.ra1[t-1] = tmp; tmpi = buf.ia1[k-1]; buf.ia1[k-1] = buf.ia1[t-1]; buf.ia1[t-1] = tmpi; t = k; } } } i = i-1; } while( i>=1 ); } // // compute tied ranks // i = 0; while( i<=n-1 ) { j = i+1; while( j<=n-1 ) { if( (double)(buf.ra1[j])!=(double)(buf.ra1[i]) ) { break; } j = j+1; } for(k=i; k<=j-1; k++) { buf.ra1[k] = 1+(double)(i+j-1)/(double)2; } i = j; } // // back to x // for(i=0; i<=n-1; i++) { x[buf.ia1[i]] = buf.ra1[i]; } }
/************************************************************************* Basecase code for RankData(), performs actual work on subset of data using temporary buffer passed as parameter. INPUT PARAMETERS: XY - array[NPoints,NFeatures], dataset I0 - index of first row to process I1 - index of past-the-last row to process; this function processes half-interval [I0,I1). NFeatures- number of features IsCentered- whether ranks are centered or not: * True - ranks are centered in such way that their within-row sum is zero * False - ranks are not centered Buf0 - temporary buffers, may be empty (this function automatically allocates/reuses buffers). Buf1 - temporary buffers, may be empty (this function automatically allocates/reuses buffers). OUTPUT PARAMETERS: XY - data in [I0,I1) are replaced by their within-row ranks; ranking starts from 0, ends at NFeatures-1 -- ALGLIB -- Copyright 18.04.2013 by Bochkanov Sergey *************************************************************************/ private static void rankdatabasecase(double[,] xy, int i0, int i1, int nfeatures, bool iscentered, apserv.apbuffers buf0, apserv.apbuffers buf1) { int i = 0; int i_ = 0; alglib.ap.assert(i1>=i0, "RankDataBasecase: internal error"); if( alglib.ap.len(buf1.ra0)<nfeatures ) { buf1.ra0 = new double[nfeatures]; } for(i=i0; i<=i1-1; i++) { for(i_=0; i_<=nfeatures-1;i_++) { buf1.ra0[i_] = xy[i,i_]; } basicstatops.rankx(buf1.ra0, nfeatures, iscentered, buf0); for(i_=0; i_<=nfeatures-1;i_++) { xy[i,i_] = buf1.ra0[i_]; } } }
/************************************************************************* Buffered variant of TagSort, which accepts preallocated output arrays as well as special structure for buffered allocations. If arrays are too short, they are reallocated. If they are large enough, no memory allocation is done. It is intended to be used in the performance-critical parts of code, where additional allocations can lead to severe performance degradation -- ALGLIB -- Copyright 14.05.2008 by Bochkanov Sergey *************************************************************************/ public static void tagsortbuf(ref double[] a, int n, ref int[] p1, ref int[] p2, apserv.apbuffers buf) { int i = 0; int lv = 0; int lp = 0; int rv = 0; int rp = 0; // // Special cases // if( n<=0 ) { return; } if( n==1 ) { apserv.ivectorsetlengthatleast(ref p1, 1); apserv.ivectorsetlengthatleast(ref p2, 1); p1[0] = 0; p2[0] = 0; return; } // // General case, N>1: prepare permutations table P1 // apserv.ivectorsetlengthatleast(ref p1, n); for(i=0; i<=n-1; i++) { p1[i] = i; } // // General case, N>1: sort, update P1 // apserv.rvectorsetlengthatleast(ref buf.ra0, n); apserv.ivectorsetlengthatleast(ref buf.ia0, n); tagsortfasti(ref a, ref p1, ref buf.ra0, ref buf.ia0, n); // // General case, N>1: fill permutations table P2 // // To fill P2 we maintain two arrays: // * PV (Buf.IA0), Position(Value). PV[i] contains position of I-th key at the moment // * VP (Buf.IA1), Value(Position). VP[i] contains key which has position I at the moment // // At each step we making permutation of two items: // Left, which is given by position/value pair LP/LV // and Right, which is given by RP/RV // and updating PV[] and VP[] correspondingly. // apserv.ivectorsetlengthatleast(ref buf.ia0, n); apserv.ivectorsetlengthatleast(ref buf.ia1, n); apserv.ivectorsetlengthatleast(ref p2, n); for(i=0; i<=n-1; i++) { buf.ia0[i] = i; buf.ia1[i] = i; } for(i=0; i<=n-1; i++) { // // calculate LP, LV, RP, RV // lp = i; lv = buf.ia1[lp]; rv = p1[i]; rp = buf.ia0[rv]; // // Fill P2 // p2[i] = rp; // // update PV and VP // buf.ia1[lp] = rv; buf.ia1[rp] = lv; buf.ia0[lv] = rp; buf.ia0[rv] = lp; } }
private static void rmatrixluinverserec(ref double[,] a, int offs, int n, ref double[] work, apserv.sinteger info, matinvreport rep) { int i = 0; int j = 0; double v = 0; int n1 = 0; int n2 = 0; int i_ = 0; int i1_ = 0; if( n<1 ) { info.val = -1; return; } // // Base case // if( n<=ablas.ablasblocksize(a) ) { // // Form inv(U) // rmatrixtrinverserec(a, offs, n, true, false, work, info, rep); if( info.val<=0 ) { return; } // // Solve the equation inv(A)*L = inv(U) for inv(A). // for(j=n-1; j>=0; j--) { // // Copy current column of L to WORK and replace with zeros. // for(i=j+1; i<=n-1; i++) { work[i] = a[offs+i,offs+j]; a[offs+i,offs+j] = 0; } // // Compute current column of inv(A). // if( j<n-1 ) { for(i=0; i<=n-1; i++) { i1_ = (j+1)-(offs+j+1); v = 0.0; for(i_=offs+j+1; i_<=offs+n-1;i_++) { v += a[offs+i,i_]*work[i_+i1_]; } a[offs+i,offs+j] = a[offs+i,offs+j]-v; } } } return; } // // Recursive code: // // ( L1 ) ( U1 U12 ) // A = ( ) * ( ) // ( L12 L2 ) ( U2 ) // // ( W X ) // A^-1 = ( ) // ( Y Z ) // ablas.ablassplitlength(a, n, ref n1, ref n2); alglib.ap.assert(n2>0, "LUInverseRec: internal error!"); // // X := inv(U1)*U12*inv(U2) // ablas.rmatrixlefttrsm(n1, n2, a, offs, offs, true, false, 0, a, offs, offs+n1); ablas.rmatrixrighttrsm(n1, n2, a, offs+n1, offs+n1, true, false, 0, a, offs, offs+n1); // // Y := inv(L2)*L12*inv(L1) // ablas.rmatrixlefttrsm(n2, n1, a, offs+n1, offs+n1, false, true, 0, a, offs+n1, offs); ablas.rmatrixrighttrsm(n2, n1, a, offs, offs, false, true, 0, a, offs+n1, offs); // // W := inv(L1*U1)+X*Y // rmatrixluinverserec(ref a, offs, n1, ref work, info, rep); if( info.val<=0 ) { return; } ablas.rmatrixgemm(n1, n1, n2, 1.0, a, offs, offs+n1, 0, a, offs+n1, offs, 0, 1.0, a, offs, offs); // // X := -X*inv(L2) // Y := -inv(U2)*Y // ablas.rmatrixrighttrsm(n1, n2, a, offs+n1, offs+n1, false, true, 0, a, offs, offs+n1); for(i=0; i<=n1-1; i++) { for(i_=offs+n1; i_<=offs+n-1;i_++) { a[offs+i,i_] = -1*a[offs+i,i_]; } } ablas.rmatrixlefttrsm(n2, n1, a, offs+n1, offs+n1, true, false, 0, a, offs+n1, offs); for(i=0; i<=n2-1; i++) { for(i_=offs; i_<=offs+n1-1;i_++) { a[offs+n1+i,i_] = -1*a[offs+n1+i,i_]; } } // // Z := inv(L2*U2) // rmatrixluinverserec(ref a, offs+n1, n2, ref work, info, rep); }
/************************************************************************* Internal ranking subroutine. INPUT PARAMETERS: X - array to rank N - array size IsCentered- whether ranks are centered or not: * True - ranks are centered in such way that their sum is zero * False - ranks are not centered Buf - temporary buffers NOTE: when IsCentered is True and all X[] are equal, this function fills X by zeros (exact zeros are used, not sum which is only approximately equal to zero). *************************************************************************/ public static void rankx(double[] x, int n, bool iscentered, apserv.apbuffers buf) { int i = 0; int j = 0; int k = 0; double tmp = 0; double voffs = 0; // // Prepare // if (n < 1) { return; } if (n == 1) { x[0] = 0; return; } if (alglib.ap.len(buf.ra1) < n) { buf.ra1 = new double[n]; } if (alglib.ap.len(buf.ia1) < n) { buf.ia1 = new int[n]; } for (i = 0; i <= n - 1; i++) { buf.ra1[i] = x[i]; buf.ia1[i] = i; } tsort.tagsortfasti(ref buf.ra1, ref buf.ia1, ref buf.ra2, ref buf.ia2, n); // // Special test for all values being equal // if ((double)(buf.ra1[0]) == (double)(buf.ra1[n - 1])) { if (iscentered) { tmp = 0.0; } else { tmp = (double)(n - 1) / (double)2; } for (i = 0; i <= n - 1; i++) { x[i] = tmp; } return; } // // compute tied ranks // i = 0; while (i <= n - 1) { j = i + 1; while (j <= n - 1) { if ((double)(buf.ra1[j]) != (double)(buf.ra1[i])) { break; } j = j + 1; } for (k = i; k <= j - 1; k++) { buf.ra1[k] = (double)(i + j - 1) / (double)2; } i = j; } // // back to x // if (iscentered) { voffs = (double)(n - 1) / (double)2; } else { voffs = 0.0; } for (i = 0; i <= n - 1; i++) { x[buf.ia1[i]] = buf.ra1[i] - voffs; } }
/************************************************************************* Triangular matrix inversion, recursive subroutine NOTE: this function sets Info on failure, leaves it unchanged on success. NOTE: only Tmp[Offs:Offs+N-1] is modified, other entries of the temporary array are not modified -- ALGLIB -- 05.02.2010, Bochkanov Sergey. Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., Courant Institute, Argonne National Lab, and Rice University February 29, 1992. *************************************************************************/ private static void rmatrixtrinverserec(double[,] a, int offs, int n, bool isupper, bool isunit, double[] tmp, apserv.sinteger info, matinvreport rep) { int n1 = 0; int n2 = 0; int i = 0; int j = 0; double v = 0; double ajj = 0; int i_ = 0; if( n<1 ) { info.val = -1; return; } // // Base case // if( n<=ablas.ablasblocksize(a) ) { if( isupper ) { // // Compute inverse of upper triangular matrix. // for(j=0; j<=n-1; j++) { if( !isunit ) { if( (double)(a[offs+j,offs+j])==(double)(0) ) { info.val = -3; return; } a[offs+j,offs+j] = 1/a[offs+j,offs+j]; ajj = -a[offs+j,offs+j]; } else { ajj = -1; } // // Compute elements 1:j-1 of j-th column. // if( j>0 ) { for(i_=offs+0; i_<=offs+j-1;i_++) { tmp[i_] = a[i_,offs+j]; } for(i=0; i<=j-1; i++) { if( i<j-1 ) { v = 0.0; for(i_=offs+i+1; i_<=offs+j-1;i_++) { v += a[offs+i,i_]*tmp[i_]; } } else { v = 0; } if( !isunit ) { a[offs+i,offs+j] = v+a[offs+i,offs+i]*tmp[offs+i]; } else { a[offs+i,offs+j] = v+tmp[offs+i]; } } for(i_=offs+0; i_<=offs+j-1;i_++) { a[i_,offs+j] = ajj*a[i_,offs+j]; } } } } else { // // Compute inverse of lower triangular matrix. // for(j=n-1; j>=0; j--) { if( !isunit ) { if( (double)(a[offs+j,offs+j])==(double)(0) ) { info.val = -3; return; } a[offs+j,offs+j] = 1/a[offs+j,offs+j]; ajj = -a[offs+j,offs+j]; } else { ajj = -1; } if( j<n-1 ) { // // Compute elements j+1:n of j-th column. // for(i_=offs+j+1; i_<=offs+n-1;i_++) { tmp[i_] = a[i_,offs+j]; } for(i=j+1; i<=n-1; i++) { if( i>j+1 ) { v = 0.0; for(i_=offs+j+1; i_<=offs+i-1;i_++) { v += a[offs+i,i_]*tmp[i_]; } } else { v = 0; } if( !isunit ) { a[offs+i,offs+j] = v+a[offs+i,offs+i]*tmp[offs+i]; } else { a[offs+i,offs+j] = v+tmp[offs+i]; } } for(i_=offs+j+1; i_<=offs+n-1;i_++) { a[i_,offs+j] = ajj*a[i_,offs+j]; } } } } return; } // // Recursive case // ablas.ablassplitlength(a, n, ref n1, ref n2); if( n2>0 ) { if( isupper ) { for(i=0; i<=n1-1; i++) { for(i_=offs+n1; i_<=offs+n-1;i_++) { a[offs+i,i_] = -1*a[offs+i,i_]; } } ablas.rmatrixrighttrsm(n1, n2, a, offs+n1, offs+n1, isupper, isunit, 0, a, offs, offs+n1); ablas.rmatrixlefttrsm(n1, n2, a, offs, offs, isupper, isunit, 0, a, offs, offs+n1); rmatrixtrinverserec(a, offs+n1, n2, isupper, isunit, tmp, info, rep); } else { for(i=0; i<=n2-1; i++) { for(i_=offs; i_<=offs+n1-1;i_++) { a[offs+n1+i,i_] = -1*a[offs+n1+i,i_]; } } ablas.rmatrixlefttrsm(n2, n1, a, offs+n1, offs+n1, isupper, isunit, 0, a, offs+n1, offs); ablas.rmatrixrighttrsm(n2, n1, a, offs, offs, isupper, isunit, 0, a, offs+n1, offs); rmatrixtrinverserec(a, offs+n1, n2, isupper, isunit, tmp, info, rep); } } rmatrixtrinverserec(a, offs, n1, isupper, isunit, tmp, info, rep); }
/************************************************************************* Buffered version of ClusterizerGetDistances() which reuses previously allocated space. -- ALGLIB -- Copyright 29.05.2015 by Bochkanov Sergey *************************************************************************/ public static void clusterizergetdistancesbuf(apserv.apbuffers buf, double[,] xy, int npoints, int nfeatures, int disttype, ref double[,] d) { int i = 0; int j = 0; double v = 0; double vv = 0; double vr = 0; int i_ = 0; alglib.ap.assert(nfeatures>=1, "ClusterizerGetDistancesBuf: NFeatures<1"); alglib.ap.assert(npoints>=0, "ClusterizerGetDistancesBuf: NPoints<1"); alglib.ap.assert((((((((disttype==0 || disttype==1) || disttype==2) || disttype==10) || disttype==11) || disttype==12) || disttype==13) || disttype==20) || disttype==21, "ClusterizerGetDistancesBuf: incorrect DistType"); alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "ClusterizerGetDistancesBuf: Rows(XY)<NPoints"); alglib.ap.assert(alglib.ap.cols(xy)>=nfeatures, "ClusterizerGetDistancesBuf: Cols(XY)<NFeatures"); alglib.ap.assert(apserv.apservisfinitematrix(xy, npoints, nfeatures), "ClusterizerGetDistancesBuf: XY contains NAN/INF"); // // Quick exit // if( npoints==0 ) { return; } if( npoints==1 ) { apserv.rmatrixsetlengthatleast(ref d, 1, 1); d[0,0] = 0; return; } // // Build distance matrix D. // if( disttype==0 || disttype==1 ) { // // Chebyshev or city-block distances: // * recursively calculate upper triangle (with main diagonal) // * copy it to the bottom part of the matrix // apserv.rmatrixsetlengthatleast(ref d, npoints, npoints); evaluatedistancematrixrec(xy, nfeatures, disttype, d, 0, npoints, 0, npoints); ablas.rmatrixenforcesymmetricity(d, npoints, true); return; } if( disttype==2 ) { // // Euclidean distance // // NOTE: parallelization is done within RMatrixSYRK // apserv.rmatrixsetlengthatleast(ref d, npoints, npoints); apserv.rmatrixsetlengthatleast(ref buf.rm0, npoints, nfeatures); apserv.rvectorsetlengthatleast(ref buf.ra1, nfeatures); apserv.rvectorsetlengthatleast(ref buf.ra0, npoints); for(j=0; j<=nfeatures-1; j++) { buf.ra1[j] = 0.0; } v = (double)1/(double)npoints; for(i=0; i<=npoints-1; i++) { for(i_=0; i_<=nfeatures-1;i_++) { buf.ra1[i_] = buf.ra1[i_] + v*xy[i,i_]; } } for(i=0; i<=npoints-1; i++) { for(i_=0; i_<=nfeatures-1;i_++) { buf.rm0[i,i_] = xy[i,i_]; } for(i_=0; i_<=nfeatures-1;i_++) { buf.rm0[i,i_] = buf.rm0[i,i_] - buf.ra1[i_]; } } ablas.rmatrixsyrk(npoints, nfeatures, 1.0, buf.rm0, 0, 0, 0, 0.0, d, 0, 0, true); for(i=0; i<=npoints-1; i++) { buf.ra0[i] = d[i,i]; } for(i=0; i<=npoints-1; i++) { d[i,i] = 0.0; for(j=i+1; j<=npoints-1; j++) { v = Math.Sqrt(Math.Max(buf.ra0[i]+buf.ra0[j]-2*d[i,j], 0.0)); d[i,j] = v; } } ablas.rmatrixenforcesymmetricity(d, npoints, true); return; } if( disttype==10 || disttype==11 ) { // // Absolute/nonabsolute Pearson correlation distance // // NOTE: parallelization is done within PearsonCorrM, which calls RMatrixSYRK internally // apserv.rmatrixsetlengthatleast(ref d, npoints, npoints); apserv.rvectorsetlengthatleast(ref buf.ra0, npoints); apserv.rmatrixsetlengthatleast(ref buf.rm0, npoints, nfeatures); for(i=0; i<=npoints-1; i++) { v = 0.0; for(j=0; j<=nfeatures-1; j++) { v = v+xy[i,j]; } v = v/nfeatures; for(j=0; j<=nfeatures-1; j++) { buf.rm0[i,j] = xy[i,j]-v; } } ablas.rmatrixsyrk(npoints, nfeatures, 1.0, buf.rm0, 0, 0, 0, 0.0, d, 0, 0, true); for(i=0; i<=npoints-1; i++) { buf.ra0[i] = d[i,i]; } for(i=0; i<=npoints-1; i++) { d[i,i] = 0.0; for(j=i+1; j<=npoints-1; j++) { v = d[i,j]/Math.Sqrt(buf.ra0[i]*buf.ra0[j]); if( disttype==10 ) { v = 1-v; } else { v = 1-Math.Abs(v); } v = Math.Max(v, 0.0); d[i,j] = v; } } ablas.rmatrixenforcesymmetricity(d, npoints, true); return; } if( disttype==12 || disttype==13 ) { // // Absolute/nonabsolute uncentered Pearson correlation distance // // NOTE: parallelization is done within RMatrixSYRK // apserv.rmatrixsetlengthatleast(ref d, npoints, npoints); apserv.rvectorsetlengthatleast(ref buf.ra0, npoints); ablas.rmatrixsyrk(npoints, nfeatures, 1.0, xy, 0, 0, 0, 0.0, d, 0, 0, true); for(i=0; i<=npoints-1; i++) { buf.ra0[i] = d[i,i]; } for(i=0; i<=npoints-1; i++) { d[i,i] = 0.0; for(j=i+1; j<=npoints-1; j++) { v = d[i,j]/Math.Sqrt(buf.ra0[i]*buf.ra0[j]); if( disttype==13 ) { v = Math.Abs(v); } v = Math.Min(v, 1.0); d[i,j] = 1-v; } } ablas.rmatrixenforcesymmetricity(d, npoints, true); return; } if( disttype==20 || disttype==21 ) { // // Spearman rank correlation // // NOTE: parallelization of correlation matrix is done within // PearsonCorrM, which calls RMatrixSYRK internally // apserv.rmatrixsetlengthatleast(ref d, npoints, npoints); apserv.rvectorsetlengthatleast(ref buf.ra0, npoints); apserv.rmatrixsetlengthatleast(ref buf.rm0, npoints, nfeatures); ablas.rmatrixcopy(npoints, nfeatures, xy, 0, 0, ref buf.rm0, 0, 0); basestat.rankdatacentered(buf.rm0, npoints, nfeatures); ablas.rmatrixsyrk(npoints, nfeatures, 1.0, buf.rm0, 0, 0, 0, 0.0, d, 0, 0, true); for(i=0; i<=npoints-1; i++) { if( (double)(d[i,i])>(double)(0) ) { buf.ra0[i] = 1/Math.Sqrt(d[i,i]); } else { buf.ra0[i] = 0.0; } } for(i=0; i<=npoints-1; i++) { v = buf.ra0[i]; d[i,i] = 0.0; for(j=i+1; j<=npoints-1; j++) { vv = d[i,j]*v*buf.ra0[j]; if( disttype==20 ) { vr = 1-vv; } else { vr = 1-Math.Abs(vv); } if( (double)(vr)<(double)(0) ) { vr = 0.0; } d[i,j] = vr; } } ablas.rmatrixenforcesymmetricity(d, npoints, true); return; } alglib.ap.assert(false); }
/************************************************************************* This function trains neural network ensemble passed to this function using current dataset and early stopping training algorithm. Each early stopping round performs NRestarts random restarts (thus, EnsembleSize*NRestarts training rounds is performed in total). -- ALGLIB -- Copyright 22.08.2012 by Bochkanov Sergey *************************************************************************/ private static void mlptrainensemblex(mlptrainer s, mlpe.mlpensemble ensemble, int idx0, int idx1, int nrestarts, int trainingmethod, apserv.sinteger ngrad, bool isrootcall, alglib.smp.shared_pool esessions) { int pcount = 0; int nin = 0; int nout = 0; int wcount = 0; int i = 0; int j = 0; int k = 0; int trnsubsetsize = 0; int valsubsetsize = 0; int k0 = 0; apserv.sinteger ngrad0 = new apserv.sinteger(); apserv.sinteger ngrad1 = new apserv.sinteger(); mlpetrnsession psession = null; hqrnd.hqrndstate rs = new hqrnd.hqrndstate(); int i_ = 0; int i1_ = 0; nin = mlpbase.mlpgetinputscount(ensemble.network); nout = mlpbase.mlpgetoutputscount(ensemble.network); wcount = mlpbase.mlpgetweightscount(ensemble.network); if( mlpbase.mlpissoftmax(ensemble.network) ) { pcount = nin; } else { pcount = nin+nout; } if( nrestarts<=0 ) { nrestarts = 1; } // // Handle degenerate case // if( s.npoints<2 ) { for(i=idx0; i<=idx1-1; i++) { for(j=0; j<=wcount-1; j++) { ensemble.weights[i*wcount+j] = 0.0; } for(j=0; j<=pcount-1; j++) { ensemble.columnmeans[i*pcount+j] = 0.0; ensemble.columnsigmas[i*pcount+j] = 1.0; } } return; } // // Process root call // if( isrootcall ) { // // Prepare: // * prepare MLPETrnSessions // * fill ensemble by zeros (helps to detect errors) // initmlpetrnsessions(ensemble.network, s, esessions); for(i=idx0; i<=idx1-1; i++) { for(j=0; j<=wcount-1; j++) { ensemble.weights[i*wcount+j] = 0.0; } for(j=0; j<=pcount-1; j++) { ensemble.columnmeans[i*pcount+j] = 0.0; ensemble.columnsigmas[i*pcount+j] = 0.0; } } // // Train in non-root mode and exit // mlptrainensemblex(s, ensemble, idx0, idx1, nrestarts, trainingmethod, ngrad, false, esessions); return; } // // Split problem // if( idx1-idx0>=2 ) { k0 = (idx1-idx0)/2; ngrad0.val = 0; ngrad1.val = 0; mlptrainensemblex(s, ensemble, idx0, idx0+k0, nrestarts, trainingmethod, ngrad0, false, esessions); mlptrainensemblex(s, ensemble, idx0+k0, idx1, nrestarts, trainingmethod, ngrad1, false, esessions); ngrad.val = ngrad0.val+ngrad1.val; return; } // // Retrieve and prepare session // alglib.smp.ae_shared_pool_retrieve(esessions, ref psession); // // Train // hqrnd.hqrndrandomize(rs); for(k=idx0; k<=idx1-1; k++) { // // Split set // trnsubsetsize = 0; valsubsetsize = 0; if( trainingmethod==0 ) { do { trnsubsetsize = 0; valsubsetsize = 0; for(i=0; i<=s.npoints-1; i++) { if( (double)(math.randomreal())<(double)(0.66) ) { // // Assign sample to training set // psession.trnsubset[trnsubsetsize] = i; trnsubsetsize = trnsubsetsize+1; } else { // // Assign sample to validation set // psession.valsubset[valsubsetsize] = i; valsubsetsize = valsubsetsize+1; } } } while( !(trnsubsetsize!=0 && valsubsetsize!=0) ); } if( trainingmethod==1 ) { valsubsetsize = 0; trnsubsetsize = s.npoints; for(i=0; i<=s.npoints-1; i++) { psession.trnsubset[i] = hqrnd.hqrnduniformi(rs, s.npoints); } } // // Train // mlptrainnetworkx(s, nrestarts, -1, psession.trnsubset, trnsubsetsize, psession.valsubset, valsubsetsize, psession.network, psession.mlprep, true, psession.mlpsessions); ngrad.val = ngrad.val+psession.mlprep.ngrad; // // Save results // i1_ = (0) - (k*wcount); for(i_=k*wcount; i_<=(k+1)*wcount-1;i_++) { ensemble.weights[i_] = psession.network.weights[i_+i1_]; } i1_ = (0) - (k*pcount); for(i_=k*pcount; i_<=(k+1)*pcount-1;i_++) { ensemble.columnmeans[i_] = psession.network.columnmeans[i_+i1_]; } i1_ = (0) - (k*pcount); for(i_=k*pcount; i_<=(k+1)*pcount-1;i_++) { ensemble.columnsigmas[i_] = psession.network.columnsigmas[i_+i1_]; } } // // Recycle session // alglib.smp.ae_shared_pool_recycle(esessions, ref psession); }
/************************************************************************* This function "fixes" centers, i.e. replaces ones which have no neighbor points by new centers which have at least one neighbor. If it is impossible to fix centers (not enough distinct points in the dataset), this function returns False. INPUT PARAMETERS: XY - dataset, array [0..NPoints-1,0..NVars-1]. NPoints - points count, >=1 NVars - number of variables, NVars>=1 CT - centers K - number of centers, K>=1 InitBuf - internal buffer, possibly unitialized instance of APBuffers. It is recommended to use this instance only with SelectInitialCenters() and FixCenters() functions, because these functions may allocate really large storage. UpdatePool - shared pool seeded with instance of APBuffers structure (seed instance can be unitialized). Used internally with KMeansUpdateDistances() function. It is recommended to use this pool ONLY with KMeansUpdateDistances() function. OUTPUT PARAMETERS: CT - set of K centers, one per row RESULT: True on success, False on failure (impossible to create K independent clusters) -- ALGLIB -- Copyright 21.01.2015 by Bochkanov Sergey *************************************************************************/ private static bool fixcenters(double[,] xy, int npoints, int nvars, double[,] ct, int k, apserv.apbuffers initbuf, alglib.smp.shared_pool updatepool) { bool result = new bool(); int fixiteration = 0; int centertofix = 0; int i = 0; int j = 0; int pdistant = 0; double ddistant = 0; double v = 0; int i_ = 0; alglib.ap.assert(npoints>=1, "FixCenters: internal error"); alglib.ap.assert(nvars>=1, "FixCenters: internal error"); alglib.ap.assert(k>=1, "FixCenters: internal error"); // // Calculate distances from points to best centers (RA0) // and best center indexes (IA0) // apserv.ivectorsetlengthatleast(ref initbuf.ia0, npoints); apserv.rvectorsetlengthatleast(ref initbuf.ra0, npoints); kmeansupdatedistances(xy, 0, npoints, nvars, ct, 0, k, initbuf.ia0, initbuf.ra0, updatepool); // // Repeat loop: // * find first center which has no corresponding point // * set it to the most distant (from the rest of the centerset) point // * recalculate distances, update IA0/RA0 // * repeat // // Loop is repeated for at most 2*K iterations. It is stopped once we have // no "empty" clusters. // apserv.bvectorsetlengthatleast(ref initbuf.ba0, k); for(fixiteration=0; fixiteration<=2*k; fixiteration++) { // // Select center to fix (one which is not mentioned in IA0), // terminate if there is no such center. // BA0[] stores True for centers which have at least one point. // for(i=0; i<=k-1; i++) { initbuf.ba0[i] = false; } for(i=0; i<=npoints-1; i++) { initbuf.ba0[initbuf.ia0[i]] = true; } centertofix = -1; for(i=0; i<=k-1; i++) { if( !initbuf.ba0[i] ) { centertofix = i; break; } } if( centertofix<0 ) { result = true; return result; } // // Replace center to fix by the most distant point. // Update IA0/RA0 // pdistant = 0; ddistant = initbuf.ra0[pdistant]; for(i=0; i<=npoints-1; i++) { if( (double)(initbuf.ra0[i])>(double)(ddistant) ) { ddistant = initbuf.ra0[i]; pdistant = i; } } if( (double)(ddistant)==(double)(0.0) ) { break; } for(i_=0; i_<=nvars-1;i_++) { ct[centertofix,i_] = xy[pdistant,i_]; } for(i=0; i<=npoints-1; i++) { v = 0.0; for(j=0; j<=nvars-1; j++) { v = v+math.sqr(xy[i,j]-ct[centertofix,j]); } if( (double)(v)<(double)(initbuf.ra0[i]) ) { initbuf.ra0[i] = v; initbuf.ia0[i] = centertofix; } } } result = false; return result; }
/************************************************************************* This function selects initial centers according to specified initialization algorithm. IMPORTANT: this function provides no guarantees regarding selection of DIFFERENT centers. Centers returned by this function may include duplicates (say, when random sampling is used). It is also possible that some centers are empty. Algorithm which uses this function must be able to deal with it. Say, you may want to use FixCenters() in order to fix empty centers. INPUT PARAMETERS: XY - dataset, array [0..NPoints-1,0..NVars-1]. NPoints - points count NVars - number of variables, NVars>=1 InitAlgo - initialization algorithm: * 0 - automatic selection of best algorithm * 1 - random selection * 2 - k-means++ * 3 - fast-greedy init *-1 - first K rows of dataset are used (debug algorithm) K - number of centers, K>=1 CT - possibly preallocated output buffer, resized if needed InitBuf - internal buffer, possibly unitialized instance of APBuffers. It is recommended to use this instance only with SelectInitialCenters() and FixCenters() functions, because these functions may allocate really large storage. UpdatePool - shared pool seeded with instance of APBuffers structure (seed instance can be unitialized). Used internally with KMeansUpdateDistances() function. It is recommended to use this pool ONLY with KMeansUpdateDistances() function. OUTPUT PARAMETERS: CT - set of K clusters, one per row RESULT: True on success, False on failure (impossible to create K independent clusters) -- ALGLIB -- Copyright 21.01.2015 by Bochkanov Sergey *************************************************************************/ private static void selectinitialcenters(double[,] xy, int npoints, int nvars, int initalgo, int k, ref double[,] ct, apserv.apbuffers initbuf, alglib.smp.shared_pool updatepool) { int cidx = 0; int i = 0; int j = 0; double v = 0; double vv = 0; double s = 0; int lastnz = 0; int ptidx = 0; int samplesize = 0; int samplescntnew = 0; int samplescntall = 0; double samplescale = 0; hqrnd.hqrndstate rs = new hqrnd.hqrndstate(); int i_ = 0; hqrnd.hqrndrandomize(rs); // // Check parameters // alglib.ap.assert(npoints>0, "SelectInitialCenters: internal error"); alglib.ap.assert(nvars>0, "SelectInitialCenters: internal error"); alglib.ap.assert(k>0, "SelectInitialCenters: internal error"); if( initalgo==0 ) { initalgo = 3; } apserv.rmatrixsetlengthatleast(ref ct, k, nvars); // // Random initialization // if( initalgo==-1 ) { for(i=0; i<=k-1; i++) { for(i_=0; i_<=nvars-1;i_++) { ct[i,i_] = xy[i%npoints,i_]; } } return; } // // Random initialization // if( initalgo==1 ) { for(i=0; i<=k-1; i++) { j = hqrnd.hqrnduniformi(rs, npoints); for(i_=0; i_<=nvars-1;i_++) { ct[i,i_] = xy[j,i_]; } } return; } // // k-means++ initialization // if( initalgo==2 ) { // // Prepare distances array. // Select initial center at random. // apserv.rvectorsetlengthatleast(ref initbuf.ra0, npoints); for(i=0; i<=npoints-1; i++) { initbuf.ra0[i] = math.maxrealnumber; } ptidx = hqrnd.hqrnduniformi(rs, npoints); for(i_=0; i_<=nvars-1;i_++) { ct[0,i_] = xy[ptidx,i_]; } // // For each newly added center repeat: // * reevaluate distances from points to best centers // * sample points with probability dependent on distance // * add new center // for(cidx=0; cidx<=k-2; cidx++) { // // Reevaluate distances // s = 0.0; for(i=0; i<=npoints-1; i++) { v = 0.0; for(j=0; j<=nvars-1; j++) { vv = xy[i,j]-ct[cidx,j]; v = v+vv*vv; } if( (double)(v)<(double)(initbuf.ra0[i]) ) { initbuf.ra0[i] = v; } s = s+initbuf.ra0[i]; } // // If all distances are zero, it means that we can not find enough // distinct points. In this case we just select non-distinct center // at random and continue iterations. This issue will be handled // later in the FixCenters() function. // if( (double)(s)==(double)(0.0) ) { ptidx = hqrnd.hqrnduniformi(rs, npoints); for(i_=0; i_<=nvars-1;i_++) { ct[cidx+1,i_] = xy[ptidx,i_]; } continue; } // // Select point as center using its distance. // We also handle situation when because of rounding errors // no point was selected - in this case, last non-zero one // will be used. // v = hqrnd.hqrnduniformr(rs); vv = 0.0; lastnz = -1; ptidx = -1; for(i=0; i<=npoints-1; i++) { if( (double)(initbuf.ra0[i])==(double)(0.0) ) { continue; } lastnz = i; vv = vv+initbuf.ra0[i]; if( (double)(v)<=(double)(vv/s) ) { ptidx = i; break; } } alglib.ap.assert(lastnz>=0, "SelectInitialCenters: integrity error"); if( ptidx<0 ) { ptidx = lastnz; } for(i_=0; i_<=nvars-1;i_++) { ct[cidx+1,i_] = xy[ptidx,i_]; } } return; } // // "Fast-greedy" algorithm based on "Scalable k-means++". // // We perform several rounds, within each round we sample about 0.5*K points // (not exactly 0.5*K) until we have 2*K points sampled. Before each round // we calculate distances from dataset points to closest points sampled so far. // We sample dataset points independently using distance xtimes 0.5*K divided by total // as probability (similar to k-means++, but each point is sampled independently; // after each round we have roughtly 0.5*K points added to sample). // // After sampling is done, we run "greedy" version of k-means++ on this subsample // which selects most distant point on every round. // if( initalgo==3 ) { // // Prepare arrays. // Select initial center at random, add it to "new" part of sample, // which is stored at the beginning of the array // samplesize = 2*k; samplescale = 0.5*k; apserv.rmatrixsetlengthatleast(ref initbuf.rm0, samplesize, nvars); ptidx = hqrnd.hqrnduniformi(rs, npoints); for(i_=0; i_<=nvars-1;i_++) { initbuf.rm0[0,i_] = xy[ptidx,i_]; } samplescntnew = 1; samplescntall = 1; apserv.rvectorsetlengthatleast(ref initbuf.ra0, npoints); apserv.rvectorsetlengthatleast(ref initbuf.ra1, npoints); apserv.ivectorsetlengthatleast(ref initbuf.ia1, npoints); for(i=0; i<=npoints-1; i++) { initbuf.ra0[i] = math.maxrealnumber; } // // Repeat until samples count is 2*K // while( samplescntall<samplesize ) { // // Evaluate distances from points to NEW centers, store to RA1. // Reset counter of "new" centers. // kmeansupdatedistances(xy, 0, npoints, nvars, initbuf.rm0, samplescntall-samplescntnew, samplescntall, initbuf.ia1, initbuf.ra1, updatepool); samplescntnew = 0; // // Merge new distances with old ones. // Calculate sum of distances, if sum is exactly zero - fill sample // by randomly selected points and terminate. // s = 0.0; for(i=0; i<=npoints-1; i++) { initbuf.ra0[i] = Math.Min(initbuf.ra0[i], initbuf.ra1[i]); s = s+initbuf.ra0[i]; } if( (double)(s)==(double)(0.0) ) { while( samplescntall<samplesize ) { ptidx = hqrnd.hqrnduniformi(rs, npoints); for(i_=0; i_<=nvars-1;i_++) { initbuf.rm0[samplescntall,i_] = xy[ptidx,i_]; } apserv.inc(ref samplescntall); apserv.inc(ref samplescntnew); } break; } // // Sample points independently. // for(i=0; i<=npoints-1; i++) { if( samplescntall==samplesize ) { break; } if( (double)(initbuf.ra0[i])==(double)(0.0) ) { continue; } if( (double)(hqrnd.hqrnduniformr(rs))<=(double)(samplescale*initbuf.ra0[i]/s) ) { for(i_=0; i_<=nvars-1;i_++) { initbuf.rm0[samplescntall,i_] = xy[i,i_]; } apserv.inc(ref samplescntall); apserv.inc(ref samplescntnew); } } } // // Run greedy version of k-means on sampled points // apserv.rvectorsetlengthatleast(ref initbuf.ra0, samplescntall); for(i=0; i<=samplescntall-1; i++) { initbuf.ra0[i] = math.maxrealnumber; } ptidx = hqrnd.hqrnduniformi(rs, samplescntall); for(i_=0; i_<=nvars-1;i_++) { ct[0,i_] = initbuf.rm0[ptidx,i_]; } for(cidx=0; cidx<=k-2; cidx++) { // // Reevaluate distances // for(i=0; i<=samplescntall-1; i++) { v = 0.0; for(j=0; j<=nvars-1; j++) { vv = initbuf.rm0[i,j]-ct[cidx,j]; v = v+vv*vv; } if( (double)(v)<(double)(initbuf.ra0[i]) ) { initbuf.ra0[i] = v; } } // // Select point as center in greedy manner - most distant // point is selected. // ptidx = 0; for(i=0; i<=samplescntall-1; i++) { if( (double)(initbuf.ra0[i])>(double)(initbuf.ra0[ptidx]) ) { ptidx = i; } } for(i_=0; i_<=nvars-1;i_++) { ct[cidx+1,i_] = initbuf.rm0[ptidx,i_]; } } return; } // // Internal error // alglib.ap.assert(false, "SelectInitialCenters: internal error"); }