diff --git a/MechJebLib/Maneuvers/InterplanetaryTransfer.cs b/MechJebLib/Maneuvers/InterplanetaryTransfer.cs index 063dd48a8..3dc5d8558 100644 --- a/MechJebLib/Maneuvers/InterplanetaryTransfer.cs +++ b/MechJebLib/Maneuvers/InterplanetaryTransfer.cs @@ -160,7 +160,9 @@ private void NLPFunction(double[] x, double[] fi, double[,] jac, object? obj = n DualV3 vsoi2helio = v2soi2 + vsoi2 / _targetToHelioScale.VelocityScale; // solve from the burn to the soi1 interface - (DualV3 vi1, DualV3 vf1) = Izzo.Solve(1.0, r0Burn, rsoi1, dt2, _direction1, rtol: 1e-12); + // (use prograde/retrograde sense of the initial parking orbit--shortway/longway produces issues--might need to pick some other + // axis if we hit issues) + (DualV3 vi1, DualV3 vf1) = Izzo.Solve(1.0, r0Burn, rsoi1, dt2, _direction1, rtol: 1e-12, h: V3.Cross(_r0, _v0)); // solve the heliocentric trajectory from soi1 to soi2 // (this uses prograde/retrograde sense from the rsoi1helio x vsoi1helio plane since shortway/longway has a 180 @@ -332,22 +334,22 @@ private void NLPFunction(double[] x, double[] fi, double[,] jac, object? obj = n var best = new Solution(V3.zero, 0.0, 0.0, 0.0, double.PositiveInfinity, double.PositiveInfinity); - _direction1 = TransferGeometry.ShortWay; + _direction1 = TransferGeometry.Prograde; _direction2 = TransferGeometry.Prograde; Solution sol1 = RunOptimizer(x0short, bndl, bndu, optguard); best = UpdateBestSolution(sol1, best); - _direction1 = TransferGeometry.LongWay; + _direction1 = TransferGeometry.Retrograde; _direction2 = TransferGeometry.Prograde; Solution sol2 = RunOptimizer(x0short, bndl, bndu, optguard); best = UpdateBestSolution(sol2, best); - _direction1 = TransferGeometry.ShortWay; + _direction1 = TransferGeometry.Prograde; _direction2 = TransferGeometry.Retrograde; Solution sol3 = RunOptimizer(x0long, bndl, bndu, optguard); best = UpdateBestSolution(sol3, best); - _direction1 = TransferGeometry.LongWay; + _direction1 = TransferGeometry.Retrograde; _direction2 = TransferGeometry.Retrograde; Solution sol4 = RunOptimizer(x0long, bndl, bndu, optguard); best = UpdateBestSolution(sol4, best); diff --git a/MechJebLibTest/PSGTests/AscentTests/KerbinTests.cs b/MechJebLibTest/PSGTests/AscentTests/KerbinTests.cs index 89f7731bb..af920a268 100644 --- a/MechJebLibTest/PSGTests/AscentTests/KerbinTests.cs +++ b/MechJebLibTest/PSGTests/AscentTests/KerbinTests.cs @@ -38,8 +38,8 @@ private void MainSailTinCanVacuum() psg.PrimalFeasibility.ShouldBeZero(1e-5); solution.Vgo(t0).ShouldEqual(2603.1239482534565, 1e-3); - // there's about 8 seconds extra burntime here due to throttling down, which these tests may be very sensitive to - solution.Tgo(t0).ShouldEqual(554.04809755858685, 1e-3); + // there's about 14 seconds extra burntime here due to throttling down, which these tests may be very sensitive to + solution.Tgo(t0).ShouldEqual(560.53674689893228, 1e-3); (V3 rf, V3 vf) = solution.TerminalStateVectors(); } @@ -68,7 +68,7 @@ private void MainSailTinCanAtmo() psg.PrimalFeasibility.ShouldBeZero(1e-5); solution.Vgo(t0).ShouldEqual(3521.7544134539125, 1e-3); // there's about 110 seconds extra burntime here due to throttling down, which these tests may be extremely sensitive to - solution.Tgo(t0).ShouldEqual(394.66454166378765, 1e-3); + solution.Tgo(t0).ShouldEqual(388.2458105314372, 1e-3); (V3 rf, V3 vf) = solution.TerminalStateVectors(); } diff --git a/alglib/alglibinternal.cs b/alglib/alglibinternal.cs index abb8734e3..ad806c8f1 100755 --- a/alglib/alglibinternal.cs +++ b/alglib/alglibinternal.cs @@ -1,5 +1,5 @@ /************************************************************************* -ALGLIB 4.07.0 (source code generated 2025-12-29) +ALGLIB 4.08.0 (source code generated 2026-06-08) Copyright (c) Sergey Bochkanov (ALGLIB project). >>> SOURCE LICENSE >>> @@ -301,7 +301,7 @@ public override void init() public override alglib.apobject make_copy() { scomplexarray _result = new scomplexarray(); - _result.val = (alglib.complex[])val.Clone(); + _result.val = (complex[])val.Clone(); return _result; } }; @@ -495,7 +495,7 @@ public static double coalesce(double a, double result = 0; result = a; - if( (double)(a)==(double)(0.0) ) + if( a==0.0 ) { result = b; } @@ -962,6 +962,54 @@ public static void rvectorsetlengthatleast(ref double[] x, } + /************************************************************************* + Retrieves a real array from a pool and checks that it is long enough. + + -- ALGLIB -- + Copyright 06.05.2026 by Bochkanov Sergey + *************************************************************************/ + public static void nrpoolretrivechk(alglib.ap.nxpool pool, + ref double[] x, + int n, + alglib.xparams _params) + { + pool.retrieve(ref x); + alglib.ap.assert(alglib.ap.len(x)>=n, "nrPoolRetriveChk: retrieved array is too short"); + } + + + /************************************************************************* + Retrieves an integer array from a pool and checks that it is long enough. + + -- ALGLIB -- + Copyright 06.05.2026 by Bochkanov Sergey + *************************************************************************/ + public static void nipoolretrivechk(alglib.ap.nxpool pool, + ref int[] x, + int n, + alglib.xparams _params) + { + pool.retrieve(ref x); + alglib.ap.assert(alglib.ap.len(x)>=n, "niPoolRetriveChk: retrieved array is too short"); + } + + + /************************************************************************* + Retrieves a boolean array from a pool and checks that it is long enough. + + -- ALGLIB -- + Copyright 06.05.2026 by Bochkanov Sergey + *************************************************************************/ + public static void nbpoolretrivechk(alglib.ap.nxpool pool, + ref bool[] x, + int n, + alglib.xparams _params) + { + pool.retrieve(ref x); + alglib.ap.assert(alglib.ap.len(x)>=n, "nbPoolRetriveChk: retrieved array is too short"); + } + + /************************************************************************* If Cols(X)=3, "FindPrimitiveRootAndInverse: N<3"); + proot = 0; + invproot = 0; + + // + // check that N is prime + // + alglib.ap.assert(isprime(n, _params), "FindPrimitiveRoot: N is not prime"); + + // + // Because N is prime, Euler totient function is equal to N-1 + // + phin = n-1; + + // + // Test different values of PRoot - from 2 to N-1. + // One of these values MUST be primitive root. + // + // For testing we use algorithm from Wiki (Primitive root modulo n): + // * compute phi(N) + // * determine the different prime factors of phi(N), say p1, ..., pk + // * for every element m of Zn*, compute m^(phi(N)/pi) mod N for i=1..k + // using a fast algorithm for modular exponentiation. + // * a number m for which these k results are all different from 1 is a + // primitive root. + // + for(candroot=2; candroot<=n-1; candroot++) { - result = result+x[i]*s[i]*(y[i]*s[i]); + + // + // We have current candidate root in CandRoot. + // + // Scan different prime factors of PhiN. Here: + // * F is a current candidate factor + // * Q is a current quotient - amount which was left after dividing PhiN + // by all previous factors + // + // For each factor, perform test mentioned above. + // + q = phin; + f = 2; + allnonone = true; + while( q>1 ) + { + if( q%f==0 ) + { + t = modexp(candroot, phin/f, n, _params); + if( t==1 ) + { + allnonone = false; + break; + } + while( q%f==0 ) + { + q = q/f; + } + } + f = f+1; + } + if( allnonone ) + { + proot = candroot; + break; + } } - return result; + alglib.ap.assert(proot>=2, "FindPrimitiveRoot: internal error (root not found)"); + + // + // Use extended Euclidean algorithm to find multiplicative inverse of primitive root + // + x = 0; + lastx = 1; + y = 1; + lasty = 0; + a = proot; + b = n; + while( b!=0 ) + { + q = a/b; + t = a%b; + a = b; + b = t; + t = lastx-q*x; + lastx = x; + x = t; + t = lasty-q*y; + lasty = y; + y = t; + } + while( lastx<0 ) + { + lastx = lastx+n; + } + invproot = lastx; + + // + // Check that it is safe to perform multiplication modulo N. + // Check results for consistency. + // + n2 = (n-1)*(n-1); + alglib.ap.assert(n2/(n-1)==n-1, "FindPrimitiveRoot: internal error"); + alglib.ap.assert(proot*invproot/proot==invproot, "FindPrimitiveRoot: internal error"); + alglib.ap.assert(proot*invproot/invproot==proot, "FindPrimitiveRoot: internal error"); + alglib.ap.assert(proot*invproot%n==1, "FindPrimitiveRoot: internal error"); } - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Computes dot product (X,A[i]) for elements [0,N) of vector X[] and row A[i,*] - - INPUT PARAMETERS: - N - vector length - X - array[N], vector to process - A - array[?,N], matrix to process - I - row index - - RESULT: - (X,Ai) - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static double rdotvr(int n, - double[] x, - double[,] a, - int i, + private static bool isprime(int n, alglib.xparams _params) { - double result = 0; - int j = 0; + bool result = new bool(); + int p = 0; - result = 0; - for(j=0; j<=n-1; j++) + result = false; + p = 2; + while( p*p<=n ) { - result = result+x[j]*a[i,j]; + if( n%p==0 ) + { + return result; + } + p = p+1; } + result = true; return result; } - #endif - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Computes dot product (X,A[i]) for rows A[ia,*] and B[ib,*] - - INPUT PARAMETERS: - N - vector length - X - array[N], vector to process - A - array[?,N], matrix to process - I - row index - - RESULT: - (X,Ai) - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static double rdotrr(int n, - double[,] a, - int ia, - double[,] b, - int ib, + private static int modmul(int a, + int b, + int n, alglib.xparams _params) { - double result = 0; - int j = 0; + int result = 0; + int t = 0; + double ra = 0; + double rb = 0; - result = 0; - for(j=0; j<=n-1; j++) + alglib.ap.assert(a>=0 && a=N"); + alglib.ap.assert(b>=0 && b=N"); + + // + // Base cases + // + ra = a; + rb = b; + if( b==0 || a==0 ) { - result = result+a[ia,j]*b[ib,j]; + result = 0; + return result; + } + if( b==1 || a==1 ) + { + result = a*b; + return result; + } + if( (double)(ra*rb)==(double)(a*b) ) + { + result = a*b%n; + return result; + } + + // + // Non-base cases + // + if( b%2==0 ) + { + + // + // A*B = (A*(B/2)) * 2 + // + // Product T=A*(B/2) is calculated recursively, product T*2 is + // calculated as follows: + // * result:=T-N + // * result:=result+T + // * if result<0 then result:=result+N + // + // In case integer result overflows, we generate exception + // + t = modmul(a, b/2, n, _params); + result = t-n; + result = result+t; + if( result<0 ) + { + result = result+n; + } + } + else + { + + // + // A*B = (A*(B div 2)) * 2 + A + // + // Product T=A*(B/2) is calculated recursively, product T*2 is + // calculated as follows: + // * result:=T-N + // * result:=result+T + // * if result<0 then result:=result+N + // + // In case integer result overflows, we generate exception + // + t = modmul(a, b/2, n, _params); + result = t-n; + result = result+t; + if( result<0 ) + { + result = result+n; + } + result = result-n; + result = result+a; + if( result<0 ) + { + result = result+n; + } } return result; } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Computes dot product (X,X) for elements [0,N) of X[] - - INPUT PARAMETERS: - N - vector length - X - array[N], vector to process - RESULT: - (X,X) - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static double rdotv2(int n, - double[] x, + private static int modexp(int a, + int b, + int n, alglib.xparams _params) { - double result = 0; - int i = 0; - double v = 0; + int result = 0; + int t = 0; - result = 0; - for(i=0; i<=n-1; i++) + alglib.ap.assert(a>=0 && a=N"); + alglib.ap.assert(b>=0, "ModExp: B<0"); + + // + // Base cases + // + if( b==0 ) { - v = x[i]; - result = result+v*v; + result = 1; + return result; + } + if( b==1 ) + { + result = a; + return result; + } + + // + // Non-base cases + // + if( b%2==0 ) + { + t = modmul(a, a, n, _params); + result = modexp(t, b/2, n, _params); + } + else + { + t = modmul(a, a, n, _params); + result = modexp(t, b/2, n, _params); + result = modmul(result, a, n, _params); } return result; } - #endif + } + public partial class ftbase + { /************************************************************************* - Computes scaled dot product (S*X,S*X) for elements [0,N) of X[] + This record stores execution plan for the fast transformation along with + preallocated temporary buffers and precalculated values. - INPUT PARAMETERS: - N - vector length - X - array[N], vector to process - S - array[N], vector to process + FIELDS: + Entries - plan entries, one row = one entry (see below for + description). + Buf0,Buf1,Buf2 - global temporary buffers; some of them are allocated, + some of them are not (as decided by plan generation + subroutine). + Buffer - global buffer whose size is equal to plan size. + There is one-to-one correspondence between elements + of global buffer and elements of array transformed. + Because of it global buffer can be used as temporary + thread-safe storage WITHOUT ACQUIRING LOCK - each + worker thread works with its part of input array, + and each part of input array corresponds to distinct + part of buffer. + + FORMAT OF THE ENTRIES TABLE: - RESULT: - (X,X) + Entries table is 2D array which stores one entry per row. Row format is: + row[0] operation type: + * 0 for "end of plan/subplan" + * +1 for "reference O(N^2) complex FFT" + * -1 for complex transposition + * -2 for multiplication by twiddle factors of complex FFT + * -3 for "start of plan/subplan" + row[1] repetition count, >=1 + row[2] base operand size (number of microvectors), >=1 + row[3] microvector size (measured in real numbers), >=1 + row[4] parameter0, meaning depends on row[0] + row[5] parameter1, meaning depends on row[0] - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static double rdotscl1v2(int n, - double[] x, - double[] s, - alglib.xparams _params) - { - double result = 0; - int i = 0; - double v = 0; + FORMAT OF THE DATA: - result = 0; - for(i=0; i<=n-1; i++) - { - v = x[i]*s[i]; - result = result+v*v; - } - return result; - } + Transformation plan works with row[1]*row[2]*row[3] real numbers, which + are (in most cases) interpreted as sequence of complex numbers. These data + are grouped as follows: + * we have row[1] contiguous OPERANDS, which can be treated separately + * each operand includes row[2] contiguous MICROVECTORS + * each microvector includes row[3] COMPONENTS, which can be treated separately + * pair of components form complex number, so in most cases row[3] will be even + Say, if you want to perform complex FFT of length 3, then: + * you have 1 operand: row[1]=1 + * operand consists of 3 microvectors: row[2]=3 + * each microvector has two components: row[3]=2 + * a pair of subsequent components is treated as complex number - /************************************************************************* - Computes scaled inf-norm of X: max(|x[i]/s[i]|) + if you want to perform TWO simultaneous complex FFT's of length 3, then you + can choose between two representations: + * 1 operand, 3 microvectors, 4 components; storage format is given below: + [ A0X A0Y B0X B0Y A1X A1Y B1X B1Y ... ] + (here A denotes first sequence, B - second one). + * 2 operands, 3 microvectors, 2 components; storage format is given below: + [ A0X A0Y A1X A2Y ... B0X B0Y B1X B1Y ... ] + Most FFT operations are supported only for the second format, but you + should remember that first format sometimes can be used too. - INPUT PARAMETERS: - N - vector length - X - array[N], vector to process - S - array[N], scales, S[i]<>0 + SUPPORTED OPERATIONS: - RESULT: - (X,X) + row[0]=0: + * "end of plan/subplan" + * in case we meet entry with such type, FFT transformation is finished + (or we return from recursive FFT subplan, in case it was subplan). - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static double rsclnrminf(int n, - double[] x, - double[] s, - alglib.xparams _params) - { - double result = 0; - int i = 0; - double v = 0; - - result = 0; - for(i=0; i<=n-1; i++) - { - v = x[i]/s[i]; - result = Math.Max(result, Math.Abs(v)); - } - return result; - } + row[0]=+1: + * "reference 1D complex FFT" + * we perform reference O(N^2) complex FFT on input data, which are treated + as row[1] arrays, each of row[2] complex numbers, and row[3] must be + equal to 2 + * transformation is performed using temporary buffer + row[0]=opBluesteinsFFT: + * input array is handled with Bluestein's algorithm (by zero-padding to + Param0 complex numbers). + * this plan calls Param0-point subplan which is located at offset Param1 + (offset is measured with respect to location of the calling entry) + * this plan uses precomputed quantities stored in Plan.PrecR at + offset Param2. + * transformation is performed using 4 temporary buffers, which are + retrieved from Plan.BluesteinPool. - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs inplace addition of Y[] to X[] + row[0]=+3: + * "optimized 1D complex FFT" + * this function supports only several operand sizes: from 1 to 5. + These transforms are hard-coded and performed very efficiently - INPUT PARAMETERS: - N - vector length - Alpha - multiplier - Y - array[N], vector to process - X - array[N], vector to process + row[0]=opRadersFFT: + * input array is handled with Rader's algorithm (permutation and + reduction to N-1-point FFT) + * this plan calls N-1-point subplan which is located at offset Param0 + (offset is measured with respect to location of the calling entry) + * this plan uses precomputed primitive root and its inverse (modulo N) + which are stored in Param1 and Param2. + * Param3 stores offset of the precomputed data for the plan + * plan length must be prime, (N-1)*(N-1) must fit into integer variable - RESULT: - X := X + alpha*Y + row[0]=-1 + * "complex transposition" + * input data are treated as row[1] independent arrays, which are processed + separately + * each of operands is treated as matrix with row[4] rows and row[2]/row[4] + columns. Each element of the matrix is microvector with row[3] components. + * transposition is performed using temporary buffer - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void raddv(int n, - double alpha, - double[] y, - double[] x, - alglib.xparams _params) - { - int i = 0; + row[0]=-2 + * "multiplication by twiddle factors of complex FFT" + * input data are treated as row[1] independent arrays, which are processed + separately + * row[4] contains N1 - length of the "first FFT" in a Cooley-Tukey FFT + algorithm + * this function does not require temporary buffers - for(i=0; i<=n-1; i++) - { - x[i] = x[i]+alpha*y[i]; - } - } - #endif + row[0]=-3 + * "start of the plan" + * each subplan must start from this entry + * param0 is ignored + * param1 stores approximate (optimistic) estimate of KFLOPs required to + transform one operand of the plan. Total cost of the plan is approximately + equal to row[1]*param1 KFLOPs. + * this function does not require temporary buffers + row[0]=-4 + * "jump" + * param0 stores relative offset of the jump site + (+1 corresponds to the next entry) - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs inplace addition of Y[]*Z[] to X[] + row[0]=-5 + * "parallel call" + * input data are treated as row[1] independent arrays + * child subplan is applied independently for each of arrays - row[1] times + * subplan length must be equal to row[2]*row[3] + * param0 stores relative offset of the child subplan site + (+1 corresponds to the next entry) + * param1 stores approximate total cost of plan, measured in UNITS + (1 UNIT = 100 KFLOPs). Plan cost must be rounded DOWN to nearest integer. - INPUT PARAMETERS: - N - vector length - Y - array[N], vector to process - Z - array[N], vector to process - X - array[N], vector to process - RESULT: - X := X + Y*Z + + TODO + 2. from KFLOPs to UNITs, 1 UNIT = 100 000 FLOP!!!!!!!!!!! - -- ALGLIB -- - Copyright 29.10.2021 by Bochkanov Sergey + 3. from IsRoot to TaskType = {0, -1, +1}; or maybe, add IsSeparatePlan + to distinguish root of child subplan from global root which uses + separate buffer + + 4. child subplans in parallel call must NOT use buffer provided by parent plan; + they must allocate their own local buffer *************************************************************************/ - public static void rmuladdv(int n, - double[] y, - double[] z, - double[] x, - alglib.xparams _params) + public class fasttransformplan : apobject { - int i = 0; - - for(i=0; i<=n-1; i++) + public int[,] entries; + public double[] buffer; + public double[] precr; + public double[] preci; + public alglib.smp.shared_pool bluesteinpool; + public fasttransformplan() { - x[i] = x[i]+y[i]*z[i]; + init(); } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs inplace subtraction of Y[]*Z[] from X[] + public override void init() + { + entries = new int[0,0]; + buffer = new double[0]; + precr = new double[0]; + preci = new double[0]; + bluesteinpool = new alglib.smp.shared_pool(); + } + public override alglib.apobject make_copy() + { + fasttransformplan _result = new fasttransformplan(); + _result.entries = (int[,])entries.Clone(); + _result.buffer = (double[])buffer.Clone(); + _result.precr = (double[])precr.Clone(); + _result.preci = (double[])preci.Clone(); + _result.bluesteinpool = bluesteinpool!=null ? (alglib.smp.shared_pool)bluesteinpool.make_copy() : null; + return _result; + } + }; - INPUT PARAMETERS: - N - vector length - Y - array[N], vector to process - Z - array[N], vector to process - X - array[N], vector to process - RESULT: - X := X - Y*Z - -- ALGLIB -- - Copyright 29.10.2021 by Bochkanov Sergey - *************************************************************************/ - public static void rnegmuladdv(int n, - double[] y, - double[] z, - double[] x, - alglib.xparams _params) - { - int i = 0; - for(i=0; i<=n-1; i++) - { - x[i] = x[i]-y[i]*z[i]; - } - } - #endif + public const int coltype = 0; + public const int coloperandscnt = 1; + public const int coloperandsize = 2; + public const int colmicrovectorsize = 3; + public const int colparam0 = 4; + public const int colparam1 = 5; + public const int colparam2 = 6; + public const int colparam3 = 7; + public const int colscnt = 8; + public const int opend = 0; + public const int opcomplexreffft = 1; + public const int opbluesteinsfft = 2; + public const int opcomplexcodeletfft = 3; + public const int opcomplexcodelettwfft = 4; + public const int opradersfft = 5; + public const int opcomplextranspose = -1; + public const int opcomplexfftfactors = -2; + public const int opstart = -3; + public const int opjmp = -4; + public const int opparallelcall = -5; + public const int maxradix = 6; + public const int updatetw = 16; + public const int recursivethreshold = 1024; + public const int raderthreshold = 19; + public const int ftbasecodeletrecommended = 5; + public const double ftbaseinefficiencyfactor = 1.3; + public const int ftbasemaxsmoothfactor = 5; - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs addition of Y[]*Z[] to X[], with result being stored to R[] + This subroutine generates FFT plan for K complex FFT's with length N each. INPUT PARAMETERS: - N - vector length - Y - array[N], vector to process - Z - array[N], vector to process - X - array[N], vector to process - R - array[N], vector to process - - RESULT: - R := X + Y*Z + N - FFT length (in complex numbers), N>=1 + K - number of repetitions, K>=1 + + OUTPUT PARAMETERS: + Plan - plan -- ALGLIB -- - Copyright 29.10.2021 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void rcopymuladdv(int n, - double[] y, - double[] z, - double[] x, - double[] r, + public static void ftcomplexfftplan(int n, + int k, + fasttransformplan plan, alglib.xparams _params) { - int i = 0; + apserv.srealarray bluesteinbuf = new apserv.srealarray(); + int rowptr = 0; + int bluesteinsize = 0; + int precrptr = 0; + int preciptr = 0; + int precrsize = 0; + int precisize = 0; - for(i=0; i<=n-1; i++) + + // + // Initial check for parameters + // + alglib.ap.assert(n>0, "FTComplexFFTPlan: N<=0"); + alglib.ap.assert(k>0, "FTComplexFFTPlan: K<=0"); + + // + // Determine required sizes of precomputed real and integer + // buffers. This stage of code is highly dependent on internals + // of FTComplexFFTPlanRec() and must be kept synchronized with + // possible changes in internals of plan generation function. + // + // Buffer size is determined as follows: + // * N is factorized + // * we factor out anything which is less or equal to MaxRadix + // * prime factor F>RaderThreshold requires 4*FTBaseFindSmooth(2*F-1) + // real entries to store precomputed Quantities for Bluestein's + // transformation + // * prime factor F<=RaderThreshold does NOT require + // precomputed storage + // + precrsize = 0; + precisize = 0; + ftdeterminespacerequirements(n, ref precrsize, ref precisize, _params); + if( precrsize>0 ) { - r[i] = x[i]+y[i]*z[i]; + plan.precr = new double[precrsize]; + } + if( precisize>0 ) + { + plan.preci = new double[precisize]; } + + // + // Generate plan + // + rowptr = 0; + precrptr = 0; + preciptr = 0; + bluesteinsize = 1; + plan.buffer = new double[2*n*k]; + ftcomplexfftplanrec(n, k, true, true, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + bluesteinbuf.val = new double[bluesteinsize]; + alglib.smp.ae_shared_pool_set_seed(plan.bluesteinpool, bluesteinbuf); + + // + // Check that actual amount of precomputed space used by transformation + // plan is EXACTLY equal to amount of space allocated by us. + // + alglib.ap.assert(precrptr==precrsize, "FTComplexFFTPlan: internal error (PrecRPtr<>PrecRSize)"); + alglib.ap.assert(preciptr==precisize, "FTComplexFFTPlan: internal error (PrecRPtr<>PrecRSize)"); } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs subtraction of Y[]*Z[] from X[], with result being stored to R[] + This subroutine applies transformation plan to input/output array A. INPUT PARAMETERS: - N - vector length - Y - array[N], vector to process - Z - array[N], vector to process - X - array[N], vector to process - R - array[N], vector to process - - RESULT: - R := X - Y*Z + Plan - transformation plan + A - array, must be large enough for plan to work + OffsA - offset of the subarray to process + RepCnt - repetition count (transformation is repeatedly applied + to subsequent subarrays) + + OUTPUT PARAMETERS: + Plan - plan (temporary buffers can be modified, plan itself + is unchanged and can be reused) + A - transformed array -- ALGLIB -- - Copyright 29.10.2021 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void rcopynegmuladdv(int n, - double[] y, - double[] z, - double[] x, - double[] r, + public static void ftapplyplan(fasttransformplan plan, + double[] a, + int offsa, + int repcnt, alglib.xparams _params) { + int plansize = 0; int i = 0; - for(i=0; i<=n-1; i++) + plansize = plan.entries[0,coloperandscnt]*plan.entries[0,coloperandsize]*plan.entries[0,colmicrovectorsize]; + for(i=0; i<=repcnt-1; i++) { - r[i] = x[i]-y[i]*z[i]; + ftapplysubplan(plan, 0, a, offsa+plansize*i, 0, plan.buffer, 1, _params); } } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs inplace addition of Y[] to X[] + Returns good factorization N=N1*N2. - INPUT PARAMETERS: - N - vector length - Alpha - multiplier - Y - source vector - OffsY - source offset - X - destination vector - OffsX - destination offset + Usually N1<=N2 (but not always - small N's may be exception). + if N1<>1 then N2<>1. - RESULT: - X := X + alpha*Y + Factorization is chosen depending on task type and codelets we have. -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 01.05.2009 by Bochkanov Sergey *************************************************************************/ - public static void raddvx(int n, - double alpha, - double[] y, - int offsy, - double[] x, - int offsx, + public static void ftbasefactorize(int n, + int tasktype, + ref int n1, + ref int n2, alglib.xparams _params) { - int i = 0; + int j = 0; - for(i=0; i<=n-1; i++) + n1 = 0; + n2 = 0; + + n1 = 0; + n2 = 0; + + // + // try to find good codelet + // + if( n1*n2!=n ) { - x[offsx+i] = x[offsx+i]+alpha*y[offsy+i]; + for(j=ftbasecodeletrecommended; j>=2; j--) + { + if( n%j==0 ) + { + n1 = j; + n2 = n/j; + break; + } + } + } + + // + // try to factorize N + // + if( n1*n2!=n ) + { + for(j=ftbasecodeletrecommended+1; j<=n-1; j++) + { + if( n%j==0 ) + { + n1 = j; + n2 = n/j; + break; + } + } + } + + // + // looks like N is prime :( + // + if( n1*n2!=n ) + { + n1 = 1; + n2 = n; + } + + // + // normalize + // + if( n2==1 && n1!=1 ) + { + n2 = n1; + n1 = 1; } } - #endif /************************************************************************* - Performs inplace addition of vector Y[] to column X[] - - INPUT PARAMETERS: - N - vector length - Alpha - multiplier - Y - vector to add - X - target column ColIdx - - RESULT: - X := X + alpha*Y + Is number smooth? -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 01.05.2009 by Bochkanov Sergey *************************************************************************/ - public static void raddvc(int n, - double alpha, - double[] y, - double[,] x, - int colidx, + public static bool ftbaseissmooth(int n, alglib.xparams _params) { + bool result = new bool(); int i = 0; - for(i=0; i<=n-1; i++) + for(i=2; i<=ftbasemaxsmoothfactor; i++) { - x[i,colidx] = x[i,colidx]+alpha*y[i]; + while( n%i==0 ) + { + n = n/i; + } } + result = n==1; + return result; } - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs inplace addition of vector Y[] to row X[] - - INPUT PARAMETERS: - N - vector length - Alpha - multiplier - Y - vector to add - X - target row RowIdx - - RESULT: - X := X + alpha*Y + Returns smallest smooth (divisible only by 2, 3, 5) number that is greater + than or equal to max(N,2) -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 01.05.2009 by Bochkanov Sergey *************************************************************************/ - public static void raddvr(int n, - double alpha, - double[] y, - double[,] x, - int rowidx, + public static int ftbasefindsmooth(int n, alglib.xparams _params) { - int i = 0; + int result = 0; + int best = 0; - for(i=0; i<=n-1; i++) + best = 2; + while( bestRaderThreshold requires 4*FTBaseFindSmooth(2*F-1) + // real entries to store precomputed Quantities for Bluestein's + // transformation + // * prime factor F<=RaderThreshold requires 2*(F-1)+ESTIMATE(F-1) + // precomputed storage + // + ncur = n; + for(i=2; i<=maxradix; i++) { - x[i] = x[i]/y[i]; + while( ncur%i==0 ) + { + ncur = ncur/i; + } } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs componentwise division of row X[] by vector Y[] - - INPUT PARAMETERS: - N - vector length - Y - vector to divide by - X - target row RowIdx - - RESULT: - X := componentwise(X/Y) - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rmergedivvr(int n, - double[] y, - double[,] x, - int rowidx, - alglib.xparams _params) - { - int i = 0; - - for(i=0; i<=n-1; i++) + f = 2; + while( f<=ncur ) { - x[rowidx,i] = x[rowidx,i]/y[i]; + while( ncur%f==0 ) + { + if( f>raderthreshold ) + { + precrsize = precrsize+4*ftbasefindsmooth(2*f-1, _params); + } + else + { + precrsize = precrsize+2*(f-1); + ftdeterminespacerequirements(f-1, ref precrsize, ref precisize, _params); + } + ncur = ncur/f; + } + f = f+1; } } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs componentwise division of row X[] by vector Y[] + Recurrent function called by FTComplexFFTPlan() and other functions. It + recursively builds transformation plan INPUT PARAMETERS: - N - vector length - Y - vector to divide by - X - target row RowIdx - - RESULT: - X := componentwise(X/Y) - + N - FFT length (in complex numbers), N>=1 + K - number of repetitions, K>=1 + ChildPlan - if True, plan generator inserts OpStart/opEnd in the + plan header/footer. + TopmostPlan - if True, plan generator assumes that it is topmost plan: + * it may use global buffer for transpositions + and there is no other plan which executes in parallel + RowPtr - index which points to past-the-last entry generated so far + BluesteinSize- amount of storage (in real numbers) required for Bluestein buffer + PrecRPtr - pointer to unused part of precomputed real buffer (Plan.PrecR): + * when this function stores some data to precomputed buffer, + it advances pointer. + * it is responsibility of the function to assert that + Plan.PrecR has enough space to store data before actually + writing to buffer. + * it is responsibility of the caller to allocate enough + space before calling this function + PrecIPtr - pointer to unused part of precomputed integer buffer (Plan.PrecI): + * when this function stores some data to precomputed buffer, + it advances pointer. + * it is responsibility of the function to assert that + Plan.PrecR has enough space to store data before actually + writing to buffer. + * it is responsibility of the caller to allocate enough + space before calling this function + Plan - plan (generated so far) + + OUTPUT PARAMETERS: + RowPtr - updated pointer (advanced by number of entries generated + by function) + BluesteinSize- updated amount + (may be increased, but may never be decreased) + + NOTE: in case TopmostPlan is True, ChildPlan is also must be True. + -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void rmergedivrv(int n, - double[,] y, - int rowidx, - double[] x, + private static void ftcomplexfftplanrec(int n, + int k, + bool childplan, + bool topmostplan, + ref int rowptr, + ref int bluesteinsize, + ref int precrptr, + ref int preciptr, + fasttransformplan plan, alglib.xparams _params) { - int i = 0; + apserv.srealarray localbuf = new apserv.srealarray(); + int m = 0; + int n1 = 0; + int n2 = 0; + int gq = 0; + int giq = 0; + int row0 = 0; + int row1 = 0; + int row2 = 0; + int row3 = 0; - for(i=0; i<=n-1; i++) + alglib.ap.assert(n>0, "FTComplexFFTPlan: N<=0"); + alglib.ap.assert(k>0, "FTComplexFFTPlan: K<=0"); + alglib.ap.assert(!topmostplan || childplan, "FTComplexFFTPlan: ChildPlan is inconsistent with TopmostPlan"); + + // + // Try to generate "topmost" plan + // + if( topmostplan && n>recursivethreshold ) { - x[i] = x[i]/y[rowidx,i]; - } - } - #endif + ftfactorize(n, false, ref n1, ref n2, _params); + if( n1*n2==0 ) + { + + // + // Handle prime-factor FFT with Bluestein's FFT. + // Determine size of Bluestein's buffer. + // + m = ftbasefindsmooth(2*n-1, _params); + bluesteinsize = Math.Max(2*m, bluesteinsize); + + // + // Generate plan + // + ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + ftpushentry4(plan, ref rowptr, opbluesteinsfft, k, n, 2, m, 2, precrptr, 0, _params); + row0 = rowptr; + ftpushentry(plan, ref rowptr, opjmp, 0, 0, 0, 0, _params); + ftcomplexfftplanrec(m, 1, true, true, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + row1 = rowptr; + plan.entries[row0,colparam0] = row1-row0; + ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); + + // + // Fill precomputed buffer + // + ftprecomputebluesteinsfft(n, m, plan.precr, precrptr, _params); + + // + // Update pointer to the precomputed area + // + precrptr = precrptr+4*m; + } + else + { + + // + // Handle composite FFT with recursive Cooley-Tukey which + // uses global buffer instead of local one. + // + ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1, _params); + row0 = rowptr; + ftpushentry2(plan, ref rowptr, opparallelcall, k*n2, n1, 2, 0, ftoptimisticestimate(n, _params), _params); + ftpushentry(plan, ref rowptr, opcomplexfftfactors, k, n, 2, n1, _params); + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n2, _params); + row2 = rowptr; + ftpushentry2(plan, ref rowptr, opparallelcall, k*n1, n2, 2, 0, ftoptimisticestimate(n, _params), _params); + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1, _params); + ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); + row1 = rowptr; + ftcomplexfftplanrec(n1, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + plan.entries[row0,colparam0] = row1-row0; + row3 = rowptr; + ftcomplexfftplanrec(n2, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + plan.entries[row2,colparam0] = row3-row2; + } + return; + } + + // + // Prepare "non-topmost" plan: + // * calculate factorization + // * use local (shared) buffer + // * update buffer size - ANY plan will need at least + // 2*N temporaries, additional requirements can be + // applied later + // + ftfactorize(n, false, ref n1, ref n2, _params); + + // + // Handle FFT's with N1*N2=0: either small-N or prime-factor + // + if( n1*n2==0 ) + { + if( n<=maxradix ) + { + + // + // Small-N FFT + // + if( childplan ) + { + ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + } + ftpushentry(plan, ref rowptr, opcomplexcodeletfft, k, n, 2, 0, _params); + if( childplan ) + { + ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); + } + return; + } + if( n<=raderthreshold ) + { + + // + // Handle prime-factor FFT's with Rader's FFT + // + m = n-1; + if( childplan ) + { + ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + } + ntheory.findprimitiverootandinverse(n, ref gq, ref giq, _params); + ftpushentry4(plan, ref rowptr, opradersfft, k, n, 2, 2, gq, giq, precrptr, _params); + ftprecomputeradersfft(n, gq, giq, plan.precr, precrptr, _params); + precrptr = precrptr+2*(n-1); + row0 = rowptr; + ftpushentry(plan, ref rowptr, opjmp, 0, 0, 0, 0, _params); + ftcomplexfftplanrec(m, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + row1 = rowptr; + plan.entries[row0,colparam0] = row1-row0; + if( childplan ) + { + ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); + } + } + else + { + + // + // Handle prime-factor FFT's with Bluestein's FFT + // + m = ftbasefindsmooth(2*n-1, _params); + bluesteinsize = Math.Max(2*m, bluesteinsize); + if( childplan ) + { + ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + } + ftpushentry4(plan, ref rowptr, opbluesteinsfft, k, n, 2, m, 2, precrptr, 0, _params); + ftprecomputebluesteinsfft(n, m, plan.precr, precrptr, _params); + precrptr = precrptr+4*m; + row0 = rowptr; + ftpushentry(plan, ref rowptr, opjmp, 0, 0, 0, 0, _params); + ftcomplexfftplanrec(m, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + row1 = rowptr; + plan.entries[row0,colparam0] = row1-row0; + if( childplan ) + { + ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); + } + } + return; + } + + // + // Handle Cooley-Tukey FFT with small N1 + // + if( n1<=maxradix ) + { + + // + // Specialized transformation for small N1: + // * N2 short inplace FFT's, each N1-point, with integrated twiddle factors + // * N1 long FFT's + // * final transposition + // + if( childplan ) + { + ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + } + ftpushentry(plan, ref rowptr, opcomplexcodelettwfft, k, n1, 2*n2, 0, _params); + ftcomplexfftplanrec(n2, k*n1, false, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1, _params); + if( childplan ) + { + ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); + } + return; + } + + // + // Handle general Cooley-Tukey FFT, either "flat" or "recursive" + // + if( n<=recursivethreshold ) + { + + // + // General code for large N1/N2, "flat" version without explicit recurrence + // (nested subplans are inserted directly into the body of the plan) + // + if( childplan ) + { + ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + } + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1, _params); + ftcomplexfftplanrec(n1, k*n2, false, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + ftpushentry(plan, ref rowptr, opcomplexfftfactors, k, n, 2, n1, _params); + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n2, _params); + ftcomplexfftplanrec(n2, k*n1, false, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1, _params); + if( childplan ) + { + ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); + } + } + else + { + + // + // General code for large N1/N2, "recursive" version - nested subplans + // are separated from the plan body. + // + // Generate parent plan. + // + if( childplan ) + { + ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + } + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1, _params); + row0 = rowptr; + ftpushentry2(plan, ref rowptr, opparallelcall, k*n2, n1, 2, 0, ftoptimisticestimate(n, _params), _params); + ftpushentry(plan, ref rowptr, opcomplexfftfactors, k, n, 2, n1, _params); + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n2, _params); + row2 = rowptr; + ftpushentry2(plan, ref rowptr, opparallelcall, k*n1, n2, 2, 0, ftoptimisticestimate(n, _params), _params); + ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1, _params); + if( childplan ) + { + ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); + } + + // + // Generate child subplans, insert refence to parent plans + // + row1 = rowptr; + ftcomplexfftplanrec(n1, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + plan.entries[row0,colparam0] = row1-row0; + row3 = rowptr; + ftcomplexfftplanrec(n2, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); + plan.entries[row2,colparam0] = row3-row2; + } + } - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs componentwise max of vector X[] and vector Y[] + This function pushes one more entry to the plan. It resizes Entries matrix + if needed. INPUT PARAMETERS: - N - vector length - Y - vector to multiply by - X - target vector - - RESULT: - X := componentwise_max(X,Y) + Plan - plan (generated so far) + RowPtr - index which points to past-the-last entry generated so far + EType - entry type + EOpCnt - operands count + EOpSize - operand size + EMcvSize - microvector size + EParam0 - parameter 0 + + OUTPUT PARAMETERS: + Plan - updated plan + RowPtr - updated pointer + NOTE: Param1 is set to -1. + -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rmergemaxv(int n, - double[] y, - double[] x, - alglib.xparams _params) - { - int i = 0; - - for(i=0; i<=n-1; i++) - { - x[i] = Math.Max(x[i], y[i]); - } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs componentwise max of row X[] and vector Y[] - - INPUT PARAMETERS: - N - vector length - Y - vector to multiply by - X - target row RowIdx - - RESULT: - X := componentwise_max(X,Y) - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rmergemaxvr(int n, - double[] y, - double[,] x, - int rowidx, - alglib.xparams _params) - { - int i = 0; - - for(i=0; i<=n-1; i++) - { - x[rowidx,i] = Math.Max(x[rowidx,i], y[i]); - } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs componentwise max of row X[I] and vector Y[] - - INPUT PARAMETERS: - N - vector length - X - matrix, I-th row is source - X - target row RowIdx - - RESULT: - Y := componentwise_max(Y,X) - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void rmergemaxrv(int n, - double[,] x, - int rowidx, - double[] y, + private static void ftpushentry(fasttransformplan plan, + ref int rowptr, + int etype, + int eopcnt, + int eopsize, + int emcvsize, + int eparam0, alglib.xparams _params) { - int i = 0; - - for(i=0; i<=n-1; i++) - { - y[i] = Math.Max(y[i], x[rowidx,i]); - } + ftpushentry2(plan, ref rowptr, etype, eopcnt, eopsize, emcvsize, eparam0, -1, _params); } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs componentwise max of vector X[] and vector Y[] + Same as FTPushEntry(), but sets Param0 AND Param1. + This function pushes one more entry to the plan. It resized Entries matrix + if needed. INPUT PARAMETERS: - N - vector length - Y - vector to multiply by - X - target vector - - RESULT: - X := componentwise_max(X,Y) + Plan - plan (generated so far) + RowPtr - index which points to past-the-last entry generated so far + EType - entry type + EOpCnt - operands count + EOpSize - operand size + EMcvSize - microvector size + EParam0 - parameter 0 + EParam1 - parameter 1 + + OUTPUT PARAMETERS: + Plan - updated plan + RowPtr - updated pointer -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void rmergeminv(int n, - double[] y, - double[] x, + private static void ftpushentry2(fasttransformplan plan, + ref int rowptr, + int etype, + int eopcnt, + int eopsize, + int emcvsize, + int eparam0, + int eparam1, alglib.xparams _params) { - int i = 0; - - for(i=0; i<=n-1; i++) + if( rowptr>=alglib.ap.rows(plan.entries) ) { - x[i] = Math.Min(x[i], y[i]); + apserv.imatrixresize(ref plan.entries, Math.Max(2*alglib.ap.rows(plan.entries), 1), colscnt, _params); } + plan.entries[rowptr,coltype] = etype; + plan.entries[rowptr,coloperandscnt] = eopcnt; + plan.entries[rowptr,coloperandsize] = eopsize; + plan.entries[rowptr,colmicrovectorsize] = emcvsize; + plan.entries[rowptr,colparam0] = eparam0; + plan.entries[rowptr,colparam1] = eparam1; + plan.entries[rowptr,colparam2] = 0; + plan.entries[rowptr,colparam3] = 0; + rowptr = rowptr+1; } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs componentwise max of row X[] and vector Y[] + Same as FTPushEntry(), but sets Param0, Param1, Param2 and Param3. + This function pushes one more entry to the plan. It resized Entries matrix + if needed. INPUT PARAMETERS: - N - vector length - Y - vector to multiply by - X - target row RowIdx - - RESULT: - X := componentwise_max(X,Y) + Plan - plan (generated so far) + RowPtr - index which points to past-the-last entry generated so far + EType - entry type + EOpCnt - operands count + EOpSize - operand size + EMcvSize - microvector size + EParam0 - parameter 0 + EParam1 - parameter 1 + EParam2 - parameter 2 + EParam3 - parameter 3 + + OUTPUT PARAMETERS: + Plan - updated plan + RowPtr - updated pointer -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void rmergeminvr(int n, - double[] y, - double[,] x, - int rowidx, + private static void ftpushentry4(fasttransformplan plan, + ref int rowptr, + int etype, + int eopcnt, + int eopsize, + int emcvsize, + int eparam0, + int eparam1, + int eparam2, + int eparam3, alglib.xparams _params) { - int i = 0; - - for(i=0; i<=n-1; i++) + if( rowptr>=alglib.ap.rows(plan.entries) ) { - x[rowidx,i] = Math.Min(x[rowidx,i], y[i]); + apserv.imatrixresize(ref plan.entries, Math.Max(2*alglib.ap.rows(plan.entries), 1), colscnt, _params); } + plan.entries[rowptr,coltype] = etype; + plan.entries[rowptr,coloperandscnt] = eopcnt; + plan.entries[rowptr,coloperandsize] = eopsize; + plan.entries[rowptr,colmicrovectorsize] = emcvsize; + plan.entries[rowptr,colparam0] = eparam0; + plan.entries[rowptr,colparam1] = eparam1; + plan.entries[rowptr,colparam2] = eparam2; + plan.entries[rowptr,colparam3] = eparam3; + rowptr = rowptr+1; } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs componentwise max of row X[I] and vector Y[] + This subroutine applies subplan to input/output array A. INPUT PARAMETERS: - N - vector length - X - matrix, I-th row is source - X - target row RowIdx - - RESULT: - X := componentwise_max(X,Y) + Plan - transformation plan + SubPlan - subplan index + A - array, must be large enough for plan to work + ABase - base offset in array A, this value points to start of + subarray whose length is equal to length of the plan + AOffset - offset with respect to ABase, 0<=AOffset1"); + n1 = plan.entries[rowidx,colparam0]; + n2 = operandsize/n1; + for(i=0; i<=operandscnt-1; i++) + { + ffttwcalc(a, abase+aoffset+i*operandsize*2, n1, n2, _params); + } + rowidx = rowidx+1; + continue; + } + + // + // Process "complex transposition" operation + // + if( operation==opcomplextranspose ) + { + alglib.ap.assert(microvectorsize==2, "FTApplySubPlan: MicrovectorSize<>1"); + n1 = plan.entries[rowidx,colparam0]; + n2 = operandsize/n1; + for(i=0; i<=operandscnt-1; i++) + { + internalcomplexlintranspose(a, n1, n2, abase+aoffset+i*operandsize*2, buf, _params); + } + rowidx = rowidx+1; + continue; + } + + // + // Error + // + alglib.ap.assert(false, "FTApplySubPlan: unexpected plan type"); } } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs inplace addition of Y[RIdx,...] to X[RIdxDst] + This subroutine applies complex reference FFT to input/output array A. - INPUT PARAMETERS: - N - vector length - Alpha - multiplier - Y - array[?,N], matrix whose RIdxSrc-th row is added - RIdxSrc - source row index - X - array[?,N], matrix whose RIdxDst-th row is target - RIdxDst - destination row index + VERY SLOW OPERATION, do not use it in real life plans :) - RESULT: - X := X + alpha*Y + INPUT PARAMETERS: + A - array, must be large enough for plan to work + Offs - offset of the subarray to process + OperandsCnt - operands count (see description of FastTransformPlan) + OperandSize - operand size (see description of FastTransformPlan) + MicrovectorSize-microvector size (see description of FastTransformPlan) + Buf - temporary array, must be at least OperandsCnt*OperandSize*MicrovectorSize + + OUTPUT PARAMETERS: + A - transformed array -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void raddrr(int n, - double alpha, - double[,] y, - int ridxsrc, - double[,] x, - int ridxdst, + private static void ftapplycomplexreffft(double[] a, + int offs, + int operandscnt, + int operandsize, + int microvectorsize, + double[] buf, alglib.xparams _params) { + int opidx = 0; int i = 0; + int k = 0; + double hre = 0; + double him = 0; + double c = 0; + double s = 0; + double re = 0; + double im = 0; + int n = 0; - for(i=0; i<=n-1; i++) + alglib.ap.assert(operandscnt>=1, "FTApplyComplexRefFFT: OperandsCnt<1"); + alglib.ap.assert(operandsize>=1, "FTApplyComplexRefFFT: OperandSize<1"); + alglib.ap.assert(microvectorsize==2, "FTApplyComplexRefFFT: MicrovectorSize<>2"); + n = operandsize; + for(opidx=0; opidx<=operandscnt-1; opidx++) { - x[ridxdst,i] = x[ridxdst,i]+alpha*y[ridxsrc,i]; + for(i=0; i<=n-1; i++) + { + hre = 0; + him = 0; + for(k=0; k<=n-1; k++) + { + re = a[offs+opidx*operandsize*2+2*k+0]; + im = a[offs+opidx*operandsize*2+2*k+1]; + c = Math.Cos(-(2*Math.PI*k*i/n)); + s = Math.Sin(-(2*Math.PI*k*i/n)); + hre = hre+c*re-s*im; + him = him+c*im+s*re; + } + buf[2*i+0] = hre; + buf[2*i+1] = him; + } + for(i=0; i<=operandsize*2-1; i++) + { + a[offs+opidx*operandsize*2+i] = buf[i]; + } } } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Performs inplace multiplication of X[] by V + This subroutine applies complex codelet FFT to input/output array A. INPUT PARAMETERS: - N - vector length - X - array[N], vector to process - V - multiplier - + A - array, must be large enough for plan to work + Offs - offset of the subarray to process + OperandsCnt - operands count (see description of FastTransformPlan) + OperandSize - operand size (see description of FastTransformPlan) + MicrovectorSize-microvector size, must be 2 + OUTPUT PARAMETERS: - X - elements 0...N-1 multiplied by V + A - transformed array -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void rmulv(int n, - double v, - double[] x, + private static void ftapplycomplexcodeletfft(double[] a, + int offs, + int operandscnt, + int operandsize, + int microvectorsize, alglib.xparams _params) { - int i = 0; + int opidx = 0; + int n = 0; + int aoffset = 0; + double a0x = 0; + double a0y = 0; + double a1x = 0; + double a1y = 0; + double a2x = 0; + double a2y = 0; + double a3x = 0; + double a3y = 0; + double a4x = 0; + double a4y = 0; + double a5x = 0; + double a5y = 0; + double v0 = 0; + double v1 = 0; + double v2 = 0; + double v3 = 0; + double t1x = 0; + double t1y = 0; + double t2x = 0; + double t2y = 0; + double t3x = 0; + double t3y = 0; + double t4x = 0; + double t4y = 0; + double t5x = 0; + double t5y = 0; + double m1x = 0; + double m1y = 0; + double m2x = 0; + double m2y = 0; + double m3x = 0; + double m3y = 0; + double m4x = 0; + double m4y = 0; + double m5x = 0; + double m5y = 0; + double s1x = 0; + double s1y = 0; + double s2x = 0; + double s2y = 0; + double s3x = 0; + double s3y = 0; + double s4x = 0; + double s4y = 0; + double s5x = 0; + double s5y = 0; + double c1 = 0; + double c2 = 0; + double c3 = 0; + double c4 = 0; + double c5 = 0; + double v = 0; - for(i=0; i<=n-1; i++) + alglib.ap.assert(operandscnt>=1, "FTApplyComplexCodeletFFT: OperandsCnt<1"); + alglib.ap.assert(operandsize>=1, "FTApplyComplexCodeletFFT: OperandSize<1"); + alglib.ap.assert(microvectorsize==2, "FTApplyComplexCodeletFFT: MicrovectorSize<>2"); + n = operandsize; + + // + // Hard-coded transforms for different N's + // + alglib.ap.assert(n<=maxradix, "FTApplyComplexCodeletFFT: N>MaxRadix"); + if( n==2 ) { - x[i] = x[i]*v; + for(opidx=0; opidx<=operandscnt-1; opidx++) + { + aoffset = offs+opidx*operandsize*2; + a0x = a[aoffset+0]; + a0y = a[aoffset+1]; + a1x = a[aoffset+2]; + a1y = a[aoffset+3]; + v0 = a0x+a1x; + v1 = a0y+a1y; + v2 = a0x-a1x; + v3 = a0y-a1y; + a[aoffset+0] = v0; + a[aoffset+1] = v1; + a[aoffset+2] = v2; + a[aoffset+3] = v3; + } + return; } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs inplace multiplication of X[] by V - - INPUT PARAMETERS: - N - row length - X - array[?,N], row to process - V - multiplier - - OUTPUT PARAMETERS: - X - elements 0...N-1 of row RowIdx are multiplied by V - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rmulr(int n, - double v, - double[,] x, - int rowidx, - alglib.xparams _params) - { - int i = 0; - - for(i=0; i<=n-1; i++) + if( n==3 ) { - x[rowidx,i] = x[rowidx,i]*v; + c1 = Math.Cos(2*Math.PI/3)-1; + c2 = Math.Sin(2*Math.PI/3); + for(opidx=0; opidx<=operandscnt-1; opidx++) + { + aoffset = offs+opidx*operandsize*2; + a0x = a[aoffset+0]; + a0y = a[aoffset+1]; + a1x = a[aoffset+2]; + a1y = a[aoffset+3]; + a2x = a[aoffset+4]; + a2y = a[aoffset+5]; + t1x = a1x+a2x; + t1y = a1y+a2y; + a0x = a0x+t1x; + a0y = a0y+t1y; + m1x = c1*t1x; + m1y = c1*t1y; + m2x = c2*(a1y-a2y); + m2y = c2*(a2x-a1x); + s1x = a0x+m1x; + s1y = a0y+m1y; + a1x = s1x+m2x; + a1y = s1y+m2y; + a2x = s1x-m2x; + a2y = s1y-m2y; + a[aoffset+0] = a0x; + a[aoffset+1] = a0y; + a[aoffset+2] = a1x; + a[aoffset+3] = a1y; + a[aoffset+4] = a2x; + a[aoffset+5] = a2y; + } + return; } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs inplace computation of Sqrt(X) - - INPUT PARAMETERS: - N - vector length - X - array[N], vector to process - - OUTPUT PARAMETERS: - X - elements 0...N-1 replaced by Sqrt(X) - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rsqrtv(int n, - double[] x, - alglib.xparams _params) - { - int i = 0; - - for(i=0; i<=n-1; i++) + if( n==4 ) { - x[i] = Math.Sqrt(x[i]); + for(opidx=0; opidx<=operandscnt-1; opidx++) + { + aoffset = offs+opidx*operandsize*2; + a0x = a[aoffset+0]; + a0y = a[aoffset+1]; + a1x = a[aoffset+2]; + a1y = a[aoffset+3]; + a2x = a[aoffset+4]; + a2y = a[aoffset+5]; + a3x = a[aoffset+6]; + a3y = a[aoffset+7]; + t1x = a0x+a2x; + t1y = a0y+a2y; + t2x = a1x+a3x; + t2y = a1y+a3y; + m2x = a0x-a2x; + m2y = a0y-a2y; + m3x = a1y-a3y; + m3y = a3x-a1x; + a[aoffset+0] = t1x+t2x; + a[aoffset+1] = t1y+t2y; + a[aoffset+4] = t1x-t2x; + a[aoffset+5] = t1y-t2y; + a[aoffset+2] = m2x+m3x; + a[aoffset+3] = m2y+m3y; + a[aoffset+6] = m2x-m3x; + a[aoffset+7] = m2y-m3y; + } + return; } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs inplace computation of Sqrt(X[RowIdx,*]) - - INPUT PARAMETERS: - N - vector length - X - array[?,N], matrix to process - - OUTPUT PARAMETERS: - X - elements 0...N-1 replaced by Sqrt(X) - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rsqrtr(int n, - double[,] x, - int rowidx, - alglib.xparams _params) - { - int i = 0; - - for(i=0; i<=n-1; i++) + if( n==5 ) { - x[rowidx,i] = Math.Sqrt(x[rowidx,i]); - } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Performs inplace multiplication of X[OffsX:OffsX+N-1] by V - - INPUT PARAMETERS: - N - subvector length - X - vector to process - V - multiplier - - OUTPUT PARAMETERS: - X - elements OffsX:OffsX+N-1 multiplied by V - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rmulvx(int n, - double v, - double[] x, - int offsx, - alglib.xparams _params) - { - int i = 0; - - for(i=0; i<=n-1; i++) - { - x[offsx+i] = x[offsx+i]*v; - } - } - #endif - - - /************************************************************************* - Returns minimum X - - INPUT PARAMETERS: - N - vector length - X - array[N], vector to process - - OUTPUT PARAMETERS: - max(X[i]) - zero for N=0 - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static double rminv(int n, - double[] x, - alglib.xparams _params) - { - double result = 0; - int i = 0; - double v = 0; - - if( n<=0 ) - { - result = 0; - return result; - } - result = x[0]; - for(i=1; i<=n-1; i++) - { - v = x[i]; - if( vresult ) + c1 = Math.Cos(2*Math.PI/3)-1; + c2 = Math.Sin(2*Math.PI/3); + c3 = Math.Cos(-(Math.PI/3)); + c4 = Math.Sin(-(Math.PI/3)); + for(opidx=0; opidx<=operandscnt-1; opidx++) { - result = v; + aoffset = offs+opidx*operandsize*2; + a0x = a[aoffset+0]; + a0y = a[aoffset+1]; + a1x = a[aoffset+2]; + a1y = a[aoffset+3]; + a2x = a[aoffset+4]; + a2y = a[aoffset+5]; + a3x = a[aoffset+6]; + a3y = a[aoffset+7]; + a4x = a[aoffset+8]; + a4y = a[aoffset+9]; + a5x = a[aoffset+10]; + a5y = a[aoffset+11]; + v0 = a0x; + v1 = a0y; + a0x = a0x+a3x; + a0y = a0y+a3y; + a3x = v0-a3x; + a3y = v1-a3y; + v0 = a1x; + v1 = a1y; + a1x = a1x+a4x; + a1y = a1y+a4y; + a4x = v0-a4x; + a4y = v1-a4y; + v0 = a2x; + v1 = a2y; + a2x = a2x+a5x; + a2y = a2y+a5y; + a5x = v0-a5x; + a5y = v1-a5y; + t4x = a4x*c3-a4y*c4; + t4y = a4x*c4+a4y*c3; + a4x = t4x; + a4y = t4y; + t5x = -(a5x*c3)-a5y*c4; + t5y = a5x*c4-a5y*c3; + a5x = t5x; + a5y = t5y; + t1x = a1x+a2x; + t1y = a1y+a2y; + a0x = a0x+t1x; + a0y = a0y+t1y; + m1x = c1*t1x; + m1y = c1*t1y; + m2x = c2*(a1y-a2y); + m2y = c2*(a2x-a1x); + s1x = a0x+m1x; + s1y = a0y+m1y; + a1x = s1x+m2x; + a1y = s1y+m2y; + a2x = s1x-m2x; + a2y = s1y-m2y; + t1x = a4x+a5x; + t1y = a4y+a5y; + a3x = a3x+t1x; + a3y = a3y+t1y; + m1x = c1*t1x; + m1y = c1*t1y; + m2x = c2*(a4y-a5y); + m2y = c2*(a5x-a4x); + s1x = a3x+m1x; + s1y = a3y+m1y; + a4x = s1x+m2x; + a4y = s1y+m2y; + a5x = s1x-m2x; + a5y = s1y-m2y; + a[aoffset+0] = a0x; + a[aoffset+1] = a0y; + a[aoffset+2] = a3x; + a[aoffset+3] = a3y; + a[aoffset+4] = a1x; + a[aoffset+5] = a1y; + a[aoffset+6] = a4x; + a[aoffset+7] = a4y; + a[aoffset+8] = a2x; + a[aoffset+9] = a2y; + a[aoffset+10] = a5x; + a[aoffset+11] = a5y; } + return; } - return result; } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Returns maximum |X| + This subroutine applies complex "integrated" codelet FFT to input/output + array A. "Integrated" codelet differs from "normal" one in following ways: + * it can work with MicrovectorSize>1 + * hence, it can be used in Cooley-Tukey FFT without transpositions + * it performs inlined multiplication by twiddle factors of Cooley-Tukey + FFT with N2=MicrovectorSize/2. INPUT PARAMETERS: - N - vector length - X - array[N], vector to process - + A - array, must be large enough for plan to work + Offs - offset of the subarray to process + OperandsCnt - operands count (see description of FastTransformPlan) + OperandSize - operand size (see description of FastTransformPlan) + MicrovectorSize-microvector size, must be 1 + OUTPUT PARAMETERS: - max(|X[i]|) - zero for N=0 + A - transformed array -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static double rmaxabsv(int n, - double[] x, - alglib.xparams _params) - { - double result = 0; - int i = 0; - double v = 0; - - result = 0; - for(i=0; i<=n-1; i++) - { - v = Math.Abs(x[i]); - if( v>result ) - { - result = v; - } - } - return result; - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Returns maximum X - - INPUT PARAMETERS: - N - vector length - X - matrix to process, RowIdx-th row is processed - - OUTPUT PARAMETERS: - max(X[RowIdx,i]) - zero for N=0 - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static double rmaxr(int n, - double[,] x, - int rowidx, + private static void ftapplycomplexcodelettwfft(double[] a, + int offs, + int operandscnt, + int operandsize, + int microvectorsize, alglib.xparams _params) { - double result = 0; - int i = 0; + int opidx = 0; + int mvidx = 0; + int n = 0; + int m = 0; + int aoffset0 = 0; + int aoffset2 = 0; + int aoffset4 = 0; + int aoffset6 = 0; + int aoffset8 = 0; + int aoffset10 = 0; + double a0x = 0; + double a0y = 0; + double a1x = 0; + double a1y = 0; + double a2x = 0; + double a2y = 0; + double a3x = 0; + double a3y = 0; + double a4x = 0; + double a4y = 0; + double a5x = 0; + double a5y = 0; + double v0 = 0; + double v1 = 0; + double v2 = 0; + double v3 = 0; + double q0x = 0; + double q0y = 0; + double t1x = 0; + double t1y = 0; + double t2x = 0; + double t2y = 0; + double t3x = 0; + double t3y = 0; + double t4x = 0; + double t4y = 0; + double t5x = 0; + double t5y = 0; + double m1x = 0; + double m1y = 0; + double m2x = 0; + double m2y = 0; + double m3x = 0; + double m3y = 0; + double m4x = 0; + double m4y = 0; + double m5x = 0; + double m5y = 0; + double s1x = 0; + double s1y = 0; + double s2x = 0; + double s2y = 0; + double s3x = 0; + double s3y = 0; + double s4x = 0; + double s4y = 0; + double s5x = 0; + double s5y = 0; + double c1 = 0; + double c2 = 0; + double c3 = 0; + double c4 = 0; + double c5 = 0; double v = 0; + double tw0 = 0; + double tw1 = 0; + double twx = 0; + double twxm1 = 0; + double twy = 0; + double tw2x = 0; + double tw2y = 0; + double tw3x = 0; + double tw3y = 0; + double tw4x = 0; + double tw4y = 0; + double tw5x = 0; + double tw5y = 0; - if( n<=0 ) - { - result = 0; - return result; - } - result = x[rowidx,0]; - for(i=1; i<=n-1; i++) + alglib.ap.assert(operandscnt>=1, "FTApplyComplexCodeletFFT: OperandsCnt<1"); + alglib.ap.assert(operandsize>=1, "FTApplyComplexCodeletFFT: OperandSize<1"); + alglib.ap.assert(microvectorsize>=1, "FTApplyComplexCodeletFFT: MicrovectorSize<>1"); + alglib.ap.assert(microvectorsize%2==0, "FTApplyComplexCodeletFFT: MicrovectorSize is not even"); + n = operandsize; + m = microvectorsize/2; + + // + // Hard-coded transforms for different N's + // + alglib.ap.assert(n<=maxradix, "FTApplyComplexCodeletTwFFT: N>MaxRadix"); + if( n==2 ) { - v = x[rowidx,i]; - if( v>result ) + v = -(2*Math.PI/(n*m)); + tw0 = -(2*math.sqr(Math.Sin(0.5*v))); + tw1 = Math.Sin(v); + for(opidx=0; opidx<=operandscnt-1; opidx++) { - result = v; + aoffset0 = offs+opidx*operandsize*microvectorsize; + aoffset2 = aoffset0+microvectorsize; + twxm1 = 0.0; + twy = 0.0; + for(mvidx=0; mvidx<=m-1; mvidx++) + { + a0x = a[aoffset0]; + a0y = a[aoffset0+1]; + a1x = a[aoffset2]; + a1y = a[aoffset2+1]; + v0 = a0x+a1x; + v1 = a0y+a1y; + v2 = a0x-a1x; + v3 = a0y-a1y; + a[aoffset0] = v0; + a[aoffset0+1] = v1; + a[aoffset2] = v2*(1+twxm1)-v3*twy; + a[aoffset2+1] = v3*(1+twxm1)+v2*twy; + aoffset0 = aoffset0+2; + aoffset2 = aoffset2+2; + if( (mvidx+1)%updatetw==0 ) + { + v = -(2*Math.PI*(mvidx+1)/(n*m)); + twxm1 = Math.Sin(0.5*v); + twxm1 = -(2*twxm1*twxm1); + twy = Math.Sin(v); + } + else + { + v = twxm1+tw0+twxm1*tw0-twy*tw1; + twy = twy+tw1+twxm1*tw1+twy*tw0; + twxm1 = v; + } + } } + return; } - return result; - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Returns maximum |X| - - INPUT PARAMETERS: - N - vector length - X - matrix to process, RowIdx-th row is processed - - OUTPUT PARAMETERS: - max(|X[RowIdx,i]|) - zero for N=0 - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static double rmaxabsr(int n, - double[,] x, - int rowidx, - alglib.xparams _params) - { - double result = 0; - int i = 0; - double v = 0; - - result = 0; - for(i=0; i<=n-1; i++) + if( n==3 ) { - v = Math.Abs(x[rowidx,i]); - if( v>result ) + v = -(2*Math.PI/(n*m)); + tw0 = -(2*math.sqr(Math.Sin(0.5*v))); + tw1 = Math.Sin(v); + c1 = Math.Cos(2*Math.PI/3)-1; + c2 = Math.Sin(2*Math.PI/3); + for(opidx=0; opidx<=operandscnt-1; opidx++) { - result = v; - } - } - return result; - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Sets vector X[] to V - - INPUT PARAMETERS: - N - vector length - V - value to set - X - array[N] - - OUTPUT PARAMETERS: - X - leading N elements are replaced by V - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rsetv(int n, - double v, - double[] x, - alglib.xparams _params) - { - int j = 0; - - for(j=0; j<=n-1; j++) - { - x[j] = v; - } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Sets X[OffsX:OffsX+N-1] to V - - INPUT PARAMETERS: - N - subvector length - V - value to set - X - array[N] - - OUTPUT PARAMETERS: - X - X[OffsX:OffsX+N-1] is replaced by V - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rsetvx(int n, - double v, - double[] x, - int offsx, - alglib.xparams _params) - { - int j = 0; - - for(j=0; j<=n-1; j++) - { - x[offsx+j] = v; - } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Sets vector X[] to V - - INPUT PARAMETERS: - N - vector length - V - value to set - X - array[N] - - OUTPUT PARAMETERS: - X - leading N elements are replaced by V - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void isetv(int n, - int v, - int[] x, - alglib.xparams _params) - { - int j = 0; - - for(j=0; j<=n-1; j++) - { - x[j] = v; - } - } - #endif - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Sets vector X[] to V - - INPUT PARAMETERS: - N - vector length - V - value to set - X - array[N] - - OUTPUT PARAMETERS: - X - leading N elements are replaced by V - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void bsetv(int n, - bool v, - bool[] x, - alglib.xparams _params) - { - int j = 0; - - for(j=0; j<=n-1; j++) - { - x[j] = v; - } - } - #endif - - - /************************************************************************* - Sets vector X[] to V - - INPUT PARAMETERS: - N - vector length - V - value to set - X - array[N] - - OUTPUT PARAMETERS: - X - leading N elements are replaced by V - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void csetv(int n, - complex v, - complex[] x, - alglib.xparams _params) - { - int j = 0; - - for(j=0; j<=n-1; j++) - { - x[j].x = v.x; - x[j].y = v.y; - } - } - - - /************************************************************************* - Sets matrix A[] to V - - INPUT PARAMETERS: - M, N - rows/cols count - V - value to set - A - array[M,N] - - OUTPUT PARAMETERS: - A - leading M rows, N cols are replaced by V - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void isetm(int m, - int n, - int v, - int[,] a, - alglib.xparams _params) - { - int i = 0; - int j = 0; - - for(i=0; i<=m-1; i++) - { - for(j=0; j<=n-1; j++) - { - a[i,j] = v; + aoffset0 = offs+opidx*operandsize*microvectorsize; + aoffset2 = aoffset0+microvectorsize; + aoffset4 = aoffset2+microvectorsize; + twx = 1.0; + twxm1 = 0.0; + twy = 0.0; + for(mvidx=0; mvidx<=m-1; mvidx++) + { + a0x = a[aoffset0]; + a0y = a[aoffset0+1]; + a1x = a[aoffset2]; + a1y = a[aoffset2+1]; + a2x = a[aoffset4]; + a2y = a[aoffset4+1]; + t1x = a1x+a2x; + t1y = a1y+a2y; + a0x = a0x+t1x; + a0y = a0y+t1y; + m1x = c1*t1x; + m1y = c1*t1y; + m2x = c2*(a1y-a2y); + m2y = c2*(a2x-a1x); + s1x = a0x+m1x; + s1y = a0y+m1y; + a1x = s1x+m2x; + a1y = s1y+m2y; + a2x = s1x-m2x; + a2y = s1y-m2y; + tw2x = twx*twx-twy*twy; + tw2y = 2*twx*twy; + a[aoffset0] = a0x; + a[aoffset0+1] = a0y; + a[aoffset2] = a1x*twx-a1y*twy; + a[aoffset2+1] = a1y*twx+a1x*twy; + a[aoffset4] = a2x*tw2x-a2y*tw2y; + a[aoffset4+1] = a2y*tw2x+a2x*tw2y; + aoffset0 = aoffset0+2; + aoffset2 = aoffset2+2; + aoffset4 = aoffset4+2; + if( (mvidx+1)%updatetw==0 ) + { + v = -(2*Math.PI*(mvidx+1)/(n*m)); + twxm1 = Math.Sin(0.5*v); + twxm1 = -(2*twxm1*twxm1); + twy = Math.Sin(v); + twx = twxm1+1; + } + else + { + v = twxm1+tw0+twxm1*tw0-twy*tw1; + twy = twy+tw1+twxm1*tw1+twy*tw0; + twxm1 = v; + twx = v+1; + } + } } + return; } - } - - - #if ALGLIB_NO_FAST_KERNELS - /************************************************************************* - Sets matrix A[] to V - - INPUT PARAMETERS: - M, N - rows/cols count - V - value to set - A - array[M,N] - - OUTPUT PARAMETERS: - A - leading M rows, N cols are replaced by V - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rsetm(int m, - int n, - double v, - double[,] a, - alglib.xparams _params) - { - int i = 0; - int j = 0; - - for(i=0; i<=m-1; i++) + if( n==4 ) { - for(j=0; j<=n-1; j++) + v = -(2*Math.PI/(n*m)); + tw0 = -(2*math.sqr(Math.Sin(0.5*v))); + tw1 = Math.Sin(v); + for(opidx=0; opidx<=operandscnt-1; opidx++) { - a[i,j] = v; - } - } - } - #endif - - - /************************************************************************* - Sets vector X[] to V, reallocating X[] if too small - - INPUT PARAMETERS: - N - vector length - V - value to set - X - possibly preallocated array - - OUTPUT PARAMETERS: - X - leading N elements are replaced by V; array is reallocated - if its length is less than N. - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rsetallocv(int n, - double v, - ref double[] x, - alglib.xparams _params) - { - if( alglib.ap.len(x)=N - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void rallocv(int n, - ref double[] x, - alglib.xparams _params) - { - if( alglib.ap.len(x)=N + PrecR - data at Offs:Offs+4*M-1 are modified: + * PrecR[Offs:Offs+2*M-1] stores Z[k]=exp(i*pi*k^2/N) + * PrecR[Offs+2*M:Offs+4*M-1] stores FFT of the Z + Other parts of PrecR are unchanged. + + NOTE: this function performs internal M-point FFT. It allocates temporary + plan which is destroyed after leaving this function. -- ALGLIB -- - Copyright 20.07.2022 by Bochkanov Sergey + Copyright 08.05.2013 by Bochkanov Sergey *************************************************************************/ - public static void callocv(int n, - ref complex[] x, + private static void ftprecomputebluesteinsfft(int n, + int m, + double[] precr, + int offs, alglib.xparams _params) { - if( alglib.ap.len(x)=N - - -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey - *************************************************************************/ - public static void iallocv(int n, - ref int[] x, - alglib.xparams _params) - { - if( alglib.ap.len(x)=N + A - transformed array -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 05.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void ballocv(int n, - ref bool[] x, - alglib.xparams _params) - { - if( alglib.ap.len(x)=1, "FTApplyComplexRefFFT: OperandsCnt<1"); + + // + // Process operands + // + for(opidx=0; opidx<=operandscnt-1; opidx++) { - a[i,j] = v; + + // + // fill QA + // + kq = 1; + p0 = abase+aoffset+opidx*n*2; + p1 = aoffset+opidx*n*2; + rx = a[p0+0]; + ry = a[p0+1]; + x0 = rx; + y0 = ry; + for(q=0; q<=n-2; q++) + { + ax = a[p0+2*kq+0]; + ay = a[p0+2*kq+1]; + buf[p1+0] = ax; + buf[p1+1] = ay; + rx = rx+ax; + ry = ry+ay; + kq = kq*rq%n; + p1 = p1+2; + } + p0 = abase+aoffset+opidx*n*2; + p1 = aoffset+opidx*n*2; + for(q=0; q<=n-2; q++) + { + a[p0] = buf[p1]; + a[p0+1] = buf[p1+1]; + p0 = p0+2; + p1 = p1+2; + } + + // + // Convolution + // + ftapplysubplan(plan, subplan, a, abase, aoffset+opidx*n*2, buf, 1, _params); + p0 = abase+aoffset+opidx*n*2; + p1 = precoffs; + for(i=0; i<=n-2; i++) + { + ax = a[p0+0]; + ay = a[p0+1]; + bx = plan.precr[p1+0]; + by = plan.precr[p1+1]; + a[p0+0] = ax*bx-ay*by; + a[p0+1] = -(ax*by+ay*bx); + p0 = p0+2; + p1 = p1+2; + } + ftapplysubplan(plan, subplan, a, abase, aoffset+opidx*n*2, buf, 1, _params); + p0 = abase+aoffset+opidx*n*2; + for(i=0; i<=n-2; i++) + { + a[p0+0] = a[p0+0]/(n-1); + a[p0+1] = -(a[p0+1]/(n-1)); + p0 = p0+2; + } + + // + // Result + // + buf[aoffset+opidx*n*2+0] = rx; + buf[aoffset+opidx*n*2+1] = ry; + kiq = 1; + p0 = aoffset+opidx*n*2; + p1 = abase+aoffset+opidx*n*2; + for(q=0; q<=n-2; q++) + { + buf[p0+2*kiq+0] = x0+a[p1+0]; + buf[p0+2*kiq+1] = y0+a[p1+1]; + kiq = kiq*riq%n; + p1 = p1+2; + } + p0 = abase+aoffset+opidx*n*2; + p1 = aoffset+opidx*n*2; + for(q=0; q<=n-1; q++) + { + a[p0] = buf[p1]; + a[p0+1] = buf[p1+1]; + p0 = p0+2; + p1 = p1+2; + } } } - #endif /************************************************************************* - Sets col J of A[,] to V + Factorizes task size N into product of two smaller sizes N1 and N2 INPUT PARAMETERS: - N - vector length - V - value to set - A - array[N,N] or larger - J - col index - + N - task size, N>0 + IsRoot - whether taks is root task (first one in a sequence) + OUTPUT PARAMETERS: - A - leading N elements of I-th col are replaced by V + N1, N2 - such numbers that: + * for prime N: N1=N2=0 + * for composite N<=MaxRadix: N1=N2=0 + * for composite N>MaxRadix: 1<=N1<=N2, N1*N2=N -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 08.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void rsetc(int n, - double v, - double[,] a, - int j, + private static void ftfactorize(int n, + bool isroot, + ref int n1, + ref int n2, alglib.xparams _params) { - int i = 0; + int j = 0; + int k = 0; - for(i=0; i<=n-1; i++) + n1 = 0; + n2 = 0; + + alglib.ap.assert(n>0, "FTFactorize: N<=0"); + n1 = 0; + n2 = 0; + + // + // Small N + // + if( n<=maxradix ) { - a[i,j] = v; + return; + } + + // + // Large N, recursive split + // + if( n>recursivethreshold ) + { + k = (int)Math.Ceiling(Math.Sqrt(n))+1; + alglib.ap.assert(k*k>=n, "FTFactorize: internal error during recursive factorization"); + for(j=k; j>=2; j--) + { + if( n%j==0 ) + { + n1 = Math.Min(n/j, j); + n2 = Math.Max(n/j, j); + return; + } + } + } + + // + // N>MaxRadix, try to find good codelet + // + for(j=maxradix; j>=2; j--) + { + if( n%j==0 ) + { + n1 = j; + n2 = n/j; + break; + } + } + + // + // In case no good codelet was found, + // try to factorize N into product of ANY primes. + // + if( n1*n2!=n ) + { + for(j=2; j<=n-1; j++) + { + if( n%j==0 ) + { + n1 = j; + n2 = n/j; + break; + } + if( j*j>n ) + { + break; + } + } + } + + // + // normalize + // + if( n1>n2 ) + { + j = n1; + n1 = n2; + n2 = j; } } - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Copies vector X[] to Y[] + Returns optimistic estimate of the FFT cost, in UNITs (1 UNIT = 100 KFLOPs) INPUT PARAMETERS: - N - vector length - X - array[N], source - Y - preallocated array[N] - - OUTPUT PARAMETERS: - Y - leading N elements are replaced by X - + N - task size, N>0 - NOTE: destination and source should NOT overlap + RESULU: + cost in UNITs, rounded down to nearest integer + + NOTE: If FFT cost is less than 1 UNIT, it will return 0 as result. -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 08.04.2013 by Bochkanov Sergey *************************************************************************/ - public static void rcopyv(int n, - double[] x, - double[] y, + private static int ftoptimisticestimate(int n, alglib.xparams _params) { - int j = 0; + int result = 0; - for(j=0; j<=n-1; j++) - { - y[j] = x[j]; - } + alglib.ap.assert(n>0, "FTOptimisticEstimate: N<=0"); + result = (int)Math.Floor(1.0E-5*5*n*Math.Log(n)/Math.Log(2)); + return result; } - #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Copies vector X[] to Y[] - - INPUT PARAMETERS: - N - vector length - X - array[N], source - Y - preallocated array[N] - - OUTPUT PARAMETERS: - Y - leading N elements are replaced by X - - - NOTE: destination and source should NOT overlap + Twiddle factors calculation -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 01.05.2009 by Bochkanov Sergey *************************************************************************/ - public static void bcopyv(int n, - bool[] x, - bool[] y, + private static void ffttwcalc(double[] a, + int aoffset, + int n1, + int n2, alglib.xparams _params) { - int j = 0; + int i = 0; + int j2 = 0; + int n = 0; + int halfn1 = 0; + int offs = 0; + double x = 0; + double y = 0; + double twxm1 = 0; + double twy = 0; + double twbasexm1 = 0; + double twbasey = 0; + double twrowxm1 = 0; + double twrowy = 0; + double tmpx = 0; + double tmpy = 0; + double v = 0; + int updatetw2 = 0; - for(j=0; j<=n-1; j++) + + // + // Multiplication by twiddle factors for complex Cooley-Tukey FFT + // with N factorized as N1*N2. + // + // Naive solution to this problem is given below: + // + // > for K:=1 to N2-1 do + // > for J:=1 to N1-1 do + // > begin + // > Idx:=K*N1+J; + // > X:=A[AOffset+2*Idx+0]; + // > Y:=A[AOffset+2*Idx+1]; + // > TwX:=Cos(-2*Pi()*K*J/(N1*N2)); + // > TwY:=Sin(-2*Pi()*K*J/(N1*N2)); + // > A[AOffset+2*Idx+0]:=X*TwX-Y*TwY; + // > A[AOffset+2*Idx+1]:=X*TwY+Y*TwX; + // > end; + // + // However, there are exist more efficient solutions. + // + // Each pass of the inner cycle corresponds to multiplication of one + // entry of A by W[k,j]=exp(-I*2*pi*k*j/N). This factor can be rewritten + // as exp(-I*2*pi*k/N)^j. So we can replace costly exponentiation by + // repeated multiplication: W[k,j+1]=W[k,j]*exp(-I*2*pi*k/N), with + // second factor being computed once in the beginning of the iteration. + // + // Also, exp(-I*2*pi*k/N) can be represented as exp(-I*2*pi/N)^k, i.e. + // we have W[K+1,1]=W[K,1]*W[1,1]. + // + // In our loop we use following variables: + // * [TwBaseXM1,TwBaseY] = [cos(2*pi/N)-1, sin(2*pi/N)] + // * [TwRowXM1, TwRowY] = [cos(2*pi*I/N)-1, sin(2*pi*I/N)] + // * [TwXM1, TwY] = [cos(2*pi*I*J/N)-1, sin(2*pi*I*J/N)] + // + // Meaning of the variables: + // * [TwXM1,TwY] is current twiddle factor W[I,J] + // * [TwRowXM1, TwRowY] is W[I,1] + // * [TwBaseXM1,TwBaseY] is W[1,1] + // + // During inner loop we multiply current twiddle factor by W[I,1], + // during outer loop we update W[I,1]. + // + // + alglib.ap.assert(updatetw>=2, "FFTTwCalc: internal error - UpdateTw<2"); + updatetw2 = updatetw/2; + halfn1 = n1/2; + n = n1*n2; + v = -(2*Math.PI/n); + twbasexm1 = -(2*math.sqr(Math.Sin(0.5*v))); + twbasey = Math.Sin(v); + twrowxm1 = 0; + twrowy = 0; + offs = aoffset; + for(i=0; i<=n2-1; i++) { - y[j] = x[j]; + + // + // Initialize twiddle factor for current row + // + twxm1 = 0; + twy = 0; + + // + // N1-point block is separated into 2-point chunks and residual 1-point chunk + // (in case N1 is odd). Unrolled loop is several times faster. + // + for(j2=0; j2<=halfn1-1; j2++) + { + + // + // Processing: + // * process first element in a chunk. + // * update twiddle factor (unconditional update) + // * process second element + // * conditional update of the twiddle factor + // + x = a[offs+0]; + y = a[offs+1]; + tmpx = x*(1+twxm1)-y*twy; + tmpy = x*twy+y*(1+twxm1); + a[offs+0] = tmpx; + a[offs+1] = tmpy; + tmpx = (1+twxm1)*twrowxm1-twy*twrowy; + twy = twy+(1+twxm1)*twrowy+twy*twrowxm1; + twxm1 = twxm1+tmpx; + x = a[offs+2]; + y = a[offs+3]; + tmpx = x*(1+twxm1)-y*twy; + tmpy = x*twy+y*(1+twxm1); + a[offs+2] = tmpx; + a[offs+3] = tmpy; + offs = offs+4; + if( (j2+1)%updatetw2==0 && j2m ) + { + + // + // New partition: + // + // "A^T -> B" becomes "(A1 A2)^T -> ( B1 ) + // ( B2 ) + // + n1 = n/2; + if( n-n1>=8 && n1%8!=0 ) + { + n1 = n1+(8-n1%8); + } + alglib.ap.assert(n-n1>0); + ffticltrec(a, astart, astride, b, bstart, bstride, m, n1, _params); + ffticltrec(a, astart+2*n1, astride, b, bstart+2*n1*bstride, bstride, m, n-n1, _params); + } + else + { + + // + // New partition: + // + // "A^T -> B" becomes "( A1 )^T -> ( B1 B2 ) + // ( A2 ) + // + m1 = m/2; + if( m-m1>=8 && m1%8!=0 ) + { + m1 = m1+(8-m1%8); } + alglib.ap.assert(m-m1>0); + ffticltrec(a, astart, astride, b, bstart, bstride, m1, n, _params); + ffticltrec(a, astart+2*m1*astride, astride, b, bstart+2*m1, bstride, m-m1, n, _params); } } /************************************************************************* - Copies matrix X[] to Y[], resizing Y[] if needed. On resize, dimensions of - Y[] are increased - but not decreased. - - INPUT PARAMETERS: - M - rows count - N - cols count - X - array[M,N], source - Y - possibly preallocated array[M,N] (resized if needed) - - OUTPUT PARAMETERS: - Y - leading [M,N] elements are replaced by X + recurrent subroutine for FFTFindSmoothRec -- ALGLIB -- - Copyright 20.01.2020 by Bochkanov Sergey + Copyright 01.05.2009 by Bochkanov Sergey *************************************************************************/ - public static void rcopyallocm(int m, - int n, - double[,] x, - ref double[,] y, + private static void ftbasefindsmoothrec(int n, + int seed, + int leastfactor, + ref int best, alglib.xparams _params) { - if( m==0 || n==0 ) + alglib.ap.assert(ftbasemaxsmoothfactor<=5, "FTBaseFindSmoothRec: internal error!"); + if( seed>=n ) { + best = Math.Min(best, seed); return; } - if( alglib.ap.rows(y)=newn ) + double result = 0; + int i = 0; + double v = 0; + + result = 0; + for(i=0; i<=n-1; i++) { - return; + v = x[i]; + result = result+v*v; } - igrowvinternal(newn, ref x, _params); + return result; } + #endif /************************************************************************* - Grows X, i.e. changes its size in such a way that: - a) contents is preserved - b) new size is at least N - c) actual size can be larger than N, so subsequent grow() calls can return - without reallocation - - -- ALGLIB -- - Copyright 20.03.2009 by Bochkanov Sergey - *************************************************************************/ - public static void bgrowv(int newn, - ref bool[] x, - alglib.xparams _params) - { - - // - // If no growth is required, exit. Call worker function otherwise. - // - // The idea is that we call function which works with dynamic arrays - // (and utilizes stack unwinding) only when absolutely necessary. - // - if( alglib.ap.len(x)>=newn ) - { - return; - } - bgrowvinternal(newn, ref x, _params); - } + Computes scaled dot product (S*X,S*X) for elements [0,N) of X[] + INPUT PARAMETERS: + N - vector length + X - array[N], vector to process + S - array[N], vector to process - /************************************************************************* - Grows X, i.e. changes its size in such a way that: - a) contents is preserved - b) new size is at least N - c) actual size can be larger than N, so subsequent grow() calls can return - without reallocation + RESULT: + (X,X) -- ALGLIB -- - Copyright 07.06.2023 by Bochkanov Sergey + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rgrowv(int newn, - ref double[] x, + public static double rdotscl1v2(int n, + double[] x, + double[] s, alglib.xparams _params) { - - // - // If no growth is required, exit. Call worker function otherwise. - // - // The idea is that we call function which works with dynamic arrays - // (and utilizes stack unwinding) only when absolutely necessary. - // - if( alglib.ap.len(x)>=newn ) + double result = 0; + int i = 0; + double v = 0; + + result = 0; + for(i=0; i<=n-1; i++) { - return; + v = x[i]*s[i]; + result = result+v*v; } - rgrowvinternal(newn, ref x, _params); + return result; } /************************************************************************* - Grows X by calling rGrowV() and sets the element X[NewN-1] to the specified - value - - -- ALGLIB -- - Copyright 07.09.2024 by Bochkanov Sergey - *************************************************************************/ - public static void rgrowappendv(int newn, - ref double[] x, - double v, - alglib.xparams _params) - { - rgrowv(newn, ref x, _params); - x[newn-1] = v; - } + Computes scaled inf-norm of X: max(|x[i]/s[i]|) + INPUT PARAMETERS: + N - vector length + X - array[N], vector to process + S - array[N], scales, S[i]<>0 - /************************************************************************* - Grows X by calling iGrowV() and sets the element X[NewN-1] to the specified - value + RESULT: + (X,X) -- ALGLIB -- - Copyright 07.09.2024 by Bochkanov Sergey + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void igrowappendv(int newn, - ref int[] x, - int v, + public static double rsclnrminf(int n, + double[] x, + double[] s, alglib.xparams _params) { - igrowv(newn, ref x, _params); - x[newn-1] = v; - } - - - /************************************************************************* - Grows X by calling bGrowV() and sets the element X[NewN-1] to the specified - value + double result = 0; + int i = 0; + double v = 0; - -- ALGLIB -- - Copyright 07.09.2024 by Bochkanov Sergey - *************************************************************************/ - public static void bgrowappendv(int newn, - ref bool[] x, - bool v, - alglib.xparams _params) - { - bgrowv(newn, ref x, _params); - x[newn-1] = v; + result = 0; + for(i=0; i<=n-1; i++) + { + v = x[i]/s[i]; + result = Math.Max(result, Math.Abs(v)); + } + return result; } /************************************************************************* - Appends several rows to the matrix A, so it has at least M rows, in such a - way that: + Performs addition of packed Y[] to scattered X[] - a) if cols(A)=ColsCnt, the new matrix row count is at least M, and the - former contents is preserved. This function usually increases matrix size - by multiplying it by approximately 2 in order to avoid frequent reallocations. - b) if cols(A)<>ColsCnt (including cols(A)>ColsCnt), then the matrix is - completely reallocated, its new size will be at least MxColsCnt, but - likely to be greater than that - + INPUT PARAMETERS: + N - vector length + Alpha - multiplier + Y - array[N], dense source vector + X - array[?], destination vector, elements with indexes dstIdx[0..N-1] + are modified + dstIdx - array[N], non-negative distinct indexes + + RESULT: + X[Idx[..]] := X[Idx[..]] + alpha*Y[..] -- ALGLIB -- - Copyright 20.03.2009 by Bochkanov Sergey + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rgrowrowsfixedcolsm(int m, - int colscnt, - ref double[,] a, + public static void rscatteraddv(int n, + double alpha, + double[] y, + double[] x, + int[] dstidx, alglib.xparams _params) { - if( alglib.ap.cols(a)!=colscnt ) - { - a = new double[(int)Math.Round(1.25*m+8), colscnt]; - return; - } - if( alglib.ap.rows(a)ColsCnt (including cols(A)>ColsCnt) AND N=0, then the matrix - is completely reallocated, its new size will be at least 1xColsCnt, but - likely to be greater than that - c) if cols(A)<>ColsCnt (including cols(A)>ColsCnt) AND N<>0, an exception - is generated - + INPUT PARAMETERS: + N - vector length + Alpha - multiplier + Y - array[N], vector to process + X - array[N], vector to process + + RESULT: + X := X + alpha*Y + + It is allowed for X and Y to be the same variable. -- ALGLIB -- - Copyright 20.03.2009 by Bochkanov Sergey + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rappendrowfixedcolsm(int n, - int colscnt, - ref double[,] a, + public static void raddv(int n, + double alpha, + double[] y, + double[] x, alglib.xparams _params) { - if( alglib.ap.cols(a)!=colscnt ) - { - if( n!=0 ) - { - alglib.ap.assert(false, "APSERV: integrity check 3225 failed"); - } - a = new double[8, colscnt]; - return; - } - if( alglib.ap.rows(a) op(A) = A - * OpA=1 => op(A) = A^T - X - input vector, has at least N elements - Beta- coefficient - Y - preallocated output array, has at least M elements - - OUTPUT PARAMETERS: - Y - vector which stores result - - HANDLING OF SPECIAL CASES: - * if M=0, then subroutine does nothing. It does not even touch arrays. - * if N=0 or Alpha=0.0, then: - * if Beta=0, then Y is filled by zeros. A and X are not referenced - at all. Initial values of Y are ignored (we do not multiply Y by - zero, we just rewrite it by zeros) - * if Beta<>0, then Y is replaced by Beta*Y - * if M>0, N>0, Alpha<>0, but Beta=0, then Y is replaced by A*x; - initial state of Y is ignored (rewritten by A*x, without initial - multiplication by zeros). + N - vector length + Alpha - multiplier + Y - source vector + OffsY - source offset + X - destination vector + OffsX - destination offset + RESULT: + X := X + alpha*Y - -- ALGLIB routine -- + It is allowed for X to exactly coincide with Y, provided that OffsX=OffsY. - 01.09.2021 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rgemv(int m, - int n, + public static void raddvx(int n, double alpha, - double[,] a, - int opa, - double[] x, - double beta, double[] y, + int offsy, + double[] x, + int offsx, alglib.xparams _params) { int i = 0; - int j = 0; - double v = 0; - - // - // Properly premultiply Y by Beta. - // - // Quick exit for M=0, N=0 or Alpha=0. - // After this block we have M>0, N>0, Alpha<>0. - // - if( m<=0 ) - { - return; - } - if( (double)(beta)!=(double)(0) ) - { - rmulv(m, beta, y, _params); - } - else - { - rsetv(m, 0.0, y, _params); - } - if( n<=0 || (double)(alpha)==(double)(0.0) ) + for(i=0; i<=n-1; i++) { - return; - } - - // - // Generic code - // - if( opa==0 ) - { - - // - // y += A*x - // - for(i=0; i<=m-1; i++) - { - v = 0; - for(j=0; j<=n-1; j++) - { - v = v+a[i,j]*x[j]; - } - y[i] = alpha*v+y[i]; - } - return; - } - if( opa==1 ) - { - - // - // y += A^T*x - // - for(i=0; i<=n-1; i++) - { - v = alpha*x[i]; - for(j=0; j<=m-1; j++) - { - y[j] = y[j]+v*a[i,j]; - } - } - return; + x[offsx+i] = x[offsx+i]+alpha*y[offsy+i]; } } #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Matrix-vector product: y := alpha*op(A)*x + beta*y - - Here x, y, A are subvectors/submatrices of larger vectors/matrices. - - NOTE: this function expects Y to be large enough to store result. No - automatic preallocation happens for smaller arrays. No integrity - checks is performed for sizes of A, x, y. + Performs inplace addition of vector Y[] to column X[] INPUT PARAMETERS: - M - number of rows of op(A) - N - number of columns of op(A) - Alpha- coefficient - A - source matrix - IA - submatrix offset (row index) - JA - submatrix offset (column index) - OpA - operation type: - * OpA=0 => op(A) = A - * OpA=1 => op(A) = A^T - X - input vector, has at least N+IX elements - IX - subvector offset - Beta- coefficient - Y - preallocated output array, has at least M+IY elements - IY - subvector offset - - OUTPUT PARAMETERS: - Y - vector which stores result - - HANDLING OF SPECIAL CASES: - * if M=0, then subroutine does nothing. It does not even touch arrays. - * if N=0 or Alpha=0.0, then: - * if Beta=0, then Y is filled by zeros. A and X are not referenced - at all. Initial values of Y are ignored (we do not multiply Y by - zero, we just rewrite it by zeros) - * if Beta<>0, then Y is replaced by Beta*Y - * if M>0, N>0, Alpha<>0, but Beta=0, then Y is replaced by A*x; - initial state of Y is ignored (rewritten by A*x, without initial - multiplication by zeros). - + N - vector length + Alpha - multiplier + Y - vector to add + X - target column ColIdx - -- ALGLIB routine -- + RESULT: + X := X + alpha*Y - 01.09.2021 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rgemvx(int m, - int n, + public static void raddvc(int n, double alpha, - double[,] a, - int ia, - int ja, - int opa, - double[] x, - int ix, - double beta, double[] y, - int iy, + double[,] x, + int colidx, alglib.xparams _params) { int i = 0; - int j = 0; - double v = 0; - - // - // Properly premultiply Y by Beta. - // - // Quick exit for M=0, N=0 or Alpha=0. - // After this block we have M>0, N>0, Alpha<>0. - // - if( m<=0 ) - { - return; - } - if( (double)(beta)!=(double)(0) ) - { - rmulvx(m, beta, y, iy, _params); - } - else - { - rsetvx(m, 0.0, y, iy, _params); - } - if( n<=0 || (double)(alpha)==(double)(0.0) ) - { - return; - } - - // - // Generic code - // - if( opa==0 ) - { - - // - // y += A*x - // - for(i=0; i<=m-1; i++) - { - v = 0; - for(j=0; j<=n-1; j++) - { - v = v+a[ia+i,ja+j]*x[ix+j]; - } - y[iy+i] = alpha*v+y[iy+i]; - } - return; - } - if( opa==1 ) + for(i=0; i<=n-1; i++) { - - // - // y += A^T*x - // - for(i=0; i<=n-1; i++) - { - v = alpha*x[ix+i]; - for(j=0; j<=m-1; j++) - { - y[iy+j] = y[iy+j]+v*a[ia+i,ja+j]; - } - } - return; + x[i,colidx] = x[i,colidx]+alpha*y[i]; } } - #endif #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Rank-1 correction: A := A + alpha*u*v' - - NOTE: this function expects A to be large enough to store result. No - automatic preallocation happens for smaller arrays. No integrity - checks is performed for sizes of A, u, v. + Performs inplace addition of vector Y[] to row X[] INPUT PARAMETERS: - M - number of rows - N - number of columns - A - target MxN matrix - Alpha- coefficient - U - vector #1 - V - vector #2 + N - vector length + Alpha - multiplier + Y - vector to add + X - target row RowIdx + RESULT: + X := X + alpha*Y - -- ALGLIB routine -- - 07.09.2021 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rger(int m, - int n, + public static void raddvr(int n, double alpha, - double[] u, - double[] v, - double[,] a, + double[] y, + double[,] x, + int rowidx, alglib.xparams _params) { int i = 0; - int j = 0; - double s = 0; - if( (m<=0 || n<=0) || (double)(alpha)==(double)(0) ) - { - return; - } - for(i=0; i<=m-1; i++) + for(i=0; i<=n-1; i++) { - s = alpha*u[i]; - for(j=0; j<=n-1; j++) - { - a[i,j] = a[i,j]+s*v[j]; - } + x[rowidx,i] = x[rowidx,i]+alpha*y[i]; } } #endif @@ -7907,6515 +9021,5028 @@ public static void rger(int m, #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - This subroutine solves linear system op(A)*x=b where: - * A is NxN upper/lower triangular/unitriangular matrix - * X and B are Nx1 vectors - * "op" may be identity transformation or transposition + Performs componentwise multiplication of vector X[] by vector Y[] - Solution replaces X. + INPUT PARAMETERS: + N - vector length + Y - vector to multiply by + X - target vector - IMPORTANT: * no overflow/underflow/denegeracy tests is performed. - * no integrity checks for operand sizes, out-of-bounds accesses - and so on is performed + RESULT: + X := componentwise(X*Y) - INPUT PARAMETERS - N - matrix size, N>=0 - A - matrix, actial matrix is stored in A[IA:IA+N-1,JA:JA+N-1] - IA - submatrix offset - JA - submatrix offset - IsUpper - whether matrix is upper triangular - IsUnit - whether matrix is unitriangular - OpType - transformation type: - * 0 - no transformation - * 1 - transposition - X - right part, actual vector is stored in X[IX:IX+N-1] - IX - offset - - OUTPUT PARAMETERS - X - solution replaces elements X[IX:IX+N-1] + It is allowed for X and Y to be the same variable. - -- ALGLIB routine -- - (c) 07.09.2021 Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rtrsvx(int n, - double[,] a, - int ia, - int ja, - bool isupper, - bool isunit, - int optype, + public static void rmergemulv(int n, + double[] y, double[] x, - int ix, alglib.xparams _params) { int i = 0; - int j = 0; - double v = 0; - if( n<=0 ) + for(i=0; i<=n-1; i++) { - return; - } - if( optype==0 && isupper ) - { - for(i=n-1; i>=0; i--) - { - v = x[ix+i]; - for(j=i+1; j<=n-1; j++) - { - v = v-a[ia+i,ja+j]*x[ix+j]; - } - if( !isunit ) - { - v = v/a[ia+i,ja+i]; - } - x[ix+i] = v; - } - return; - } - if( optype==0 && !isupper ) - { - for(i=0; i<=n-1; i++) - { - v = x[ix+i]; - for(j=0; j<=i-1; j++) - { - v = v-a[ia+i,ja+j]*x[ix+j]; - } - if( !isunit ) - { - v = v/a[ia+i,ja+i]; - } - x[ix+i] = v; - } - return; - } - if( optype==1 && isupper ) - { - for(i=0; i<=n-1; i++) - { - v = x[ix+i]; - if( !isunit ) - { - v = v/a[ia+i,ja+i]; - } - x[ix+i] = v; - if( v==0 ) - { - continue; - } - for(j=i+1; j<=n-1; j++) - { - x[ix+j] = x[ix+j]-v*a[ia+i,ja+j]; - } - } - return; - } - if( optype==1 && !isupper ) - { - for(i=n-1; i>=0; i--) - { - v = x[ix+i]; - if( !isunit ) - { - v = v/a[ia+i,ja+i]; - } - x[ix+i] = v; - if( v==0 ) - { - continue; - } - for(j=0; j<=i-1; j++) - { - x[ix+j] = x[ix+j]-v*a[ia+i,ja+j]; - } - } - return; + x[i] = x[i]*y[i]; } - alglib.ap.assert(false, "rTRSVX: unexpected operation type"); } #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel - - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey - *************************************************************************/ - public static bool rmatrixgerf(int m, - int n, - double[,] a, - int ia, - int ja, - double ralpha, - double[] u, - int iu, - double[] v, - int iv, - alglib.xparams _params) - { - bool result = new bool(); + Performs componentwise multiplication of vector X[] by vector Y[] using offsets - result = false; - return result; - } + INPUT PARAMETERS: + N - vector length + Y - vector to multiply by + OffsY - source offset + X - target vector + OffsX - destination offset + RESULT: + X := componentwise(X*Y) using offsets - /************************************************************************* - Fast kernel + It is allowed for X to exactly coincide with Y, provided that OffsX=OffsY. - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool cmatrixrank1f(int m, - int n, - complex[,] a, - int ia, - int ja, - complex[] u, - int iu, - complex[] v, - int iv, + public static void rmergemulvx(int n, + double[] y, + int offsy, + double[] x, + int offsx, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = false; - return result; + for(i=0; i<=n-1; i++) + { + x[offsx+i] = x[offsx+i]*y[offsy+i]; + } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel + Performs componentwise multiplication of row X[] by vector Y[] - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + INPUT PARAMETERS: + N - vector length + Y - vector to multiply by + X - target row RowIdx + + RESULT: + X := componentwise(X*Y) + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool rmatrixrank1f(int m, - int n, - double[,] a, - int ia, - int ja, - double[] u, - int iu, - double[] v, - int iv, + public static void rmergemulvr(int n, + double[] y, + double[,] x, + int rowidx, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = false; - return result; + for(i=0; i<=n-1; i++) + { + x[rowidx,i] = x[rowidx,i]*y[i]; + } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel + Performs componentwise multiplication of row X[] by vector Y[] - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + INPUT PARAMETERS: + N - vector length + Y - vector to multiply by + X - target row RowIdx + + RESULT: + X := componentwise(X*Y) + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool cmatrixrighttrsmf(int m, - int n, - complex[,] a, - int i1, - int j1, - bool isupper, - bool isunit, - int optype, - complex[,] x, - int i2, - int j2, + public static void rmergemulrv(int n, + double[,] y, + int rowidx, + double[] x, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = false; - return result; + for(i=0; i<=n-1; i++) + { + x[i] = x[i]*y[rowidx,i]; + } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel + Performs componentwise division of vector X[] by vector Y[] - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + INPUT PARAMETERS: + N - vector length + Y - vector to divide by + X - target vector + + RESULT: + X := componentwise(X/Y) + + It is allowed for X and Y to be the same variable. In this case every + processed element must be nonzero. + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool cmatrixlefttrsmf(int m, - int n, - complex[,] a, - int i1, - int j1, - bool isupper, - bool isunit, - int optype, - complex[,] x, - int i2, - int j2, + public static void rmergedivv(int n, + double[] y, + double[] x, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = false; - return result; + for(i=0; i<=n-1; i++) + { + x[i] = x[i]/y[i]; + } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel + Performs componentwise division of row X[] by vector Y[] - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + INPUT PARAMETERS: + N - vector length + Y - vector to divide by + X - target row RowIdx + + RESULT: + X := componentwise(X/Y) + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool rmatrixrighttrsmf(int m, - int n, - double[,] a, - int i1, - int j1, - bool isupper, - bool isunit, - int optype, + public static void rmergedivvr(int n, + double[] y, double[,] x, - int i2, - int j2, + int rowidx, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = false; - return result; + for(i=0; i<=n-1; i++) + { + x[rowidx,i] = x[rowidx,i]/y[i]; + } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel + Performs componentwise division of row X[] by vector Y[] - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + INPUT PARAMETERS: + N - vector length + Y - vector to divide by + X - target row RowIdx + + RESULT: + X := componentwise(X/Y) + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool rmatrixlefttrsmf(int m, - int n, - double[,] a, - int i1, - int j1, - bool isupper, - bool isunit, - int optype, - double[,] x, - int i2, - int j2, + public static void rmergedivrv(int n, + double[,] y, + int rowidx, + double[] x, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = false; - return result; + for(i=0; i<=n-1; i++) + { + x[i] = x[i]/y[rowidx,i]; + } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel + Performs componentwise max of vector X[] and vector Y[] - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + INPUT PARAMETERS: + N - vector length + Y - vector to multiply by + X - target vector + + RESULT: + X := componentwise_max(X,Y) + + It is allowed for X and Y to be the same variable. + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool cmatrixherkf(int n, - int k, - double alpha, - complex[,] a, - int ia, - int ja, - int optypea, - double beta, - complex[,] c, - int ic, - int jc, - bool isupper, + public static void rmergemaxv(int n, + double[] y, + double[] x, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = false; - return result; + for(i=0; i<=n-1; i++) + { + x[i] = Math.Max(x[i], y[i]); + } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel + Performs componentwise max of row X[] and vector Y[] - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + INPUT PARAMETERS: + N - vector length + Y - vector to multiply by + X - target row RowIdx + + RESULT: + X := componentwise_max(X,Y) + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool rmatrixsyrkf(int n, - int k, - double alpha, - double[,] a, - int ia, - int ja, - int optypea, - double beta, - double[,] c, - int ic, - int jc, - bool isupper, + public static void rmergemaxvr(int n, + double[] y, + double[,] x, + int rowidx, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = false; - return result; + for(i=0; i<=n-1; i++) + { + x[rowidx,i] = Math.Max(x[rowidx,i], y[i]); + } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel + Performs componentwise max of row X[I] and vector Y[] - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + INPUT PARAMETERS: + N - vector length + X - matrix, I-th row is source + X - target row RowIdx + + RESULT: + Y := componentwise_max(Y,X) + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool cmatrixgemmf(int m, - int n, - int k, - complex alpha, - complex[,] a, - int ia, - int ja, - int optypea, - complex[,] b, - int ib, - int jb, - int optypeb, - complex beta, - complex[,] c, - int ic, - int jc, + public static void rmergemaxrv(int n, + double[,] x, + int rowidx, + double[] y, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = false; - return result; + for(i=0; i<=n-1; i++) + { + y[i] = Math.Max(y[i], x[rowidx,i]); + } } + #endif /************************************************************************* - CMatrixGEMM kernel, basecase code for CMatrixGEMM. - - This subroutine calculates C = alpha*op1(A)*op2(B) +beta*C where: - * C is MxN general matrix - * op1(A) is MxK matrix - * op2(B) is KxN matrix - * "op" may be identity transformation, transposition, conjugate transposition - - Additional info: - * multiplication result replaces C. If Beta=0, C elements are not used in - calculations (not multiplied by zero - just not referenced) - * if Alpha=0, A is not used (not multiplied by zero - just not referenced) - * if both Beta and Alpha are zero, C is filled by zeros. + Performs componentwise max of vectors X[] and abs(Y[]) - IMPORTANT: + INPUT PARAMETERS: + N - vector length + Y - vector to merge in + X - target vector - This function does NOT preallocate output matrix C, it MUST be preallocated - by caller prior to calling this function. In case C does not have enough - space to store result, exception will be generated. + RESULT: + X := componentwise_max(X,abs(Y)) - INPUT PARAMETERS - M - matrix size, M>0 - N - matrix size, N>0 - K - matrix size, K>0 - Alpha - coefficient - A - matrix - IA - submatrix offset - JA - submatrix offset - OpTypeA - transformation type: - * 0 - no transformation - * 1 - transposition - * 2 - conjugate transposition - B - matrix - IB - submatrix offset - JB - submatrix offset - OpTypeB - transformation type: - * 0 - no transformation - * 1 - transposition - * 2 - conjugate transposition - Beta - coefficient - C - PREALLOCATED output matrix - IC - submatrix offset - JC - submatrix offset + It is allowed for X and Y to be the same variable. - -- ALGLIB routine -- - 27.03.2013 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void cmatrixgemmk(int m, - int n, - int k, - complex alpha, - complex[,] a, - int ia, - int ja, - int optypea, - complex[,] b, - int ib, - int jb, - int optypeb, - complex beta, - complex[,] c, - int ic, - int jc, + public static void rmergemaxabsv(int n, + double[] y, + double[] x, alglib.xparams _params) { int i = 0; - int j = 0; - complex v = 0; - complex v00 = 0; - complex v01 = 0; - complex v10 = 0; - complex v11 = 0; - double v00x = 0; - double v00y = 0; - double v01x = 0; - double v01y = 0; - double v10x = 0; - double v10y = 0; - double v11x = 0; - double v11y = 0; - double a0x = 0; - double a0y = 0; - double a1x = 0; - double a1y = 0; - double b0x = 0; - double b0y = 0; - double b1x = 0; - double b1y = 0; - int idxa0 = 0; - int idxa1 = 0; - int idxb0 = 0; - int idxb1 = 0; - int i0 = 0; - int i1 = 0; - int ik = 0; - int j0 = 0; - int j1 = 0; - int jk = 0; - int t = 0; - int offsa = 0; - int offsb = 0; - int i_ = 0; - int i1_ = 0; - - // - // if matrix size is zero - // - if( m==0 || n==0 ) + for(i=0; i<=n-1; i++) { - return; + x[i] = Math.Max(x[i], Math.Abs(y[i])); } - - // - // Try optimized code - // - if( cmatrixgemmf(m, n, k, alpha, a, ia, ja, optypea, b, ib, jb, optypeb, beta, c, ic, jc, _params) ) + } + + + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Performs componentwise min of vector X[] and vector Y[] + + INPUT PARAMETERS: + N - vector length + Y - vector to multiply by + X - target vector + + RESULT: + X := componentwise_min(X,Y) + + It is allowed for X and Y to be the same variable. + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rmergeminv(int n, + double[] y, + double[] x, + alglib.xparams _params) + { + int i = 0; + + for(i=0; i<=n-1; i++) { - return; + x[i] = Math.Min(x[i], y[i]); } - - // - // if K=0 or Alpha=0, then C=Beta*C - // - if( k==0 || alpha==0 ) + } + #endif + + + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Performs componentwise max of row X[] and vector Y[] + + INPUT PARAMETERS: + N - vector length + Y - vector to multiply by + X - target row RowIdx + + RESULT: + X := componentwise_max(X,Y) + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rmergeminvr(int n, + double[] y, + double[,] x, + int rowidx, + alglib.xparams _params) + { + int i = 0; + + for(i=0; i<=n-1; i++) { - if( beta!=1 ) - { - if( beta!=0 ) - { - for(i=0; i<=m-1; i++) - { - for(j=0; j<=n-1; j++) - { - c[ic+i,jc+j] = beta*c[ic+i,jc+j]; - } - } - } - else - { - for(i=0; i<=m-1; i++) - { - for(j=0; j<=n-1; j++) - { - c[ic+i,jc+j] = 0; - } - } - } - } - return; + x[rowidx,i] = Math.Min(x[rowidx,i], y[i]); } - - // - // This phase is not really necessary, but compiler complains - // about "possibly uninitialized variables" - // - a0x = 0; - a0y = 0; - a1x = 0; - a1y = 0; - b0x = 0; - b0y = 0; - b1x = 0; - b1y = 0; - - // - // General case - // - i = 0; - while( i0 - N - matrix size, N>0 - K - matrix size, K>0 - Alpha - coefficient - A - matrix - IA - submatrix offset - JA - submatrix offset - OpTypeA - transformation type: - * 0 - no transformation - * 1 - transposition - B - matrix - IB - submatrix offset - JB - submatrix offset - OpTypeB - transformation type: - * 0 - no transformation - * 1 - transposition - Beta - coefficient - C - PREALLOCATED output matrix - IC - submatrix offset - JC - submatrix offset - -- ALGLIB routine -- - 27.03.2013 - Bochkanov Sergey + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Performs inplace addition of Y[RIdx,...] to X[RIdxDst] + + INPUT PARAMETERS: + N - vector length + Alpha - multiplier + Y - array[?,N], matrix whose RIdxSrc-th row is added + RIdxSrc - source row index + X - array[?,N], matrix whose RIdxDst-th row is target + RIdxDst - destination row index + + RESULT: + X := X + alpha*Y + + It is allowed for source and destination rows to be the same row. In this + case the row is scaled by 1+Alpha. + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rmatrixgemmk(int m, - int n, - int k, + public static void raddrr(int n, double alpha, - double[,] a, - int ia, - int ja, - int optypea, - double[,] b, - int ib, - int jb, - int optypeb, - double beta, - double[,] c, - int ic, - int jc, + double[,] y, + int ridxsrc, + double[,] x, + int ridxdst, alglib.xparams _params) { int i = 0; - int j = 0; - - // - // if matrix size is zero - // - if( m==0 || n==0 ) - { - return; - } - - // - // Try optimized code - // - if( rgemm32basecase(m, n, k, alpha, a, ia, ja, optypea, b, ib, jb, optypeb, beta, c, ic, jc, _params) ) - { - return; - } - - // - // if K=0 or Alpha=0, then C=Beta*C - // - if( k==0 || (double)(alpha)==(double)(0) ) - { - if( (double)(beta)!=(double)(1) ) - { - if( (double)(beta)!=(double)(0) ) - { - for(i=0; i<=m-1; i++) - { - for(j=0; j<=n-1; j++) - { - c[ic+i,jc+j] = beta*c[ic+i,jc+j]; - } - } - } - else - { - for(i=0; i<=m-1; i++) - { - for(j=0; j<=n-1; j++) - { - c[ic+i,jc+j] = 0; - } - } - } - } - return; - } - - // - // Call specialized code. - // - // NOTE: specialized code was moved to separate function because of strange - // issues with instructions cache on some systems; Having too long - // functions significantly slows down internal loop of the algorithm. - // - if( optypea==0 && optypeb==0 ) + for(i=0; i<=n-1; i++) { - rmatrixgemmk44v00(m, n, k, alpha, a, ia, ja, b, ib, jb, beta, c, ic, jc, _params); + x[ridxdst,i] = x[ridxdst,i]+alpha*y[ridxsrc,i]; } - if( optypea==0 && optypeb!=0 ) + } + #endif + + + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Performs inplace multiplication of X[] by V + + INPUT PARAMETERS: + N - vector length + X - array[N], vector to process + V - multiplier + + OUTPUT PARAMETERS: + X - elements 0...N-1 multiplied by V + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rmulv(int n, + double v, + double[] x, + alglib.xparams _params) + { + int i = 0; + + for(i=0; i<=n-1; i++) { - rmatrixgemmk44v01(m, n, k, alpha, a, ia, ja, b, ib, jb, beta, c, ic, jc, _params); + x[i] = x[i]*v; } - if( optypea!=0 && optypeb==0 ) + } + #endif + + + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Performs inplace multiplication of X[] by V + + INPUT PARAMETERS: + N - row length + X - array[?,N], row to process + V - multiplier + + OUTPUT PARAMETERS: + X - elements 0...N-1 of row RowIdx are multiplied by V + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rmulr(int n, + double v, + double[,] x, + int rowidx, + alglib.xparams _params) + { + int i = 0; + + for(i=0; i<=n-1; i++) { - rmatrixgemmk44v10(m, n, k, alpha, a, ia, ja, b, ib, jb, beta, c, ic, jc, _params); + x[rowidx,i] = x[rowidx,i]*v; } - if( optypea!=0 && optypeb!=0 ) + } + #endif + + + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Performs inplace computation of Sqrt(X) + + INPUT PARAMETERS: + N - vector length + X - array[N], vector to process + + OUTPUT PARAMETERS: + X - elements 0...N-1 replaced by Sqrt(X) + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rsqrtv(int n, + double[] x, + alglib.xparams _params) + { + int i = 0; + + for(i=0; i<=n-1; i++) { - rmatrixgemmk44v11(m, n, k, alpha, a, ia, ja, b, ib, jb, beta, c, ic, jc, _params); + x[i] = Math.Sqrt(x[i]); } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - RMatrixGEMM kernel, basecase code for RMatrixGEMM, specialized for sitation - with OpTypeA=0 and OpTypeB=0. + Performs inplace computation of Sqrt(X[RowIdx,*]) - Additional info: - * this function requires that Alpha<>0 (assertion is thrown otherwise) + INPUT PARAMETERS: + N - vector length + X - array[?,N], matrix to process - INPUT PARAMETERS - M - matrix size, M>0 - N - matrix size, N>0 - K - matrix size, K>0 - Alpha - coefficient - A - matrix - IA - submatrix offset - JA - submatrix offset - B - matrix - IB - submatrix offset - JB - submatrix offset - Beta - coefficient - C - PREALLOCATED output matrix - IC - submatrix offset - JC - submatrix offset + OUTPUT PARAMETERS: + X - elements 0...N-1 replaced by Sqrt(X) - -- ALGLIB routine -- - 27.03.2013 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rmatrixgemmk44v00(int m, - int n, - int k, - double alpha, - double[,] a, - int ia, - int ja, - double[,] b, - int ib, - int jb, - double beta, - double[,] c, - int ic, - int jc, + public static void rsqrtr(int n, + double[,] x, + int rowidx, alglib.xparams _params) { int i = 0; - int j = 0; - double v = 0; - double v00 = 0; - double v01 = 0; - double v02 = 0; - double v03 = 0; - double v10 = 0; - double v11 = 0; - double v12 = 0; - double v13 = 0; - double v20 = 0; - double v21 = 0; - double v22 = 0; - double v23 = 0; - double v30 = 0; - double v31 = 0; - double v32 = 0; - double v33 = 0; - double a0 = 0; - double a1 = 0; - double a2 = 0; - double a3 = 0; - double b0 = 0; - double b1 = 0; - double b2 = 0; - double b3 = 0; - int idxa0 = 0; - int idxa1 = 0; - int idxa2 = 0; - int idxa3 = 0; - int idxb0 = 0; - int idxb1 = 0; - int idxb2 = 0; - int idxb3 = 0; - int i0 = 0; - int i1 = 0; - int ik = 0; - int j0 = 0; - int j1 = 0; - int jk = 0; - int t = 0; - int offsa = 0; - int offsb = 0; - int i_ = 0; - int i1_ = 0; - alglib.ap.assert((double)(alpha)!=(double)(0), "RMatrixGEMMK44V00: internal error (Alpha=0)"); - - // - // if matrix size is zero - // - if( m==0 || n==0 ) - { - return; - } - - // - // A*B - // - i = 0; - while( i0 (assertion is thrown otherwise) + INPUT PARAMETERS: + N - subvector length + X - vector to process + V - multiplier - INPUT PARAMETERS - M - matrix size, M>0 - N - matrix size, N>0 - K - matrix size, K>0 - Alpha - coefficient - A - matrix - IA - submatrix offset - JA - submatrix offset - B - matrix - IB - submatrix offset - JB - submatrix offset - Beta - coefficient - C - PREALLOCATED output matrix - IC - submatrix offset - JC - submatrix offset + OUTPUT PARAMETERS: + X - elements OffsX:OffsX+N-1 multiplied by V - -- ALGLIB routine -- - 27.03.2013 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rmatrixgemmk44v01(int m, - int n, - int k, - double alpha, - double[,] a, - int ia, - int ja, - double[,] b, - int ib, - int jb, - double beta, - double[,] c, - int ic, - int jc, + public static void rmulvx(int n, + double v, + double[] x, + int offsx, alglib.xparams _params) { int i = 0; - int j = 0; - double v = 0; - double v00 = 0; - double v01 = 0; - double v02 = 0; - double v03 = 0; - double v10 = 0; - double v11 = 0; - double v12 = 0; - double v13 = 0; - double v20 = 0; - double v21 = 0; - double v22 = 0; - double v23 = 0; - double v30 = 0; - double v31 = 0; - double v32 = 0; - double v33 = 0; - double a0 = 0; - double a1 = 0; - double a2 = 0; - double a3 = 0; - double b0 = 0; - double b1 = 0; - double b2 = 0; - double b3 = 0; - int idxa0 = 0; - int idxa1 = 0; - int idxa2 = 0; - int idxa3 = 0; - int idxb0 = 0; - int idxb1 = 0; - int idxb2 = 0; - int idxb3 = 0; - int i0 = 0; - int i1 = 0; - int ik = 0; - int j0 = 0; - int j1 = 0; - int jk = 0; - int t = 0; - int offsa = 0; - int offsb = 0; - int i_ = 0; - int i1_ = 0; - alglib.ap.assert((double)(alpha)!=(double)(0), "RMatrixGEMMK44V00: internal error (Alpha=0)"); - - // - // if matrix size is zero - // - if( m==0 || n==0 ) + for(i=0; i<=n-1; i++) { - return; + x[offsx+i] = x[offsx+i]*v; } - - // - // A*B' - // - i = 0; - while( i0 (assertion is thrown otherwise) + Returns maximum X - INPUT PARAMETERS - M - matrix size, M>0 - N - matrix size, N>0 - K - matrix size, K>0 - Alpha - coefficient - A - matrix - IA - submatrix offset - JA - submatrix offset - B - matrix - IB - submatrix offset - JB - submatrix offset - Beta - coefficient - C - PREALLOCATED output matrix - IC - submatrix offset - JC - submatrix offset + INPUT PARAMETERS: + N - vector length + X - array[N], vector to process - -- ALGLIB routine -- - 27.03.2013 - Bochkanov Sergey + OUTPUT PARAMETERS: + max(X[i]) + zero for N=0 + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rmatrixgemmk44v10(int m, - int n, - int k, - double alpha, - double[,] a, - int ia, - int ja, - double[,] b, - int ib, - int jb, - double beta, - double[,] c, - int ic, - int jc, + public static double rmaxv(int n, + double[] x, alglib.xparams _params) { + double result = 0; int i = 0; - int j = 0; double v = 0; - double v00 = 0; - double v01 = 0; - double v02 = 0; - double v03 = 0; - double v10 = 0; - double v11 = 0; - double v12 = 0; - double v13 = 0; - double v20 = 0; - double v21 = 0; - double v22 = 0; - double v23 = 0; - double v30 = 0; - double v31 = 0; - double v32 = 0; - double v33 = 0; - double a0 = 0; - double a1 = 0; - double a2 = 0; - double a3 = 0; - double b0 = 0; - double b1 = 0; - double b2 = 0; - double b3 = 0; - int idxa0 = 0; - int idxa1 = 0; - int idxa2 = 0; - int idxa3 = 0; - int idxb0 = 0; - int idxb1 = 0; - int idxb2 = 0; - int idxb3 = 0; - int i0 = 0; - int i1 = 0; - int ik = 0; - int j0 = 0; - int j1 = 0; - int jk = 0; - int t = 0; - int offsa = 0; - int offsb = 0; - int i_ = 0; - int i1_ = 0; - alglib.ap.assert((double)(alpha)!=(double)(0), "RMatrixGEMMK44V00: internal error (Alpha=0)"); - - // - // if matrix size is zero - // - if( m==0 || n==0 ) + if( n<=0 ) { - return; + result = 0; + return result; } - - // - // A'*B - // - i = 0; - while( iresult ) { - - // - // Choose between specialized 4x4 code and general code - // - if( i+4<=m && j+4<=n ) - { - - // - // Specialized 4x4 code for [I..I+3]x[J..J+3] submatrix of C. - // - // This submatrix is calculated as sum of K rank-1 products, - // with operands cached in local variables in order to speed - // up operations with arrays. - // - idxa0 = ja+i+0; - idxa1 = ja+i+1; - idxa2 = ja+i+2; - idxa3 = ja+i+3; - offsa = ia; - idxb0 = jb+j+0; - idxb1 = jb+j+1; - idxb2 = jb+j+2; - idxb3 = jb+j+3; - offsb = ib; - v00 = 0.0; - v01 = 0.0; - v02 = 0.0; - v03 = 0.0; - v10 = 0.0; - v11 = 0.0; - v12 = 0.0; - v13 = 0.0; - v20 = 0.0; - v21 = 0.0; - v22 = 0.0; - v23 = 0.0; - v30 = 0.0; - v31 = 0.0; - v32 = 0.0; - v33 = 0.0; - for(t=0; t<=k-1; t++) - { - a0 = a[offsa,idxa0]; - a1 = a[offsa,idxa1]; - b0 = b[offsb,idxb0]; - b1 = b[offsb,idxb1]; - v00 = v00+a0*b0; - v01 = v01+a0*b1; - v10 = v10+a1*b0; - v11 = v11+a1*b1; - a2 = a[offsa,idxa2]; - a3 = a[offsa,idxa3]; - v20 = v20+a2*b0; - v21 = v21+a2*b1; - v30 = v30+a3*b0; - v31 = v31+a3*b1; - b2 = b[offsb,idxb2]; - b3 = b[offsb,idxb3]; - v22 = v22+a2*b2; - v23 = v23+a2*b3; - v32 = v32+a3*b2; - v33 = v33+a3*b3; - v02 = v02+a0*b2; - v03 = v03+a0*b3; - v12 = v12+a1*b2; - v13 = v13+a1*b3; - offsa = offsa+1; - offsb = offsb+1; - } - if( (double)(beta)==(double)(0) ) - { - c[ic+i+0,jc+j+0] = alpha*v00; - c[ic+i+0,jc+j+1] = alpha*v01; - c[ic+i+0,jc+j+2] = alpha*v02; - c[ic+i+0,jc+j+3] = alpha*v03; - c[ic+i+1,jc+j+0] = alpha*v10; - c[ic+i+1,jc+j+1] = alpha*v11; - c[ic+i+1,jc+j+2] = alpha*v12; - c[ic+i+1,jc+j+3] = alpha*v13; - c[ic+i+2,jc+j+0] = alpha*v20; - c[ic+i+2,jc+j+1] = alpha*v21; - c[ic+i+2,jc+j+2] = alpha*v22; - c[ic+i+2,jc+j+3] = alpha*v23; - c[ic+i+3,jc+j+0] = alpha*v30; - c[ic+i+3,jc+j+1] = alpha*v31; - c[ic+i+3,jc+j+2] = alpha*v32; - c[ic+i+3,jc+j+3] = alpha*v33; - } - else - { - c[ic+i+0,jc+j+0] = beta*c[ic+i+0,jc+j+0]+alpha*v00; - c[ic+i+0,jc+j+1] = beta*c[ic+i+0,jc+j+1]+alpha*v01; - c[ic+i+0,jc+j+2] = beta*c[ic+i+0,jc+j+2]+alpha*v02; - c[ic+i+0,jc+j+3] = beta*c[ic+i+0,jc+j+3]+alpha*v03; - c[ic+i+1,jc+j+0] = beta*c[ic+i+1,jc+j+0]+alpha*v10; - c[ic+i+1,jc+j+1] = beta*c[ic+i+1,jc+j+1]+alpha*v11; - c[ic+i+1,jc+j+2] = beta*c[ic+i+1,jc+j+2]+alpha*v12; - c[ic+i+1,jc+j+3] = beta*c[ic+i+1,jc+j+3]+alpha*v13; - c[ic+i+2,jc+j+0] = beta*c[ic+i+2,jc+j+0]+alpha*v20; - c[ic+i+2,jc+j+1] = beta*c[ic+i+2,jc+j+1]+alpha*v21; - c[ic+i+2,jc+j+2] = beta*c[ic+i+2,jc+j+2]+alpha*v22; - c[ic+i+2,jc+j+3] = beta*c[ic+i+2,jc+j+3]+alpha*v23; - c[ic+i+3,jc+j+0] = beta*c[ic+i+3,jc+j+0]+alpha*v30; - c[ic+i+3,jc+j+1] = beta*c[ic+i+3,jc+j+1]+alpha*v31; - c[ic+i+3,jc+j+2] = beta*c[ic+i+3,jc+j+2]+alpha*v32; - c[ic+i+3,jc+j+3] = beta*c[ic+i+3,jc+j+3]+alpha*v33; - } - } - else - { - - // - // Determine submatrix [I0..I1]x[J0..J1] to process - // - i0 = i; - i1 = Math.Min(i+3, m-1); - j0 = j; - j1 = Math.Min(j+3, n-1); - - // - // Process submatrix - // - for(ik=i0; ik<=i1; ik++) - { - for(jk=j0; jk<=j1; jk++) - { - if( k==0 || (double)(alpha)==(double)(0) ) - { - v = 0; - } - else - { - v = 0.0; - i1_ = (ib)-(ia); - v = 0.0; - for(i_=ia; i_<=ia+k-1;i_++) - { - v += a[i_,ja+ik]*b[i_+i1_,jb+jk]; - } - } - if( (double)(beta)==(double)(0) ) - { - c[ic+ik,jc+jk] = alpha*v; - } - else - { - c[ic+ik,jc+jk] = beta*c[ic+ik,jc+jk]+alpha*v; - } - } - } - } - j = j+4; + result = v; } - i = i+4; } + return result; } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - RMatrixGEMM kernel, basecase code for RMatrixGEMM, specialized for sitation - with OpTypeA=1 and OpTypeB=1. + Returns maximum |X| - Additional info: - * this function requires that Alpha<>0 (assertion is thrown otherwise) + INPUT PARAMETERS: + N - vector length + X - array[N], vector to process - INPUT PARAMETERS - M - matrix size, M>0 - N - matrix size, N>0 - K - matrix size, K>0 - Alpha - coefficient - A - matrix - IA - submatrix offset - JA - submatrix offset - B - matrix - IB - submatrix offset - JB - submatrix offset - Beta - coefficient - C - PREALLOCATED output matrix - IC - submatrix offset - JC - submatrix offset + OUTPUT PARAMETERS: + max(|X[i]|) + zero for N=0 - -- ALGLIB routine -- - 27.03.2013 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void rmatrixgemmk44v11(int m, - int n, - int k, - double alpha, - double[,] a, - int ia, - int ja, - double[,] b, - int ib, - int jb, - double beta, - double[,] c, - int ic, - int jc, + public static double rmaxabsv(int n, + double[] x, alglib.xparams _params) { + double result = 0; int i = 0; - int j = 0; double v = 0; - double v00 = 0; - double v01 = 0; - double v02 = 0; - double v03 = 0; - double v10 = 0; - double v11 = 0; - double v12 = 0; - double v13 = 0; - double v20 = 0; - double v21 = 0; - double v22 = 0; - double v23 = 0; - double v30 = 0; - double v31 = 0; - double v32 = 0; - double v33 = 0; - double a0 = 0; - double a1 = 0; - double a2 = 0; - double a3 = 0; - double b0 = 0; - double b1 = 0; - double b2 = 0; - double b3 = 0; - int idxa0 = 0; - int idxa1 = 0; - int idxa2 = 0; - int idxa3 = 0; - int idxb0 = 0; - int idxb1 = 0; - int idxb2 = 0; - int idxb3 = 0; - int i0 = 0; - int i1 = 0; - int ik = 0; - int j0 = 0; - int j1 = 0; - int jk = 0; - int t = 0; - int offsa = 0; - int offsb = 0; - int i_ = 0; - int i1_ = 0; - alglib.ap.assert((double)(alpha)!=(double)(0), "RMatrixGEMMK44V00: internal error (Alpha=0)"); + result = 0; + for(i=0; i<=n-1; i++) + { + v = Math.Abs(x[i]); + if( v>result ) + { + result = v; + } + } + return result; + } + #endif + + + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Returns maximum X + + INPUT PARAMETERS: + N - vector length + X - matrix to process, RowIdx-th row is processed + + OUTPUT PARAMETERS: + max(X[RowIdx,i]) + zero for N=0 + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static double rmaxr(int n, + double[,] x, + int rowidx, + alglib.xparams _params) + { + double result = 0; + int i = 0; + double v = 0; + + if( n<=0 ) + { + result = 0; + return result; + } + result = x[rowidx,0]; + for(i=1; i<=n-1; i++) + { + v = x[rowidx,i]; + if( v>result ) + { + result = v; + } + } + return result; + } + #endif + + + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Returns maximum |X| + + INPUT PARAMETERS: + N - vector length + X - matrix to process, RowIdx-th row is processed + + OUTPUT PARAMETERS: + max(|X[RowIdx,i]|) + zero for N=0 + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static double rmaxabsr(int n, + double[,] x, + int rowidx, + alglib.xparams _params) + { + double result = 0; + int i = 0; + double v = 0; + + result = 0; + for(i=0; i<=n-1; i++) + { + v = Math.Abs(x[rowidx,i]); + if( v>result ) + { + result = v; + } + } + return result; + } + #endif + + + /************************************************************************* + Computes V1 per-variable scales + + INPUT PARAMETERS: + N - vector length + V - scaling coefficient, >=0 + X - variable values + Y - target vector + + RESULT: + Y := max(1,V*|X|) - // - // if matrix size is zero - // - if( m==0 || n==0 ) + It is allowed for X and Y to be the same variable. + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rv1scale(int n, + double v, + double[] x, + double[] y, + alglib.xparams _params) + { + int i = 0; + + for(i=0; i<=n-1; i++) { - return; + y[i] = Math.Max(1, v*Math.Abs(x[i])); } + } + + + /************************************************************************* + Computes inverse squared V1 per-variable scales, multiplied by scaling + coefficient + + INPUT PARAMETERS: + N - vector length + V - scaling coefficient, >=0 + X - variable values + Y - target vector + + RESULT: + Y := F/Sqr(max(1,V*|X|)) - // - // A'*B' - // - i = 0; - while( i=newn ) + for(j=0; j<=n-1; j++) { - return; + x[j] = v; } - oldn = alglib.ap.len(x); - newn = Math.Max(newn, (int)Math.Round(1.8*oldn+1)); - alglib.ap.swap(ref x, ref oldx); - x = new int[newn]; - icopyv(oldn, oldx, x, _params); } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Internal function that actually works with dynamic arrays. + Sets X[OffsX:OffsX+N-1] to V + + INPUT PARAMETERS: + N - subvector length + V - value to set + X - array[N] + + OUTPUT PARAMETERS: + X - X[OffsX:OffsX+N-1] is replaced by V -- ALGLIB -- - Copyright 07.06.2023 by Bochkanov Sergey + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - private static void bgrowvinternal(int newn, - ref bool[] x, + public static void rsetvx(int n, + double v, + double[] x, + int offsx, alglib.xparams _params) { - bool[] oldx = new bool[0]; - int oldn = 0; + int j = 0; - if( alglib.ap.len(x)>=newn ) + for(j=0; j<=n-1; j++) { - return; + x[offsx+j] = v; } - oldn = alglib.ap.len(x); - newn = Math.Max(newn, (int)Math.Round(1.8*oldn+1)); - alglib.ap.swap(ref x, ref oldx); - x = new bool[newn]; - bcopyv(oldn, oldx, x, _params); } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Internal function which actually works with dynamic arrays + Sets vector X[] to V + + INPUT PARAMETERS: + N - vector length + V - value to set + X - array[N] + + OUTPUT PARAMETERS: + X - leading N elements are replaced by V -- ALGLIB -- - Copyright 07.06.2023 by Bochkanov Sergey + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - private static void rgrowvinternal(int newn, - ref double[] x, + public static void isetv(int n, + int v, + int[] x, alglib.xparams _params) { - double[] oldx = new double[0]; - int oldn = 0; + int j = 0; - if( alglib.ap.len(x)>=newn ) + for(j=0; j<=n-1; j++) { - return; + x[j] = v; } - oldn = alglib.ap.len(x); - newn = Math.Max(newn, (int)Math.Round(1.8*oldn+1)); - alglib.ap.swap(ref x, ref oldx); - x = new double[newn]; - rcopyv(oldn, oldx, x, _params); } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Internal function which actually works with dynamic arrays. We need it to - be a separate function in order to minimize penalty associated with maintaining - a local dynamically allocated variable. + Sets vector X[] to V + + INPUT PARAMETERS: + N - vector length + V - value to set + X - array[N] + + OUTPUT PARAMETERS: + X - leading N elements are replaced by V -- ALGLIB -- - Copyright 20.03.2009 by Bochkanov Sergey + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - private static void rincreaserowsfixedcolsminternal(int newrows, - ref double[,] a, + public static void bsetv(int n, + bool v, + bool[] x, alglib.xparams _params) { - double[,] olda = new double[0,0]; + int j = 0; - alglib.ap.swap(ref a, ref olda); - a = new double[(int)Math.Round(Math.Max(1.8*alglib.ap.rows(olda)+8, 1.25*newrows)), alglib.ap.cols(olda)]; - rcopym(alglib.ap.rows(olda), alglib.ap.cols(olda), olda, a, _params); + for(j=0; j<=n-1; j++) + { + x[j] = v; + } } + #endif - #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Fast kernel (new version with AVX2/SSE2) + Sets vector X[] to V - -- ALGLIB routine -- - 19.01.2010 - Bochkanov Sergey + INPUT PARAMETERS: + N - vector length + V - value to set + X - array[N] + + OUTPUT PARAMETERS: + X - leading N elements are replaced by V + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - private static bool rgemm32basecase(int m, - int n, - int k, - double alpha, - double[,] a, - int ia, - int ja, - int optypea, - double[,] b, - int ib, - int jb, - int optypeb, - double beta, - double[,] c, - int ic, - int jc, + public static void csetv(int n, + complex v, + complex[] x, alglib.xparams _params) { - bool result = new bool(); + int j = 0; - result = false; - return result; + for(j=0; j<=n-1; j++) + { + x[j].x = v.x; + x[j].y = v.y; + } } - #endif - } - public class hblas - { - public static void hermitianmatrixvectormultiply(complex[,] a, - bool isupper, - int i1, - int i2, - complex[] x, - complex alpha, - ref complex[] y, + /************************************************************************* + Sets matrix A[] to V + + INPUT PARAMETERS: + M, N - rows/cols count + V - value to set + A - array[M,N] + + OUTPUT PARAMETERS: + A - leading M rows, N cols are replaced by V + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void isetm(int m, + int n, + int v, + int[,] a, alglib.xparams _params) { int i = 0; - int ba1 = 0; - int by1 = 0; - int by2 = 0; - int bx1 = 0; - int bx2 = 0; - int n = 0; - complex v = 0; - int i_ = 0; - int i1_ = 0; + int j = 0; - n = i2-i1+1; - if( n<=0 ) - { - return; - } - - // - // Let A = L + D + U, where - // L is strictly lower triangular (main diagonal is zero) - // D is diagonal - // U is strictly upper triangular (main diagonal is zero) - // - // A*x = L*x + D*x + U*x - // - // Calculate D*x first - // - for(i=i1; i<=i2; i++) - { - y[i-i1+1] = a[i,i]*x[i-i1+1]; - } - - // - // Add L*x + U*x - // - if( isupper ) - { - for(i=i1; i<=i2-1; i++) - { - - // - // Add L*x to the result - // - v = x[i-i1+1]; - by1 = i-i1+2; - by2 = n; - ba1 = i+1; - i1_ = (ba1) - (by1); - for(i_=by1; i_<=by2;i_++) - { - y[i_] = y[i_] + v*math.conj(a[i,i_+i1_]); - } - - // - // Add U*x to the result - // - bx1 = i-i1+2; - bx2 = n; - ba1 = i+1; - i1_ = (ba1)-(bx1); - v = 0.0; - for(i_=bx1; i_<=bx2;i_++) - { - v += x[i_]*a[i,i_+i1_]; - } - y[i-i1+1] = y[i-i1+1]+v; - } - } - else + for(i=0; i<=m-1; i++) { - for(i=i1+1; i<=i2; i++) + for(j=0; j<=n-1; j++) { - - // - // Add L*x to the result - // - bx1 = 1; - bx2 = i-i1; - ba1 = i1; - i1_ = (ba1)-(bx1); - v = 0.0; - for(i_=bx1; i_<=bx2;i_++) - { - v += x[i_]*a[i,i_+i1_]; - } - y[i-i1+1] = y[i-i1+1]+v; - - // - // Add U*x to the result - // - v = x[i-i1+1]; - by1 = 1; - by2 = i-i1; - ba1 = i1; - i1_ = (ba1) - (by1); - for(i_=by1; i_<=by2;i_++) - { - y[i_] = y[i_] + v*math.conj(a[i,i_+i1_]); - } + a[i,j] = v; } } - for(i_=1; i_<=n;i_++) - { - y[i_] = alpha*y[i_]; - } } - public static void hermitianrank2update(complex[,] a, - bool isupper, - int i1, - int i2, - complex[] x, - complex[] y, - ref complex[] t, - complex alpha, + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Sets matrix A[] to V + + INPUT PARAMETERS: + M, N - rows/cols count + V - value to set + A - array[M,N] + + OUTPUT PARAMETERS: + A - leading M rows, N cols are replaced by V + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rsetm(int m, + int n, + double v, + double[,] a, alglib.xparams _params) { int i = 0; - int tp1 = 0; - int tp2 = 0; - complex v = 0; - int i_ = 0; - int i1_ = 0; + int j = 0; - if( isupper ) + for(i=0; i<=m-1; i++) { - for(i=i1; i<=i2; i++) + for(j=0; j<=n-1; j++) { - tp1 = i+1-i1; - tp2 = i2-i1+1; - v = alpha*x[i+1-i1]; - for(i_=tp1; i_<=tp2;i_++) - { - t[i_] = v*math.conj(y[i_]); - } - v = math.conj(alpha)*y[i+1-i1]; - for(i_=tp1; i_<=tp2;i_++) - { - t[i_] = t[i_] + v*math.conj(x[i_]); - } - i1_ = (tp1) - (i); - for(i_=i; i_<=i2;i_++) - { - a[i,i_] = a[i,i_] + t[i_+i1_]; - } - } - } - else - { - for(i=i1; i<=i2; i++) - { - tp1 = 1; - tp2 = i+1-i1; - v = alpha*x[i+1-i1]; - for(i_=tp1; i_<=tp2;i_++) - { - t[i_] = v*math.conj(y[i_]); - } - v = math.conj(alpha)*y[i+1-i1]; - for(i_=tp1; i_<=tp2;i_++) - { - t[i_] = t[i_] + v*math.conj(x[i_]); - } - i1_ = (tp1) - (i1); - for(i_=i1; i_<=i;i_++) - { - a[i,i_] = a[i,i_] + t[i_+i1_]; - } + a[i,j] = v; } } } + #endif - } - public class creflections - { /************************************************************************* - Generation of an elementary complex reflection transformation - - The subroutine generates elementary complex reflection H of order N, so - that, for a given X, the following equality holds true: + Sets vector X[] to V, reallocating X[] if too small - ( X(1) ) ( Beta ) - H' * ( .. ) = ( 0 ), H'*H = I, Beta is a real number - ( X(n) ) ( 0 ) + INPUT PARAMETERS: + N - vector length + V - value to set + X - possibly preallocated array - where + OUTPUT PARAMETERS: + X - leading N elements are replaced by V; array is reallocated + if its length is less than N. - ( V(1) ) - H = 1 - Tau * ( .. ) * ( conj(V(1)), ..., conj(V(n)) ) - ( V(n) ) + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rsetallocv(int n, + double v, + ref double[] x, + alglib.xparams _params) + { + if( alglib.ap.len(x)1 ) + if( alglib.ap.rows(a)N2 or M1>M2, C is not modified. + OUTPUT PARAMETERS: + A - leading M rows, N cols are replaced by V; the matrix is + reallocated if its rows/cols count is less than M/N. - -- LAPACK auxiliary routine (version 3.0) -- - Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., - Courant Institute, Argonne National Lab, and Rice University - September 30, 1994 + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static void complexapplyreflectionfromtheleft(complex[,] c, - complex tau, - complex[] v, - int m1, - int m2, - int n1, - int n2, - ref complex[] work, + public static void rsetallocm(int m, + int n, + double v, + ref double[,] a, alglib.xparams _params) { - complex t = 0; - int i = 0; - int i_ = 0; - - if( (tau==0 || n1>n2) || m1>m2 ) - { - return; - } - - // - // w := C^T * conj(v) - // - for(i=n1; i<=n2; i++) + if( alglib.ap.rows(a)=N + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rallocv(int n, + ref double[] x, + alglib.xparams _params) + { + if( alglib.ap.len(x)N2 or M1>M2, C is not modified. + OUTPUT PARAMETERS: + X - length(X)>=N - -- LAPACK auxiliary routine (version 3.0) -- - Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., - Courant Institute, Argonne National Lab, and Rice University - September 30, 1994 + -- ALGLIB -- + Copyright 20.07.2022 by Bochkanov Sergey *************************************************************************/ - public static void complexapplyreflectionfromtheright(complex[,] c, - complex tau, - complex[] v, - int m1, - int m2, - int n1, - int n2, - ref complex[] work, + public static void callocv(int n, + ref complex[] x, alglib.xparams _params) { - complex t = 0; - int i = 0; - int vm = 0; - int i_ = 0; - int i1_ = 0; - - if( (tau==0 || n1>n2) || m1>m2 ) + if( alglib.ap.len(x)=N + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void iallocv(int n, + ref int[] x, + alglib.xparams _params) + { + if( alglib.ap.len(x)=N + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void ballocv(int n, + ref bool[] x, + alglib.xparams _params) + { + if( alglib.ap.len(x)=newn ) + { + return; + } + igrowvinternal(newn, ref x, _params); } /************************************************************************* - PBL-based kernel. + Grows X, i.e. changes its size in such a way that: + a) contents is preserved + b) new size is at least N + c) actual size can be larger than N, so subsequent grow() calls can return + without reallocation - -- ALGLIB routine -- - 20.10.2014 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ - public static bool rmatrixplupbl(double[,] a, - int offs, - int m, - int n, - ref int[] pivots, + public static void bgrowv(int newn, + ref bool[] x, alglib.xparams _params) { - bool result = new bool(); - - result = false; - return result; + + // + // If no growth is required, exit. Call worker function otherwise. + // + // The idea is that we call function which works with dynamic arrays + // (and utilizes stack unwinding) only when absolutely necessary. + // + if( alglib.ap.len(x)>=newn ) + { + return; + } + bgrowvinternal(newn, ref x, _params); } /************************************************************************* - PBL-based kernel. - - NOTE: this function needs preallocated output/temporary arrays. - D and E must be at least max(M,N)-wide. + Grows X, i.e. changes its size in such a way that: + a) contents is preserved + b) new size is at least N + c) actual size can be larger than N, so subsequent grow() calls can return + without reallocation - -- ALGLIB routine -- - 20.10.2014 - Bochkanov Sergey + -- ALGLIB -- + Copyright 07.06.2023 by Bochkanov Sergey *************************************************************************/ - public static bool rmatrixbdpbl(double[,] a, - int m, - int n, - double[] d, - double[] e, - double[] tauq, - double[] taup, + public static void rgrowv(int newn, + ref double[] x, alglib.xparams _params) { - bool result = new bool(); - - result = false; - return result; + + // + // If no growth is required, exit. Call worker function otherwise. + // + // The idea is that we call function which works with dynamic arrays + // (and utilizes stack unwinding) only when absolutely necessary. + // + if( alglib.ap.len(x)>=newn ) + { + return; + } + rgrowvinternal(newn, ref x, _params); } /************************************************************************* - PBL-based kernel. - - If ByQ is True, TauP is not used (can be empty array). - If ByQ is False, TauQ is not used (can be empty array). + Grows X by calling rGrowV() and sets the element X[NewN-1] to the specified + value - -- ALGLIB routine -- - 20.10.2014 - Bochkanov Sergey + -- ALGLIB -- + Copyright 07.09.2024 by Bochkanov Sergey *************************************************************************/ - public static bool rmatrixbdmultiplybypbl(double[,] qp, - int m, - int n, - double[] tauq, - double[] taup, - double[,] z, - int zrows, - int zcolumns, - bool byq, - bool fromtheright, - bool dotranspose, + public static void rgrowappendv(int newn, + ref double[] x, + double v, alglib.xparams _params) { - bool result = new bool(); - - result = false; - return result; + rgrowv(newn, ref x, _params); + x[newn-1] = v; } /************************************************************************* - PBL-based kernel. - - NOTE: Tau must be preallocated array with at least N-1 elements. + Grows X by calling iGrowV() and sets the element X[NewN-1] to the specified + value - -- ALGLIB routine -- - 20.10.2014 - Bochkanov Sergey + -- ALGLIB -- + Copyright 07.09.2024 by Bochkanov Sergey *************************************************************************/ - public static bool rmatrixhessenbergpbl(double[,] a, - int n, - double[] tau, + public static void igrowappendv(int newn, + ref int[] x, + int v, alglib.xparams _params) { - bool result = new bool(); - - result = false; - return result; + igrowv(newn, ref x, _params); + x[newn-1] = v; } /************************************************************************* - PBL-based kernel. - - NOTE: Q must be preallocated N*N array + Grows X by calling bGrowV() and sets the element X[NewN-1] to the specified + value - -- ALGLIB routine -- - 20.10.2014 - Bochkanov Sergey + -- ALGLIB -- + Copyright 07.09.2024 by Bochkanov Sergey *************************************************************************/ - public static bool rmatrixhessenbergunpackqpbl(double[,] a, - int n, - double[] tau, - double[,] q, + public static void bgrowappendv(int newn, + ref bool[] x, + bool v, alglib.xparams _params) { - bool result = new bool(); - - result = false; - return result; + bgrowv(newn, ref x, _params); + x[newn-1] = v; } /************************************************************************* - PBL-based kernel. + Appends several rows to the matrix A, so it has at least M rows, in such a + way that: - NOTE: Tau, D, E must be preallocated arrays; - length(E)=length(Tau)=N-1 (or larger) - length(D)=N (or larger) + a) if cols(A)=ColsCnt, the new matrix row count is at least M, and the + former contents is preserved. This function usually increases matrix size + by multiplying it by approximately 2 in order to avoid frequent reallocations. + b) if cols(A)<>ColsCnt (including cols(A)>ColsCnt), then the matrix is + completely reallocated, its new size will be at least MxColsCnt, but + likely to be greater than that + - -- ALGLIB routine -- - 20.10.2014 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ - public static bool smatrixtdpbl(double[,] a, - int n, - bool isupper, - double[] tau, - double[] d, - double[] e, + public static void rgrowrowsfixedcolsm(int m, + int colscnt, + ref double[,] a, alglib.xparams _params) { - bool result = new bool(); - - result = false; - return result; + if( alglib.ap.cols(a)!=colscnt ) + { + a = new double[(int)Math.Round(1.25*m+8), colscnt]; + return; + } + if( alglib.ap.rows(a)ColsCnt (including cols(A)>ColsCnt) AND N=0, then the matrix + is completely reallocated, its new size will be at least 1xColsCnt, but + likely to be greater than that + c) if cols(A)<>ColsCnt (including cols(A)>ColsCnt) AND N<>0, an exception + is generated + - -- ALGLIB routine -- - 20.10.2014 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.03.2009 by Bochkanov Sergey *************************************************************************/ - public static bool smatrixtdunpackqpbl(double[,] a, - int n, - bool isupper, - double[] tau, - double[,] q, + public static void rappendrowfixedcolsm(int n, + int colscnt, + ref double[,] a, alglib.xparams _params) { - bool result = new bool(); - - result = false; - return result; + if( alglib.ap.cols(a)!=colscnt ) + { + if( n!=0 ) + { + alglib.ap.assert(false, "APSERV: integrity check 3225 failed"); + } + a = new double[8, colscnt]; + return; + } + if( alglib.ap.rows(a)0; ignored for ZNeeded=0. + INPUT PARAMETERS: + N - vector length + X - array[N], source + A - preallocated 2D array large enough to store result + I - destination row index - EVDResult is modified if and only if PBL is present. + OUTPUT PARAMETERS: + A - leading N elements of I-th row are replaced by X - -- ALGLIB routine -- - 20.10.2014 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool smatrixtdevdpbl(double[] d, - double[] e, - int n, - int zneeded, - double[,] z, - ref bool evdresult, + public static void rcopyvr(int n, + double[] x, + double[,] a, + int i, alglib.xparams _params) { - bool result = new bool(); + int j = 0; - result = false; - return result; + for(j=0; j<=n-1; j++) + { + a[i,j] = x[j]; + } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - PBL-based kernel. - - Returns True if PBL was present and handled request (PBL completion code - is returned as separate output parameter). - - D and E are pre-allocated arrays with length N (both of them!). On output, - D constraints eigenvalues, and E is destroyed. + Copies row I of A[,] to vector X[] - Z is preallocated array[N,N] for ZNeeded<>0; ignored for ZNeeded=0. + INPUT PARAMETERS: + N - vector length + A - 2D array, source + I - source row index + X - preallocated destination - EVDResult is modified if and only if PBL is present. + OUTPUT PARAMETERS: + X - array[N], destination - -- ALGLIB routine -- - 20.10.2014 - Bochkanov Sergey + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey *************************************************************************/ - public static bool sparsegemvcrspbl(int opa, - int arows, - int acols, - double alpha, - double[] vals, - int[] cidx, - int[] ridx, + public static void rcopyrv(int n, + double[,] a, + int i, double[] x, - int ix, - double beta, - double[] y, - int iy, alglib.xparams _params) { - bool result = new bool(); - - result = false; - return result; - } - - - } - public class scodes - { - public static int getrdfserializationcode(alglib.xparams _params) - { - int result = 0; + int j = 0; - result = 1; - return result; + for(j=0; j<=n-1; j++) + { + x[j] = a[i,j]; + } } + #endif - public static int getkdtreeserializationcode(alglib.xparams _params) - { - int result = 0; - - result = 2; - return result; - } - + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Copies row I of A[,] to row K of B[,]. - public static int getmlpserializationcode(alglib.xparams _params) - { - int result = 0; + A[i,...] and B[k,...] may overlap. - result = 3; - return result; - } + INPUT PARAMETERS: + N - vector length + A - 2D array, source + I - source row index + B - preallocated destination + K - destination row index + OUTPUT PARAMETERS: + B - row K overwritten - public static int getmlpeserializationcode(alglib.xparams _params) + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rcopyrr(int n, + double[,] a, + int i, + double[,] b, + int k, + alglib.xparams _params) { - int result = 0; + int j = 0; - result = 4; - return result; + for(j=0; j<=n-1; j++) + { + b[k,j] = a[i,j]; + } } + #endif - public static int getrbfserializationcode(alglib.xparams _params) - { - int result = 0; + /************************************************************************* + Copies vector X[] to column J of A[,] - result = 5; - return result; - } + INPUT PARAMETERS: + N - vector length + X - array[N], source + A - preallocated 2D array large enough to store result + J - destination col index + OUTPUT PARAMETERS: + A - leading N elements of J-th column are replaced by X - public static int getspline2dserializationcode(alglib.xparams _params) + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rcopyvc(int n, + double[] x, + double[,] a, + int j, + alglib.xparams _params) { - int result = 0; + int i = 0; - result = 6; - return result; + for(i=0; i<=n-1; i++) + { + a[i,j] = x[i]; + } } - public static int getidwserializationcode(alglib.xparams _params) - { - int result = 0; + /************************************************************************* + Copies column J of A[,] to vector X[] - result = 7; - return result; - } + INPUT PARAMETERS: + N - vector length + A - source 2D array + J - source col index + OUTPUT PARAMETERS: + X - preallocated array[N], destination - public static int getsparsematrixserializationcode(alglib.xparams _params) + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rcopycv(int n, + double[,] a, + int j, + double[] x, + alglib.xparams _params) { - int result = 0; + int i = 0; - result = 8; - return result; + for(i=0; i<=n-1; i++) + { + x[i] = a[i,j]; + } } - public static int getspline2dwithmissingnodesserializationcode(alglib.xparams _params) - { - int result = 0; - - result = 9; - return result; - } - + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Matrix-vector product: y := alpha*op(A)*x + beta*y - public static int getspline1dserializationcode(alglib.xparams _params) - { - int result = 0; + NOTE: this function expects Y to be large enough to store result. No + automatic preallocation happens for smaller arrays. No integrity + checks is performed for sizes of A, x, y. - result = 10; - return result; - } + INPUT PARAMETERS: + M - number of rows of op(A) + N - number of columns of op(A) + Alpha- coefficient + A - source matrix + OpA - operation type: + * OpA=0 => op(A) = A + * OpA=1 => op(A) = A^T + X - input vector, has at least N elements + Beta- coefficient + Y - preallocated output array, has at least M elements + OUTPUT PARAMETERS: + Y - vector which stores result - public static int getknnserializationcode(alglib.xparams _params) - { - int result = 0; + HANDLING OF SPECIAL CASES: + * if M=0, then subroutine does nothing. It does not even touch arrays. + * if N=0 or Alpha=0.0, then: + * if Beta=0, then Y is filled by zeros. A and X are not referenced + at all. Initial values of Y are ignored (we do not multiply Y by + zero, we just rewrite it by zeros) + * if Beta<>0, then Y is replaced by Beta*Y + * if M>0, N>0, Alpha<>0, but Beta=0, then Y is replaced by A*x; + initial state of Y is ignored (rewritten by A*x, without initial + multiplication by zeros). - result = 108; - return result; - } + -- ALGLIB routine -- - public static int getlptestserializationcode(alglib.xparams _params) + 01.09.2021 + Bochkanov Sergey + *************************************************************************/ + public static void rgemv(int m, + int n, + double alpha, + double[,] a, + int opa, + double[] x, + double beta, + double[] y, + alglib.xparams _params) { - int result = 0; - - result = 200; - return result; - } - - - } - public class tsort - { - /************************************************************************* - This function sorts array of real keys by ascending. - - Its results are: - * sorted array A - * permutation tables P1, P2 - - Algorithm outputs permutation tables using two formats: - * as usual permutation of [0..N-1]. If P1[i]=j, then sorted A[i] contains - value which was moved there from J-th position. - * as a sequence of pairwise permutations. Sorted A[] may be obtained by - swaping A[i] and A[P2[i]] for all i from 0 to N-1. - - INPUT PARAMETERS: - A - unsorted array - N - array size - - OUPUT PARAMETERS: - A - sorted array - P1, P2 - permutation tables, array[N] - - NOTES: - this function assumes that A[] is finite; it doesn't checks that - condition. All other conditions (size of input arrays, etc.) are not - checked too. - - -- ALGLIB -- - Copyright 14.05.2008 by Bochkanov Sergey - *************************************************************************/ - public static void tagsort(ref double[] a, - int n, - ref int[] p1, - ref int[] p2, - alglib.xparams _params) - { - apserv.apbuffers buf = new apserv.apbuffers(); - - p1 = new int[0]; - p2 = new int[0]; - - tagsortbuf(ref a, n, ref p1, ref p2, buf, _params); - } - - - /************************************************************************* - 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, - alglib.xparams _params) - { - int i = 0; - int lv = 0; - int lp = 0; - int rv = 0; - int rp = 0; + int i = 0; + int j = 0; + double v = 0; // - // Special cases + // Properly premultiply Y by Beta. // - if( n<=0 ) + // Quick exit for M=0, N=0 or Alpha=0. + // After this block we have M>0, N>0, Alpha<>0. + // + if( m<=0 ) { return; } - if( n==1 ) + if( (double)(beta)!=(double)(0) ) { - apserv.ivectorsetlengthatleast(ref p1, 1, _params); - apserv.ivectorsetlengthatleast(ref p2, 1, _params); - p1[0] = 0; - p2[0] = 0; - return; + rmulv(m, beta, y, _params); } - - // - // General case, N>1: prepare permutations table P1 - // - apserv.ivectorsetlengthatleast(ref p1, n, _params); - for(i=0; i<=n-1; i++) + else { - p1[i] = i; + rsetv(m, 0.0, y, _params); + } + if( n<=0 || (double)(alpha)==(double)(0.0) ) + { + return; } // - // General case, N>1: sort, update P1 - // - apserv.rvectorsetlengthatleast(ref buf.ra0, n, _params); - apserv.ivectorsetlengthatleast(ref buf.ia0, n, _params); - tagsortfasti(ref a, ref p1, ref buf.ra0, ref buf.ia0, n, _params); - - // - // 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. + // Generic code // - apserv.ivectorsetlengthatleast(ref buf.ia0, n, _params); - apserv.ivectorsetlengthatleast(ref buf.ia1, n, _params); - apserv.ivectorsetlengthatleast(ref p2, n, _params); - for(i=0; i<=n-1; i++) - { - buf.ia0[i] = i; - buf.ia1[i] = i; - } - for(i=0; i<=n-1; i++) + if( opa==0 ) { // - // calculate LP, LV, RP, RV - // - lp = i; - lv = buf.ia1[lp]; - rv = p1[i]; - rp = buf.ia0[rv]; - - // - // Fill P2 + // y += A*x // - p2[i] = rp; + for(i=0; i<=m-1; i++) + { + v = 0; + for(j=0; j<=n-1; j++) + { + v = v+a[i,j]*x[j]; + } + y[i] = alpha*v+y[i]; + } + return; + } + if( opa==1 ) + { // - // update PV and VP + // y += A^T*x // - buf.ia1[lp] = rv; - buf.ia1[rp] = lv; - buf.ia0[lv] = rp; - buf.ia0[rv] = lp; + for(i=0; i<=n-1; i++) + { + v = alpha*x[i]; + for(j=0; j<=m-1; j++) + { + y[j] = y[j]+v*a[i,j]; + } + } + return; } } + #endif + #if ALGLIB_NO_FAST_KERNELS /************************************************************************* - Same as TagSort, but optimized for real keys and integer labels. + Matrix-vector product: y := alpha*op(A)*x + beta*y - A is sorted, and same permutations are applied to B. + Here x, y, A are subvectors/submatrices of larger vectors/matrices. - NOTES: - 1. this function assumes that A[] is finite; it doesn't checks that - condition. All other conditions (size of input arrays, etc.) are not - checked too. - 2. this function uses two buffers, BufA and BufB, each is N elements large. - They may be preallocated (which will save some time) or not, in which - case function will automatically allocate memory. + NOTE: this function expects Y to be large enough to store result. No + automatic preallocation happens for smaller arrays. No integrity + checks is performed for sizes of A, x, y. - -- ALGLIB -- - Copyright 11.12.2008 by Bochkanov Sergey + INPUT PARAMETERS: + M - number of rows of op(A) + N - number of columns of op(A) + Alpha- coefficient + A - source matrix + IA - submatrix offset (row index) + JA - submatrix offset (column index) + OpA - operation type: + * OpA=0 => op(A) = A + * OpA=1 => op(A) = A^T + X - input vector, has at least N+IX elements + IX - subvector offset + Beta- coefficient + Y - preallocated output array, has at least M+IY elements + IY - subvector offset + + OUTPUT PARAMETERS: + Y - vector which stores result + + HANDLING OF SPECIAL CASES: + * if M=0, then subroutine does nothing. It does not even touch arrays. + * if N=0 or Alpha=0.0, then: + * if Beta=0, then Y is filled by zeros. A and X are not referenced + at all. Initial values of Y are ignored (we do not multiply Y by + zero, we just rewrite it by zeros) + * if Beta<>0, then Y is replaced by Beta*Y + * if M>0, N>0, Alpha<>0, but Beta=0, then Y is replaced by A*x; + initial state of Y is ignored (rewritten by A*x, without initial + multiplication by zeros). + + + -- ALGLIB routine -- + + 01.09.2021 + Bochkanov Sergey *************************************************************************/ - public static void tagsortfasti(ref double[] a, - ref int[] b, - ref double[] bufa, - ref int[] bufb, + public static void rgemvx(int m, int n, + double alpha, + double[,] a, + int ia, + int ja, + int opa, + double[] x, + int ix, + double beta, + double[] y, + int iy, alglib.xparams _params) { int i = 0; int j = 0; - bool isascending = new bool(); - bool isdescending = new bool(); - double tmpr = 0; - int tmpi = 0; + double v = 0; // - // Special case + // Properly premultiply Y by Beta. // - if( n<=1 ) + // Quick exit for M=0, N=0 or Alpha=0. + // After this block we have M>0, N>0, Alpha<>0. + // + if( m<=0 ) { return; } - - // - // Test for already sorted set - // - isascending = true; - isdescending = true; - for(i=1; i<=n-1; i++) + if( (double)(beta)!=(double)(0) ) { - isascending = isascending && a[i]>=a[i-1]; - isdescending = isdescending && a[i]<=a[i-1]; + rmulvx(m, beta, y, iy, _params); } - if( isascending ) + else { - return; + rsetvx(m, 0.0, y, iy, _params); } - if( isdescending ) + if( n<=0 || (double)(alpha)==(double)(0.0) ) { - for(i=0; i<=n-1; i++) - { - j = n-1-i; - if( j<=i ) - { - break; - } - tmpr = a[i]; - a[i] = a[j]; - a[j] = tmpr; - tmpi = b[i]; - b[i] = b[j]; - b[j] = tmpi; - } return; } // - // General case + // Generic code // - if( alglib.ap.len(bufa)=a[i-1]; - isdescending = isdescending && a[i]<=a[i-1]; - } - if( isascending ) + if( (m<=0 || n<=0) || (double)(alpha)==(double)(0) ) { return; } - if( isdescending ) + for(i=0; i<=m-1; i++) { - for(i=0; i<=n-1; i++) + s = alpha*u[i]; + for(j=0; j<=n-1; j++) { - j = n-1-i; - if( j<=i ) - { - break; - } - tmpr = a[i]; - a[i] = a[j]; - a[j] = tmpr; - tmpr = b[i]; - b[i] = b[j]; - b[j] = tmpr; + a[i,j] = a[i,j]+s*v[j]; } - return; - } - - // - // General case - // - if( alglib.ap.len(bufa)=0 + A - matrix, actial matrix is stored in A[IA:IA+N-1,JA:JA+N-1] + IA - submatrix offset + JA - submatrix offset + IsUpper - whether matrix is upper triangular + IsUnit - whether matrix is unitriangular + OpType - transformation type: + * 0 - no transformation + * 1 - transposition + X - right part, actual vector is stored in X[IX:IX+N-1] + IX - offset + + OUTPUT PARAMETERS + X - solution replaces elements X[IX:IX+N-1] + + -- ALGLIB routine -- + (c) 07.09.2021 Bochkanov Sergey *************************************************************************/ - public static void tagsortfast(ref double[] a, - ref double[] bufa, - int n, + public static void rtrsvx(int n, + double[,] a, + int ia, + int ja, + bool isupper, + bool isunit, + int optype, + double[] x, + int ix, alglib.xparams _params) { int i = 0; int j = 0; - bool isascending = new bool(); - bool isdescending = new bool(); - double tmpr = 0; + double v = 0; - - // - // Special case - // - if( n<=1 ) + if( n<=0 ) { return; } - - // - // Test for already sorted set - // - isascending = true; - isdescending = true; - for(i=1; i<=n-1; i++) - { - isascending = isascending && a[i]>=a[i-1]; - isdescending = isdescending && a[i]<=a[i-1]; - } - if( isascending ) + if( optype==0 && isupper ) { + for(i=n-1; i>=0; i--) + { + v = x[ix+i]; + for(j=i+1; j<=n-1; j++) + { + v = v-a[ia+i,ja+j]*x[ix+j]; + } + if( !isunit ) + { + v = v/a[ia+i,ja+i]; + } + x[ix+i] = v; + } return; } - if( isdescending ) + if( optype==0 && !isupper ) { for(i=0; i<=n-1; i++) { - j = n-1-i; - if( j<=i ) + v = x[ix+i]; + for(j=0; j<=i-1; j++) { - break; + v = v-a[ia+i,ja+j]*x[ix+j]; } - tmpr = a[i]; - a[i] = a[j]; - a[j] = tmpr; + if( !isunit ) + { + v = v/a[ia+i,ja+i]; + } + x[ix+i] = v; } return; } - - // - // General case - // - if( alglib.ap.len(bufa)=a[offset+i-1]; - } - if( isascending ) - { - return; - } - - // - // General case, N>1: sort, update B - // - for(i=2; i<=n; i++) + if( optype==1 && isupper ) { - t = i; - while( t!=1 ) + for(i=0; i<=n-1; i++) { - k = t/2; - p0 = offset+k-1; - p1 = offset+t-1; - ak = a[p0]; - at = a[p1]; - if( ak>=at ) + v = x[ix+i]; + if( !isunit ) { - break; + v = v/a[ia+i,ja+i]; + } + x[ix+i] = v; + if( v==0 ) + { + continue; + } + for(j=i+1; j<=n-1; j++) + { + x[ix+j] = x[ix+j]-v*a[ia+i,ja+j]; } - a[p0] = at; - a[p1] = ak; - tmpr = b[p0]; - b[p0] = b[p1]; - b[p1] = tmpr; - t = k; } + return; } - for(i=n-1; i>=1; i--) + if( optype==1 && !isupper ) { - p0 = offset+0; - p1 = offset+i; - tmp = a[p1]; - a[p1] = a[p0]; - a[p0] = tmp; - at = tmp; - tmpr = b[p1]; - b[p1] = b[p0]; - b[p0] = tmpr; - bt = tmpr; - t = 0; - while( true ) + for(i=n-1; i>=0; i--) { - k = 2*t+1; - if( k+1>i ) + v = x[ix+i]; + if( !isunit ) { - break; + v = v/a[ia+i,ja+i]; } - p0 = offset+t; - p1 = offset+k; - ak = a[p1]; - if( k+1ak ) - { - ak = ak1; - p1 = p1+1; - k = k+1; - } + continue; } - if( at>=ak ) + for(j=0; j<=i-1; j++) { - break; + x[ix+j] = x[ix+j]-v*a[ia+i,ja+j]; } - a[p1] = at; - a[p0] = ak; - b[p0] = b[p1]; - b[p1] = bt; - t = k; } + return; } + alglib.ap.assert(false, "rTRSVX: unexpected operation type"); } + #endif /************************************************************************* - Sorting function optimized for integer keys and real labels, can be used - to sort middle of the array + Fast kernel - A is sorted, and same permutations are applied to B and C. + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey + *************************************************************************/ + public static bool rmatrixgerf(int m, + int n, + double[,] a, + int ia, + int ja, + double ralpha, + double[] u, + int iu, + double[] v, + int iv, + alglib.xparams _params) + { + bool result = new bool(); - Elements beyond [offs:offs+N-1] are not modified or referenced. + result = false; + return result; + } - NOTES: - this function assumes that A[] is finite; it doesn't checks that - condition. All other conditions (size of input arrays, etc.) are not - checked too. - -- ALGLIB -- - Copyright 11.12.2024 by Bochkanov Sergey + /************************************************************************* + Fast kernel + + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey *************************************************************************/ - public static void tagsortmiddleirr(int[] a, - double[] b, - double[] c, - int offset, + public static bool cmatrixrank1f(int m, int n, + complex[,] a, + int ia, + int ja, + complex[] u, + int iu, + complex[] v, + int iv, alglib.xparams _params) { - int i = 0; - int k = 0; - int t = 0; - int tmp = 0; - double tmpr = 0; - double tmpr2 = 0; - int p0 = 0; - int p1 = 0; - int at = 0; - int ak = 0; - int ak1 = 0; - double bt = 0; - double ct = 0; + bool result = new bool(); - - // - // Special cases - // - if( n<=1 ) - { - return; - } - - // - // General case, N>1: sort, update B and C - // - for(i=2; i<=n; i++) - { - t = i; - while( t!=1 ) - { - k = t/2; - p0 = offset+k-1; - p1 = offset+t-1; - ak = a[p0]; - at = a[p1]; - if( ak>=at ) - { - break; - } - a[p0] = at; - a[p1] = ak; - tmpr = b[p0]; - b[p0] = b[p1]; - b[p1] = tmpr; - tmpr2 = c[p0]; - c[p0] = c[p1]; - c[p1] = tmpr2; - t = k; - } - } - for(i=n-1; i>=1; i--) - { - p0 = offset+0; - p1 = offset+i; - tmp = a[p1]; - a[p1] = a[p0]; - a[p0] = tmp; - at = tmp; - tmpr = b[p1]; - b[p1] = b[p0]; - b[p0] = tmpr; - bt = tmpr; - tmpr2 = c[p1]; - c[p1] = c[p0]; - c[p0] = tmpr2; - ct = tmpr2; - t = 0; - while( true ) - { - k = 2*t+1; - if( k+1>i ) - { - break; - } - p0 = offset+t; - p1 = offset+k; - ak = a[p1]; - if( k+1ak ) - { - ak = ak1; - p1 = p1+1; - k = k+1; - } - } - if( at>=ak ) - { - break; - } - a[p1] = at; - a[p0] = ak; - b[p0] = b[p1]; - b[p1] = bt; - c[p0] = c[p1]; - c[p1] = ct; - t = k; - } - } + result = false; + return result; } /************************************************************************* - Sorting function optimized for real keys and integer labels, can be used - to sort middle of the array + Fast kernel - A is sorted, and same permutations are applied to B. + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey + *************************************************************************/ + public static bool rmatrixrank1f(int m, + int n, + double[,] a, + int ia, + int ja, + double[] u, + int iu, + double[] v, + int iv, + alglib.xparams _params) + { + bool result = new bool(); - NOTES: - this function assumes that A[] is finite; it doesn't checks that - condition. All other conditions (size of input arrays, etc.) are not - checked too. + result = false; + return result; + } - -- ALGLIB -- - Copyright 11.12.2008 by Bochkanov Sergey + + /************************************************************************* + Fast kernel + + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey *************************************************************************/ - public static void tagsortmiddleri(double[] a, - int[] b, - int offset, + public static bool cmatrixrighttrsmf(int m, int n, + complex[,] a, + int i1, + int j1, + bool isupper, + bool isunit, + int optype, + complex[,] x, + int i2, + int j2, alglib.xparams _params) { - int i = 0; - int k = 0; - int t = 0; - double tmpa = 0; - int tmpi = 0; - int p0 = 0; - int p1 = 0; - double at = 0; - double ak = 0; - double ak1 = 0; - int bt = 0; + bool result = new bool(); - - // - // Special cases - // - if( n<=1 ) - { - return; - } - - // - // General case, N>1: sort, update B - // - for(i=2; i<=n; i++) - { - t = i; - while( t!=1 ) - { - k = t/2; - p0 = offset+k-1; - p1 = offset+t-1; - ak = a[p0]; - at = a[p1]; - if( (double)(ak)>=(double)(at) ) - { - break; - } - a[p0] = at; - a[p1] = ak; - tmpi = b[p0]; - b[p0] = b[p1]; - b[p1] = tmpi; - t = k; - } - } - for(i=n-1; i>=1; i--) - { - p0 = offset+0; - p1 = offset+i; - tmpa = a[p1]; - a[p1] = a[p0]; - a[p0] = tmpa; - at = tmpa; - tmpi = b[p1]; - b[p1] = b[p0]; - b[p0] = tmpi; - bt = tmpi; - t = 0; - while( true ) - { - k = 2*t+1; - if( k+1>i ) - { - break; - } - p0 = offset+t; - p1 = offset+k; - ak = a[p1]; - if( k+1(double)(ak) ) - { - ak = ak1; - p1 = p1+1; - k = k+1; - } - } - if( (double)(at)>=(double)(ak) ) - { - break; - } - a[p1] = at; - a[p0] = ak; - b[p0] = b[p1]; - b[p1] = bt; - t = k; - } - } + result = false; + return result; } /************************************************************************* - Sorting function optimized for integer keys and integer labels, can be used - to sort middle of the array + Fast kernel - A is sorted, and same permutations are applied to B. + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey + *************************************************************************/ + public static bool cmatrixlefttrsmf(int m, + int n, + complex[,] a, + int i1, + int j1, + bool isupper, + bool isunit, + int optype, + complex[,] x, + int i2, + int j2, + alglib.xparams _params) + { + bool result = new bool(); - NOTES: - this function assumes that A[] is finite; it doesn't checks that - condition. All other conditions (size of input arrays, etc.) are not - checked too. + result = false; + return result; + } - -- ALGLIB -- - Copyright 11.12.2008 by Bochkanov Sergey + + /************************************************************************* + Fast kernel + + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey *************************************************************************/ - public static void tagsortmiddleii(ref int[] a, - ref int[] b, - int offset, + public static bool rmatrixrighttrsmf(int m, int n, + double[,] a, + int i1, + int j1, + bool isupper, + bool isunit, + int optype, + double[,] x, + int i2, + int j2, alglib.xparams _params) { - int i = 0; - int k = 0; - int t = 0; - int tmp = 0; - int tmpi = 0; - int p0 = 0; - int p1 = 0; - int at = 0; - int ak = 0; - int ak1 = 0; - int bt = 0; + bool result = new bool(); - - // - // Special cases - // - if( n<=1 ) - { - return; - } - - // - // General case, N>1: sort, update B - // - for(i=2; i<=n; i++) - { - t = i; - while( t!=1 ) - { - k = t/2; - p0 = offset+k-1; - p1 = offset+t-1; - ak = a[p0]; - at = a[p1]; - if( ak>=at ) - { - break; - } - a[p0] = at; - a[p1] = ak; - tmpi = b[p0]; - b[p0] = b[p1]; - b[p1] = tmpi; - t = k; - } - } - for(i=n-1; i>=1; i--) - { - p0 = offset+0; - p1 = offset+i; - tmp = a[p1]; - a[p1] = a[p0]; - a[p0] = tmp; - at = tmp; - tmpi = b[p1]; - b[p1] = b[p0]; - b[p0] = tmpi; - bt = tmpi; - t = 0; - while( true ) - { - k = 2*t+1; - if( k+1>i ) - { - break; - } - p0 = offset+t; - p1 = offset+k; - ak = a[p1]; - if( k+1ak ) - { - ak = ak1; - p1 = p1+1; - k = k+1; - } - } - if( at>=ak ) - { - break; - } - a[p1] = at; - a[p0] = ak; - b[p0] = b[p1]; - b[p1] = bt; - t = k; - } - } + result = false; + return result; } /************************************************************************* - Sorting function optimized for integer keys and real labels, can be used - to sort middle of the array + Fast kernel - A is sorted, and same permutations are applied to B. + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey + *************************************************************************/ + public static bool rmatrixlefttrsmf(int m, + int n, + double[,] a, + int i1, + int j1, + bool isupper, + bool isunit, + int optype, + double[,] x, + int i2, + int j2, + alglib.xparams _params) + { + bool result = new bool(); - NOTES: - this function assumes that A[] is finite; it doesn't checks that - condition. All other conditions (size of input arrays, etc.) are not - checked too. + result = false; + return result; + } - -- ALGLIB -- - Copyright 11.12.2008 by Bochkanov Sergey + + /************************************************************************* + Fast kernel + + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey *************************************************************************/ - public static void tagsortmiddlei(ref int[] a, - int offset, - int n, + public static bool cmatrixherkf(int n, + int k, + double alpha, + complex[,] a, + int ia, + int ja, + int optypea, + double beta, + complex[,] c, + int ic, + int jc, + bool isupper, alglib.xparams _params) { - int i = 0; - int k = 0; - int t = 0; - int tmp = 0; - int p0 = 0; - int p1 = 0; - int at = 0; - int ak = 0; - int ak1 = 0; + bool result = new bool(); - - // - // Special cases - // - if( n<=1 ) - { - return; - } - - // - // General case, N>1: sort, update B - // - for(i=2; i<=n; i++) - { - t = i; - while( t!=1 ) - { - k = t/2; - p0 = offset+k-1; - p1 = offset+t-1; - ak = a[p0]; - at = a[p1]; - if( ak>=at ) - { - break; - } - a[p0] = at; - a[p1] = ak; - t = k; - } - } - for(i=n-1; i>=1; i--) - { - p0 = offset+0; - p1 = offset+i; - tmp = a[p1]; - a[p1] = a[p0]; - a[p0] = tmp; - at = tmp; - t = 0; - while( true ) - { - k = 2*t+1; - if( k+1>i ) - { - break; - } - p0 = offset+t; - p1 = offset+k; - ak = a[p1]; - if( k+1ak ) - { - ak = ak1; - p1 = p1+1; - k = k+1; - } - } - if( at>=ak ) - { - break; - } - a[p1] = at; - a[p0] = ak; - t = k; - } - } + result = false; + return result; } /************************************************************************* - Sorting function optimized for integer values (only keys, no labels), can - be used to sort middle of the array + Fast kernel - -- ALGLIB -- - Copyright 11.12.2008 by Bochkanov Sergey + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey *************************************************************************/ - public static void sortmiddlei(int[] a, - int offset, - int n, + public static bool rmatrixsyrkf(int n, + int k, + double alpha, + double[,] a, + int ia, + int ja, + int optypea, + double beta, + double[,] c, + int ic, + int jc, + bool isupper, alglib.xparams _params) { - int i = 0; - int k = 0; - int t = 0; - int tmp = 0; - int p0 = 0; - int p1 = 0; - int at = 0; - int ak = 0; - int ak1 = 0; + bool result = new bool(); - - // - // Special cases - // - if( n<=1 ) - { - return; - } - - // - // General case, N>1: sort, update B - // - for(i=2; i<=n; i++) - { - t = i; - while( t!=1 ) - { - k = t/2; - p0 = offset+k-1; - p1 = offset+t-1; - ak = a[p0]; - at = a[p1]; - if( ak>=at ) - { - break; - } - a[p0] = at; - a[p1] = ak; - t = k; - } - } - for(i=n-1; i>=1; i--) - { - p0 = offset+0; - p1 = offset+i; - tmp = a[p1]; - a[p1] = a[p0]; - a[p0] = tmp; - at = tmp; - t = 0; - while( true ) - { - k = 2*t+1; - if( k+1>i ) - { - break; - } - p0 = offset+t; - p1 = offset+k; - ak = a[p1]; - if( k+1ak ) - { - ak = ak1; - p1 = p1+1; - k = k+1; - } - } - if( at>=ak ) - { - break; - } - a[p1] = at; - a[p0] = ak; - t = k; - } - } + result = false; + return result; } /************************************************************************* - Heap operations: adds element to the heap + Fast kernel - PARAMETERS: - A - heap itself, must be at least array[0..N] - B - array of integer tags, which are updated according to - permutations in the heap - N - size of the heap (without new element). - updated on output - VA - value of the element being added - VB - value of the tag + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey + *************************************************************************/ + public static bool cmatrixgemmf(int m, + int n, + int k, + complex alpha, + complex[,] a, + int ia, + int ja, + int optypea, + complex[,] b, + int ib, + int jb, + int optypeb, + complex beta, + complex[,] c, + int ic, + int jc, + alglib.xparams _params) + { + bool result = new bool(); - -- ALGLIB -- - Copyright 28.02.2010 by Bochkanov Sergey + result = false; + return result; + } + + + /************************************************************************* + CMatrixGEMM kernel, basecase code for CMatrixGEMM. + + This subroutine calculates C = alpha*op1(A)*op2(B) +beta*C where: + * C is MxN general matrix + * op1(A) is MxK matrix + * op2(B) is KxN matrix + * "op" may be identity transformation, transposition, conjugate transposition + + Additional info: + * multiplication result replaces C. If Beta=0, C elements are not used in + calculations (not multiplied by zero - just not referenced) + * if Alpha=0, A is not used (not multiplied by zero - just not referenced) + * if both Beta and Alpha are zero, C is filled by zeros. + + IMPORTANT: + + This function does NOT preallocate output matrix C, it MUST be preallocated + by caller prior to calling this function. In case C does not have enough + space to store result, exception will be generated. + + INPUT PARAMETERS + M - matrix size, M>0 + N - matrix size, N>0 + K - matrix size, K>0 + Alpha - coefficient + A - matrix + IA - submatrix offset + JA - submatrix offset + OpTypeA - transformation type: + * 0 - no transformation + * 1 - transposition + * 2 - conjugate transposition + B - matrix + IB - submatrix offset + JB - submatrix offset + OpTypeB - transformation type: + * 0 - no transformation + * 1 - transposition + * 2 - conjugate transposition + Beta - coefficient + C - PREALLOCATED output matrix + IC - submatrix offset + JC - submatrix offset + + -- ALGLIB routine -- + 27.03.2013 + Bochkanov Sergey *************************************************************************/ - public static void tagheappushi(ref double[] a, - ref int[] b, - ref int n, - double va, - int vb, + public static void cmatrixgemmk(int m, + int n, + int k, + complex alpha, + complex[,] a, + int ia, + int ja, + int optypea, + complex[,] b, + int ib, + int jb, + int optypeb, + complex beta, + complex[,] c, + int ic, + int jc, alglib.xparams _params) { + int i = 0; int j = 0; - int k = 0; - double v = 0; + complex v = 0; + complex v00 = 0; + complex v01 = 0; + complex v10 = 0; + complex v11 = 0; + double v00x = 0; + double v00y = 0; + double v01x = 0; + double v01y = 0; + double v10x = 0; + double v10y = 0; + double v11x = 0; + double v11y = 0; + double a0x = 0; + double a0y = 0; + double a1x = 0; + double a1y = 0; + double b0x = 0; + double b0y = 0; + double b1x = 0; + double b1y = 0; + int idxa0 = 0; + int idxa1 = 0; + int idxb0 = 0; + int idxb1 = 0; + int i0 = 0; + int i1 = 0; + int ik = 0; + int j0 = 0; + int j1 = 0; + int jk = 0; + int t = 0; + int offsa = 0; + int offsb = 0; + int i_ = 0; + int i1_ = 0; - if( n<0 ) + + // + // if matrix size is zero + // + if( m==0 || n==0 ) { return; } // - // N=0 is a special case + // Try optimized code // - if( n==0 ) + if( cmatrixgemmf(m, n, k, alpha, a, ia, ja, optypea, b, ib, jb, optypeb, beta, c, ic, jc, _params) ) { - a[0] = va; - b[0] = vb; - n = n+1; return; } // - // add current point to the heap - // (add to the bottom, then move up) - // - // we don't write point to the heap - // until its final position is determined - // (it allow us to reduce number of array access operations) + // if K=0 or Alpha=0, then C=Beta*C // - j = n; - n = n+1; - while( j>0 ) + if( k==0 || alpha==0 ) { - k = (j-1)/2; - v = a[k]; - if( v=n ) - { - - // - // only one child. - // - // swap and terminate (because this child - // have no siblings due to heap structure) - // - v = a[k1]; - if( v>va ) - { - a[j] = v; - b[j] = b[k1]; - j = k1; - } - break; - } - else + j = 0; + while( jv2 ) + if( i+2<=m && j+2<=n ) { - if( va=1 - - On output top element is moved to A[N-1], B[N-1], heap is reordered, N is - decreased by 1. - - -- ALGLIB -- - Copyright 28.02.2010 by Bochkanov Sergey - *************************************************************************/ - public static void tagheappopi(ref double[] a, - ref int[] b, - ref int n, - alglib.xparams _params) - { - double va = 0; - int vb = 0; - - if( n<1 ) - { - return; - } - - // - // N=1 is a special case - // - if( n==1 ) - { - n = 0; - return; + else + { + + // + // Determine submatrix [I0..I1]x[J0..J1] to process + // + i0 = i; + i1 = Math.Min(i+1, m-1); + j0 = j; + j1 = Math.Min(j+1, n-1); + + // + // Process submatrix + // + for(ik=i0; ik<=i1; ik++) + { + for(jk=j0; jk<=j1; jk++) + { + if( k==0 || alpha==0 ) + { + v = 0; + } + else + { + v = 0.0; + if( optypea==0 && optypeb==0 ) + { + i1_ = (ib)-(ja); + v = 0.0; + for(i_=ja; i_<=ja+k-1;i_++) + { + v += a[ia+ik,i_]*b[i_+i1_,jb+jk]; + } + } + if( optypea==0 && optypeb==1 ) + { + i1_ = (jb)-(ja); + v = 0.0; + for(i_=ja; i_<=ja+k-1;i_++) + { + v += a[ia+ik,i_]*b[ib+jk,i_+i1_]; + } + } + if( optypea==0 && optypeb==2 ) + { + i1_ = (jb)-(ja); + v = 0.0; + for(i_=ja; i_<=ja+k-1;i_++) + { + v += a[ia+ik,i_]*math.conj(b[ib+jk,i_+i1_]); + } + } + if( optypea==1 && optypeb==0 ) + { + i1_ = (ib)-(ia); + v = 0.0; + for(i_=ia; i_<=ia+k-1;i_++) + { + v += a[i_,ja+ik]*b[i_+i1_,jb+jk]; + } + } + if( optypea==1 && optypeb==1 ) + { + i1_ = (jb)-(ia); + v = 0.0; + for(i_=ia; i_<=ia+k-1;i_++) + { + v += a[i_,ja+ik]*b[ib+jk,i_+i1_]; + } + } + if( optypea==1 && optypeb==2 ) + { + i1_ = (jb)-(ia); + v = 0.0; + for(i_=ia; i_<=ia+k-1;i_++) + { + v += a[i_,ja+ik]*math.conj(b[ib+jk,i_+i1_]); + } + } + if( optypea==2 && optypeb==0 ) + { + i1_ = (ib)-(ia); + v = 0.0; + for(i_=ia; i_<=ia+k-1;i_++) + { + v += math.conj(a[i_,ja+ik])*b[i_+i1_,jb+jk]; + } + } + if( optypea==2 && optypeb==1 ) + { + i1_ = (jb)-(ia); + v = 0.0; + for(i_=ia; i_<=ia+k-1;i_++) + { + v += math.conj(a[i_,ja+ik])*b[ib+jk,i_+i1_]; + } + } + if( optypea==2 && optypeb==2 ) + { + i1_ = (jb)-(ia); + v = 0.0; + for(i_=ia; i_<=ia+k-1;i_++) + { + v += math.conj(a[i_,ja+ik])*math.conj(b[ib+jk,i_+i1_]); + } + } + } + if( beta==0 ) + { + c[ic+ik,jc+jk] = alpha*v; + } + else + { + c[ic+ik,jc+jk] = beta*c[ic+ik,jc+jk]+alpha*v; + } + } + } + } + j = j+2; + } + i = i+2; } - - // - // swap top element and last element, - // then reorder heap - // - va = a[n-1]; - vb = b[n-1]; - a[n-1] = a[0]; - b[n-1] = b[0]; - n = n-1; - tagheapreplacetopi(ref a, ref b, n, va, vb, _params); } /************************************************************************* - Search first element less than T in sorted array. - - PARAMETERS: - A - sorted array by ascending from 0 to N-1 - N - number of elements in array - T - the desired element + RMatrixGEMM kernel, basecase code for RMatrixGEMM. - RESULT: - The very first element's index, which isn't less than T. - In the case when there aren't such elements, returns N. - *************************************************************************/ - public static int lowerbound(double[] a, - int n, - double t, - alglib.xparams _params) - { - int result = 0; - int l = 0; - int half = 0; - int first = 0; - int middle = 0; + This subroutine calculates C = alpha*op1(A)*op2(B) +beta*C where: + * C is MxN general matrix + * op1(A) is MxK matrix + * op2(B) is KxN matrix + * "op" may be identity transformation, transposition - l = n; - first = 0; - while( l>0 ) - { - half = l/2; - middle = first+half; - if( (double)(a[middle])<(double)(t) ) - { - first = middle+1; - l = l-half-1; - } - else - { - l = half; - } - } - result = first; - return result; - } + Additional info: + * multiplication result replaces C. If Beta=0, C elements are not used in + calculations (not multiplied by zero - just not referenced) + * if Alpha=0, A is not used (not multiplied by zero - just not referenced) + * if both Beta and Alpha are zero, C is filled by zeros. + IMPORTANT: - /************************************************************************* - Search first element more than T in sorted array. + This function does NOT preallocate output matrix C, it MUST be preallocated + by caller prior to calling this function. In case C does not have enough + space to store result, exception will be generated. - PARAMETERS: - A - sorted array by ascending from 0 to N-1 - N - number of elements in array - T - the desired element + INPUT PARAMETERS + M - matrix size, M>0 + N - matrix size, N>0 + K - matrix size, K>0 + Alpha - coefficient + A - matrix + IA - submatrix offset + JA - submatrix offset + OpTypeA - transformation type: + * 0 - no transformation + * 1 - transposition + B - matrix + IB - submatrix offset + JB - submatrix offset + OpTypeB - transformation type: + * 0 - no transformation + * 1 - transposition + Beta - coefficient + C - PREALLOCATED output matrix + IC - submatrix offset + JC - submatrix offset - RESULT: - The very first element's index, which more than T. - In the case when there aren't such elements, returns N. + -- ALGLIB routine -- + 27.03.2013 + Bochkanov Sergey *************************************************************************/ - public static int upperbound(double[] a, + public static void rmatrixgemmk(int m, int n, - double t, + int k, + double alpha, + double[,] a, + int ia, + int ja, + int optypea, + double[,] b, + int ib, + int jb, + int optypeb, + double beta, + double[,] c, + int ic, + int jc, alglib.xparams _params) { - int result = 0; - int l = 0; - int half = 0; - int first = 0; - int middle = 0; + int i = 0; + int j = 0; - l = n; - first = 0; - while( l>0 ) - { - half = l/2; - middle = first+half; - if( (double)(t)<(double)(a[middle]) ) - { - l = half; - } - else - { - first = middle+1; - l = l-half-1; - } + + // + // if matrix size is zero + // + if( m==0 || n==0 ) + { + return; } - result = first; - return result; - } - - - /************************************************************************* - Internal TagSortFastI: sorts A[I1...I2] (both bounds are included), - applies same permutations to B. - - -- ALGLIB -- - Copyright 06.09.2010 by Bochkanov Sergey - *************************************************************************/ - private static void tagsortfastirec(ref double[] a, - ref int[] b, - ref double[] bufa, - ref int[] bufb, - int i1, - int i2, - alglib.xparams _params) - { - int i = 0; - int j = 0; - int k = 0; - int cntless = 0; - int cnteq = 0; - int cntgreater = 0; - double tmpr = 0; - int tmpi = 0; - double v0 = 0; - double v1 = 0; - double v2 = 0; - double vp = 0; - // - // Fast exit + // Try optimized code // - if( i2<=i1 ) + if( rgemm32basecase(m, n, k, alpha, a, ia, ja, optypea, b, ib, jb, optypeb, beta, c, ic, jc, _params) ) { return; } // - // Non-recursive sort for small arrays + // if K=0 or Alpha=0, then C=Beta*C // - if( i2-i1<=16 ) + if( k==0 || (double)(alpha)==(double)(0) ) { - for(j=i1+1; j<=i2; j++) + if( (double)(beta)!=(double)(1) ) { - - // - // Search elements [I1..J-1] for place to insert Jth element. - // - // This code stops immediately if we can leave A[J] at J-th position - // (all elements have same value of A[J] larger than any of them) - // - tmpr = a[j]; - tmpi = j; - for(k=j-1; k>=i1; k--) + if( (double)(beta)!=(double)(0) ) { - if( a[k]<=tmpr ) + for(i=0; i<=m-1; i++) { - break; + for(j=0; j<=n-1; j++) + { + c[ic+i,jc+j] = beta*c[ic+i,jc+j]; + } } - tmpi = k; } - k = tmpi; - - // - // Insert Jth element into Kth position - // - if( k!=j ) + else { - tmpr = a[j]; - tmpi = b[j]; - for(i=j-1; i>=k; i--) + for(i=0; i<=m-1; i++) { - a[i+1] = a[i]; - b[i+1] = b[i]; + for(j=0; j<=n-1; j++) + { + c[ic+i,jc+j] = 0; + } } - a[k] = tmpr; - b[k] = tmpi; } } return; } // - // Quicksort: choose pivot - // Here we assume that I2-I1>=2 + // Call specialized code. // - v0 = a[i1]; - v1 = a[i1+(i2-i1)/2]; - v2 = a[i2]; - if( v0>v1 ) - { - tmpr = v1; - v1 = v0; - v0 = tmpr; - } - if( v1>v2 ) - { - tmpr = v2; - v2 = v1; - v1 = tmpr; - } - if( v0>v1 ) + // NOTE: specialized code was moved to separate function because of strange + // issues with instructions cache on some systems; Having too long + // functions significantly slows down internal loop of the algorithm. + // + if( optypea==0 && optypeb==0 ) { - tmpr = v1; - v1 = v0; - v0 = tmpr; + rmatrixgemmk44v00(m, n, k, alpha, a, ia, ja, b, ib, jb, beta, c, ic, jc, _params); } - vp = v1; - - // - // now pass through A/B and: - // * move elements that are LESS than VP to the left of A/B - // * move elements that are EQUAL to VP to the right of BufA/BufB (in the reverse order) - // * move elements that are GREATER than VP to the left of BufA/BufB (in the normal order - // * move elements from the tail of BufA/BufB to the middle of A/B (restoring normal order) - // * move elements from the left of BufA/BufB to the end of A/B - // - cntless = 0; - cnteq = 0; - cntgreater = 0; - for(i=i1; i<=i2; i++) + if( optypea==0 && optypeb!=0 ) { - v0 = a[i]; - if( v00 (assertion is thrown otherwise) + + INPUT PARAMETERS + M - matrix size, M>0 + N - matrix size, N>0 + K - matrix size, K>0 + Alpha - coefficient + A - matrix + IA - submatrix offset + JA - submatrix offset + B - matrix + IB - submatrix offset + JB - submatrix offset + Beta - coefficient + C - PREALLOCATED output matrix + IC - submatrix offset + JC - submatrix offset + + -- ALGLIB routine -- + 27.03.2013 + Bochkanov Sergey *************************************************************************/ - private static void tagsortfastrrec(ref double[] a, - ref double[] b, - ref double[] bufa, - ref double[] bufb, - int i1, - int i2, + public static void rmatrixgemmk44v00(int m, + int n, + int k, + double alpha, + double[,] a, + int ia, + int ja, + double[,] b, + int ib, + int jb, + double beta, + double[,] c, + int ic, + int jc, alglib.xparams _params) { int i = 0; int j = 0; - int k = 0; - double tmpr = 0; - double tmpr2 = 0; - int tmpi = 0; - int cntless = 0; - int cnteq = 0; - int cntgreater = 0; - double v0 = 0; - double v1 = 0; - double v2 = 0; - double vp = 0; + double v = 0; + double v00 = 0; + double v01 = 0; + double v02 = 0; + double v03 = 0; + double v10 = 0; + double v11 = 0; + double v12 = 0; + double v13 = 0; + double v20 = 0; + double v21 = 0; + double v22 = 0; + double v23 = 0; + double v30 = 0; + double v31 = 0; + double v32 = 0; + double v33 = 0; + double a0 = 0; + double a1 = 0; + double a2 = 0; + double a3 = 0; + double b0 = 0; + double b1 = 0; + double b2 = 0; + double b3 = 0; + int idxa0 = 0; + int idxa1 = 0; + int idxa2 = 0; + int idxa3 = 0; + int idxb0 = 0; + int idxb1 = 0; + int idxb2 = 0; + int idxb3 = 0; + int i0 = 0; + int i1 = 0; + int ik = 0; + int j0 = 0; + int j1 = 0; + int jk = 0; + int t = 0; + int offsa = 0; + int offsb = 0; + int i_ = 0; + int i1_ = 0; + alglib.ap.assert((double)(alpha)!=(double)(0), "RMatrixGEMMK44V00: internal error (Alpha=0)"); // - // Fast exit + // if matrix size is zero // - if( i2<=i1 ) + if( m==0 || n==0 ) { return; } // - // Non-recursive sort for small arrays + // A*B // - if( i2-i1<=16 ) + i = 0; + while( i=i1; k--) + if( i+4<=m && j+4<=n ) { - if( a[k]<=tmpr ) + + // + // Specialized 4x4 code for [I..I+3]x[J..J+3] submatrix of C. + // + // This submatrix is calculated as sum of K rank-1 products, + // with operands cached in local variables in order to speed + // up operations with arrays. + // + idxa0 = ia+i+0; + idxa1 = ia+i+1; + idxa2 = ia+i+2; + idxa3 = ia+i+3; + offsa = ja; + idxb0 = jb+j+0; + idxb1 = jb+j+1; + idxb2 = jb+j+2; + idxb3 = jb+j+3; + offsb = ib; + v00 = 0.0; + v01 = 0.0; + v02 = 0.0; + v03 = 0.0; + v10 = 0.0; + v11 = 0.0; + v12 = 0.0; + v13 = 0.0; + v20 = 0.0; + v21 = 0.0; + v22 = 0.0; + v23 = 0.0; + v30 = 0.0; + v31 = 0.0; + v32 = 0.0; + v33 = 0.0; + + // + // Different variants of internal loop + // + for(t=0; t<=k-1; t++) { - break; + a0 = a[idxa0,offsa]; + a1 = a[idxa1,offsa]; + b0 = b[offsb,idxb0]; + b1 = b[offsb,idxb1]; + v00 = v00+a0*b0; + v01 = v01+a0*b1; + v10 = v10+a1*b0; + v11 = v11+a1*b1; + a2 = a[idxa2,offsa]; + a3 = a[idxa3,offsa]; + v20 = v20+a2*b0; + v21 = v21+a2*b1; + v30 = v30+a3*b0; + v31 = v31+a3*b1; + b2 = b[offsb,idxb2]; + b3 = b[offsb,idxb3]; + v22 = v22+a2*b2; + v23 = v23+a2*b3; + v32 = v32+a3*b2; + v33 = v33+a3*b3; + v02 = v02+a0*b2; + v03 = v03+a0*b3; + v12 = v12+a1*b2; + v13 = v13+a1*b3; + offsa = offsa+1; + offsb = offsb+1; + } + if( (double)(beta)==(double)(0) ) + { + c[ic+i+0,jc+j+0] = alpha*v00; + c[ic+i+0,jc+j+1] = alpha*v01; + c[ic+i+0,jc+j+2] = alpha*v02; + c[ic+i+0,jc+j+3] = alpha*v03; + c[ic+i+1,jc+j+0] = alpha*v10; + c[ic+i+1,jc+j+1] = alpha*v11; + c[ic+i+1,jc+j+2] = alpha*v12; + c[ic+i+1,jc+j+3] = alpha*v13; + c[ic+i+2,jc+j+0] = alpha*v20; + c[ic+i+2,jc+j+1] = alpha*v21; + c[ic+i+2,jc+j+2] = alpha*v22; + c[ic+i+2,jc+j+3] = alpha*v23; + c[ic+i+3,jc+j+0] = alpha*v30; + c[ic+i+3,jc+j+1] = alpha*v31; + c[ic+i+3,jc+j+2] = alpha*v32; + c[ic+i+3,jc+j+3] = alpha*v33; + } + else + { + c[ic+i+0,jc+j+0] = beta*c[ic+i+0,jc+j+0]+alpha*v00; + c[ic+i+0,jc+j+1] = beta*c[ic+i+0,jc+j+1]+alpha*v01; + c[ic+i+0,jc+j+2] = beta*c[ic+i+0,jc+j+2]+alpha*v02; + c[ic+i+0,jc+j+3] = beta*c[ic+i+0,jc+j+3]+alpha*v03; + c[ic+i+1,jc+j+0] = beta*c[ic+i+1,jc+j+0]+alpha*v10; + c[ic+i+1,jc+j+1] = beta*c[ic+i+1,jc+j+1]+alpha*v11; + c[ic+i+1,jc+j+2] = beta*c[ic+i+1,jc+j+2]+alpha*v12; + c[ic+i+1,jc+j+3] = beta*c[ic+i+1,jc+j+3]+alpha*v13; + c[ic+i+2,jc+j+0] = beta*c[ic+i+2,jc+j+0]+alpha*v20; + c[ic+i+2,jc+j+1] = beta*c[ic+i+2,jc+j+1]+alpha*v21; + c[ic+i+2,jc+j+2] = beta*c[ic+i+2,jc+j+2]+alpha*v22; + c[ic+i+2,jc+j+3] = beta*c[ic+i+2,jc+j+3]+alpha*v23; + c[ic+i+3,jc+j+0] = beta*c[ic+i+3,jc+j+0]+alpha*v30; + c[ic+i+3,jc+j+1] = beta*c[ic+i+3,jc+j+1]+alpha*v31; + c[ic+i+3,jc+j+2] = beta*c[ic+i+3,jc+j+2]+alpha*v32; + c[ic+i+3,jc+j+3] = beta*c[ic+i+3,jc+j+3]+alpha*v33; } - tmpi = k; } - k = tmpi; - - // - // Insert Jth element into Kth position - // - if( k!=j ) + else { - tmpr = a[j]; - tmpr2 = b[j]; - for(i=j-1; i>=k; i--) + + // + // Determine submatrix [I0..I1]x[J0..J1] to process + // + i0 = i; + i1 = Math.Min(i+3, m-1); + j0 = j; + j1 = Math.Min(j+3, n-1); + + // + // Process submatrix + // + for(ik=i0; ik<=i1; ik++) { - a[i+1] = a[i]; - b[i+1] = b[i]; + for(jk=j0; jk<=j1; jk++) + { + if( k==0 || (double)(alpha)==(double)(0) ) + { + v = 0; + } + else + { + i1_ = (ib)-(ja); + v = 0.0; + for(i_=ja; i_<=ja+k-1;i_++) + { + v += a[ia+ik,i_]*b[i_+i1_,jb+jk]; + } + } + if( (double)(beta)==(double)(0) ) + { + c[ic+ik,jc+jk] = alpha*v; + } + else + { + c[ic+ik,jc+jk] = beta*c[ic+ik,jc+jk]+alpha*v; + } + } } - a[k] = tmpr; - b[k] = tmpr2; } + j = j+4; } - return; - } - - // - // Quicksort: choose pivot - // Here we assume that I2-I1>=16 - // - v0 = a[i1]; - v1 = a[i1+(i2-i1)/2]; - v2 = a[i2]; - if( v0>v1 ) - { - tmpr = v1; - v1 = v0; - v0 = tmpr; - } - if( v1>v2 ) - { - tmpr = v2; - v2 = v1; - v1 = tmpr; - } - if( v0>v1 ) - { - tmpr = v1; - v1 = v0; - v0 = tmpr; + i = i+4; } - vp = v1; - - // - // now pass through A/B and: - // * move elements that are LESS than VP to the left of A/B - // * move elements that are EQUAL to VP to the right of BufA/BufB (in the reverse order) - // * move elements that are GREATER than VP to the left of BufA/BufB (in the normal order - // * move elements from the tail of BufA/BufB to the middle of A/B (restoring normal order) - // * move elements from the left of BufA/BufB to the end of A/B - // - cntless = 0; - cnteq = 0; - cntgreater = 0; - for(i=i1; i<=i2; i++) - { - v0 = a[i]; - if( v00 (assertion is thrown otherwise) + + INPUT PARAMETERS + M - matrix size, M>0 + N - matrix size, N>0 + K - matrix size, K>0 + Alpha - coefficient + A - matrix + IA - submatrix offset + JA - submatrix offset + B - matrix + IB - submatrix offset + JB - submatrix offset + Beta - coefficient + C - PREALLOCATED output matrix + IC - submatrix offset + JC - submatrix offset + + -- ALGLIB routine -- + 27.03.2013 + Bochkanov Sergey *************************************************************************/ - private static void tagsortfastrec(ref double[] a, - ref double[] bufa, - int i1, - int i2, + public static void rmatrixgemmk44v01(int m, + int n, + int k, + double alpha, + double[,] a, + int ia, + int ja, + double[,] b, + int ib, + int jb, + double beta, + double[,] c, + int ic, + int jc, alglib.xparams _params) { - int cntless = 0; - int cnteq = 0; - int cntgreater = 0; int i = 0; int j = 0; - int k = 0; - double tmpr = 0; - int tmpi = 0; - double v0 = 0; - double v1 = 0; - double v2 = 0; - double vp = 0; + double v = 0; + double v00 = 0; + double v01 = 0; + double v02 = 0; + double v03 = 0; + double v10 = 0; + double v11 = 0; + double v12 = 0; + double v13 = 0; + double v20 = 0; + double v21 = 0; + double v22 = 0; + double v23 = 0; + double v30 = 0; + double v31 = 0; + double v32 = 0; + double v33 = 0; + double a0 = 0; + double a1 = 0; + double a2 = 0; + double a3 = 0; + double b0 = 0; + double b1 = 0; + double b2 = 0; + double b3 = 0; + int idxa0 = 0; + int idxa1 = 0; + int idxa2 = 0; + int idxa3 = 0; + int idxb0 = 0; + int idxb1 = 0; + int idxb2 = 0; + int idxb3 = 0; + int i0 = 0; + int i1 = 0; + int ik = 0; + int j0 = 0; + int j1 = 0; + int jk = 0; + int t = 0; + int offsa = 0; + int offsb = 0; + int i_ = 0; + int i1_ = 0; + alglib.ap.assert((double)(alpha)!=(double)(0), "RMatrixGEMMK44V00: internal error (Alpha=0)"); // - // Fast exit + // if matrix size is zero // - if( i2<=i1 ) + if( m==0 || n==0 ) { return; } // - // Non-recursive sort for small arrays + // A*B' // - if( i2-i1<=16 ) + i = 0; + while( i=i1; k--) + if( i+4<=m && j+4<=n ) { - if( a[k]<=tmpr ) + + // + // Specialized 4x4 code for [I..I+3]x[J..J+3] submatrix of C. + // + // This submatrix is calculated as sum of K rank-1 products, + // with operands cached in local variables in order to speed + // up operations with arrays. + // + idxa0 = ia+i+0; + idxa1 = ia+i+1; + idxa2 = ia+i+2; + idxa3 = ia+i+3; + offsa = ja; + idxb0 = ib+j+0; + idxb1 = ib+j+1; + idxb2 = ib+j+2; + idxb3 = ib+j+3; + offsb = jb; + v00 = 0.0; + v01 = 0.0; + v02 = 0.0; + v03 = 0.0; + v10 = 0.0; + v11 = 0.0; + v12 = 0.0; + v13 = 0.0; + v20 = 0.0; + v21 = 0.0; + v22 = 0.0; + v23 = 0.0; + v30 = 0.0; + v31 = 0.0; + v32 = 0.0; + v33 = 0.0; + for(t=0; t<=k-1; t++) { - break; + a0 = a[idxa0,offsa]; + a1 = a[idxa1,offsa]; + b0 = b[idxb0,offsb]; + b1 = b[idxb1,offsb]; + v00 = v00+a0*b0; + v01 = v01+a0*b1; + v10 = v10+a1*b0; + v11 = v11+a1*b1; + a2 = a[idxa2,offsa]; + a3 = a[idxa3,offsa]; + v20 = v20+a2*b0; + v21 = v21+a2*b1; + v30 = v30+a3*b0; + v31 = v31+a3*b1; + b2 = b[idxb2,offsb]; + b3 = b[idxb3,offsb]; + v22 = v22+a2*b2; + v23 = v23+a2*b3; + v32 = v32+a3*b2; + v33 = v33+a3*b3; + v02 = v02+a0*b2; + v03 = v03+a0*b3; + v12 = v12+a1*b2; + v13 = v13+a1*b3; + offsa = offsa+1; + offsb = offsb+1; } - tmpi = k; - } - k = tmpi; - - // - // Insert Jth element into Kth position - // - if( k!=j ) + if( (double)(beta)==(double)(0) ) + { + c[ic+i+0,jc+j+0] = alpha*v00; + c[ic+i+0,jc+j+1] = alpha*v01; + c[ic+i+0,jc+j+2] = alpha*v02; + c[ic+i+0,jc+j+3] = alpha*v03; + c[ic+i+1,jc+j+0] = alpha*v10; + c[ic+i+1,jc+j+1] = alpha*v11; + c[ic+i+1,jc+j+2] = alpha*v12; + c[ic+i+1,jc+j+3] = alpha*v13; + c[ic+i+2,jc+j+0] = alpha*v20; + c[ic+i+2,jc+j+1] = alpha*v21; + c[ic+i+2,jc+j+2] = alpha*v22; + c[ic+i+2,jc+j+3] = alpha*v23; + c[ic+i+3,jc+j+0] = alpha*v30; + c[ic+i+3,jc+j+1] = alpha*v31; + c[ic+i+3,jc+j+2] = alpha*v32; + c[ic+i+3,jc+j+3] = alpha*v33; + } + else + { + c[ic+i+0,jc+j+0] = beta*c[ic+i+0,jc+j+0]+alpha*v00; + c[ic+i+0,jc+j+1] = beta*c[ic+i+0,jc+j+1]+alpha*v01; + c[ic+i+0,jc+j+2] = beta*c[ic+i+0,jc+j+2]+alpha*v02; + c[ic+i+0,jc+j+3] = beta*c[ic+i+0,jc+j+3]+alpha*v03; + c[ic+i+1,jc+j+0] = beta*c[ic+i+1,jc+j+0]+alpha*v10; + c[ic+i+1,jc+j+1] = beta*c[ic+i+1,jc+j+1]+alpha*v11; + c[ic+i+1,jc+j+2] = beta*c[ic+i+1,jc+j+2]+alpha*v12; + c[ic+i+1,jc+j+3] = beta*c[ic+i+1,jc+j+3]+alpha*v13; + c[ic+i+2,jc+j+0] = beta*c[ic+i+2,jc+j+0]+alpha*v20; + c[ic+i+2,jc+j+1] = beta*c[ic+i+2,jc+j+1]+alpha*v21; + c[ic+i+2,jc+j+2] = beta*c[ic+i+2,jc+j+2]+alpha*v22; + c[ic+i+2,jc+j+3] = beta*c[ic+i+2,jc+j+3]+alpha*v23; + c[ic+i+3,jc+j+0] = beta*c[ic+i+3,jc+j+0]+alpha*v30; + c[ic+i+3,jc+j+1] = beta*c[ic+i+3,jc+j+1]+alpha*v31; + c[ic+i+3,jc+j+2] = beta*c[ic+i+3,jc+j+2]+alpha*v32; + c[ic+i+3,jc+j+3] = beta*c[ic+i+3,jc+j+3]+alpha*v33; + } + } + else { - tmpr = a[j]; - for(i=j-1; i>=k; i--) + + // + // Determine submatrix [I0..I1]x[J0..J1] to process + // + i0 = i; + i1 = Math.Min(i+3, m-1); + j0 = j; + j1 = Math.Min(j+3, n-1); + + // + // Process submatrix + // + for(ik=i0; ik<=i1; ik++) { - a[i+1] = a[i]; + for(jk=j0; jk<=j1; jk++) + { + if( k==0 || (double)(alpha)==(double)(0) ) + { + v = 0; + } + else + { + i1_ = (jb)-(ja); + v = 0.0; + for(i_=ja; i_<=ja+k-1;i_++) + { + v += a[ia+ik,i_]*b[ib+jk,i_+i1_]; + } + } + if( (double)(beta)==(double)(0) ) + { + c[ic+ik,jc+jk] = alpha*v; + } + else + { + c[ic+ik,jc+jk] = beta*c[ic+ik,jc+jk]+alpha*v; + } + } } - a[k] = tmpr; } + j = j+4; } - return; + i = i+4; } + } + + + /************************************************************************* + RMatrixGEMM kernel, basecase code for RMatrixGEMM, specialized for sitation + with OpTypeA=1 and OpTypeB=0. + + Additional info: + * this function requires that Alpha<>0 (assertion is thrown otherwise) + + INPUT PARAMETERS + M - matrix size, M>0 + N - matrix size, N>0 + K - matrix size, K>0 + Alpha - coefficient + A - matrix + IA - submatrix offset + JA - submatrix offset + B - matrix + IB - submatrix offset + JB - submatrix offset + Beta - coefficient + C - PREALLOCATED output matrix + IC - submatrix offset + JC - submatrix offset + + -- ALGLIB routine -- + 27.03.2013 + Bochkanov Sergey + *************************************************************************/ + public static void rmatrixgemmk44v10(int m, + int n, + int k, + double alpha, + double[,] a, + int ia, + int ja, + double[,] b, + int ib, + int jb, + double beta, + double[,] c, + int ic, + int jc, + alglib.xparams _params) + { + int i = 0; + int j = 0; + double v = 0; + double v00 = 0; + double v01 = 0; + double v02 = 0; + double v03 = 0; + double v10 = 0; + double v11 = 0; + double v12 = 0; + double v13 = 0; + double v20 = 0; + double v21 = 0; + double v22 = 0; + double v23 = 0; + double v30 = 0; + double v31 = 0; + double v32 = 0; + double v33 = 0; + double a0 = 0; + double a1 = 0; + double a2 = 0; + double a3 = 0; + double b0 = 0; + double b1 = 0; + double b2 = 0; + double b3 = 0; + int idxa0 = 0; + int idxa1 = 0; + int idxa2 = 0; + int idxa3 = 0; + int idxb0 = 0; + int idxb1 = 0; + int idxb2 = 0; + int idxb3 = 0; + int i0 = 0; + int i1 = 0; + int ik = 0; + int j0 = 0; + int j1 = 0; + int jk = 0; + int t = 0; + int offsa = 0; + int offsb = 0; + int i_ = 0; + int i1_ = 0; + + alglib.ap.assert((double)(alpha)!=(double)(0), "RMatrixGEMMK44V00: internal error (Alpha=0)"); // - // Quicksort: choose pivot - // Here we assume that I2-I1>=16 + // if matrix size is zero // - v0 = a[i1]; - v1 = a[i1+(i2-i1)/2]; - v2 = a[i2]; - if( v0>v1 ) - { - tmpr = v1; - v1 = v0; - v0 = tmpr; - } - if( v1>v2 ) - { - tmpr = v2; - v2 = v1; - v1 = tmpr; - } - if( v0>v1 ) + if( m==0 || n==0 ) { - tmpr = v1; - v1 = v0; - v0 = tmpr; + return; } - vp = v1; // - // now pass through A/B and: - // * move elements that are LESS than VP to the left of A/B - // * move elements that are EQUAL to VP to the right of BufA/BufB (in the reverse order) - // * move elements that are GREATER than VP to the left of BufA/BufB (in the normal order - // * move elements from the tail of BufA/BufB to the middle of A/B (restoring normal order) - // * move elements from the left of BufA/BufB to the end of A/B + // A'*B // - cntless = 0; - cnteq = 0; - cntgreater = 0; - for(i=i1; i<=i2; i++) + i = 0; + while( i(double)(Math.Abs(x[result])) ) - { - result = i; - } - } - return result; - } + /************************************************************************* + RMatrixGEMM kernel, basecase code for RMatrixGEMM, specialized for sitation + with OpTypeA=1 and OpTypeB=1. + Additional info: + * this function requires that Alpha<>0 (assertion is thrown otherwise) - public static int columnidxabsmax(double[,] x, - int i1, - int i2, - int j, - alglib.xparams _params) - { - int result = 0; - int i = 0; + INPUT PARAMETERS + M - matrix size, M>0 + N - matrix size, N>0 + K - matrix size, K>0 + Alpha - coefficient + A - matrix + IA - submatrix offset + JA - submatrix offset + B - matrix + IB - submatrix offset + JB - submatrix offset + Beta - coefficient + C - PREALLOCATED output matrix + IC - submatrix offset + JC - submatrix offset - result = i1; - for(i=i1+1; i<=i2; i++) - { - if( (double)(Math.Abs(x[i,j]))>(double)(Math.Abs(x[result,j])) ) - { - result = i; - } - } - return result; - } - - - public static int rowidxabsmax(double[,] x, - int j1, - int j2, - int i, - alglib.xparams _params) - { - int result = 0; - int j = 0; - - result = j1; - for(j=j1+1; j<=j2; j++) - { - if( (double)(Math.Abs(x[i,j]))>(double)(Math.Abs(x[i,result])) ) - { - result = j; - } - } - return result; - } - - - public static double upperhessenberg1norm(double[,] a, - int i1, - int i2, - int j1, - int j2, - ref double[] work, - alglib.xparams _params) - { - double result = 0; - int i = 0; - int j = 0; - - alglib.ap.assert(i2-i1==j2-j1, "UpperHessenberg1Norm: I2-I1<>J2-J1!"); - for(j=j1; j<=j2; j++) - { - work[j] = 0; - } - for(i=i1; i<=i2; i++) - { - for(j=Math.Max(j1, j1+i-i1-1); j<=j2; j++) - { - work[j] = work[j]+Math.Abs(a[i,j]); - } - } - result = 0; - for(j=j1; j<=j2; j++) - { - result = Math.Max(result, work[j]); - } - return result; - } - - - public static void copymatrix(double[,] a, - int is1, - int is2, - int js1, - int js2, - ref double[,] b, - int id1, - int id2, - int jd1, - int jd2, - alglib.xparams _params) - { - int isrc = 0; - int idst = 0; - int i_ = 0; - int i1_ = 0; - - if( is1>is2 || js1>js2 ) - { - return; - } - alglib.ap.assert(is2-is1==id2-id1, "CopyMatrix: different sizes!"); - alglib.ap.assert(js2-js1==jd2-jd1, "CopyMatrix: different sizes!"); - for(isrc=is1; isrc<=is2; isrc++) - { - idst = isrc-is1+id1; - i1_ = (js1) - (jd1); - for(i_=jd1; i_<=jd2;i_++) - { - b[idst,i_] = a[isrc,i_+i1_]; - } - } - } - - - public static void inplacetranspose(ref double[,] a, - int i1, - int i2, - int j1, - int j2, - ref double[] work, + -- ALGLIB routine -- + 27.03.2013 + Bochkanov Sergey + *************************************************************************/ + public static void rmatrixgemmk44v11(int m, + int n, + int k, + double alpha, + double[,] a, + int ia, + int ja, + double[,] b, + int ib, + int jb, + double beta, + double[,] c, + int ic, + int jc, alglib.xparams _params) { int i = 0; int j = 0; - int ips = 0; - int jps = 0; - int l = 0; - int i_ = 0; - int i1_ = 0; - - if( i1>i2 || j1>j2 ) - { - return; - } - alglib.ap.assert(i1-i2==j1-j2, "InplaceTranspose error: incorrect array size!"); - for(i=i1; i<=i2-1; i++) - { - j = j1+i-i1; - ips = i+1; - jps = j1+ips-i1; - l = i2-i; - i1_ = (ips) - (1); - for(i_=1; i_<=l;i_++) - { - work[i_] = a[i_+i1_,j]; - } - i1_ = (jps) - (ips); - for(i_=ips; i_<=i2;i_++) - { - a[i_,j] = a[i,i_+i1_]; - } - i1_ = (1) - (jps); - for(i_=jps; i_<=j2;i_++) - { - a[i,i_] = work[i_+i1_]; - } - } - } - - - public static void copyandtranspose(double[,] a, - int is1, - int is2, - int js1, - int js2, - ref double[,] b, - int id1, - int id2, - int jd1, - int jd2, - alglib.xparams _params) - { - int isrc = 0; - int jdst = 0; + double v = 0; + double v00 = 0; + double v01 = 0; + double v02 = 0; + double v03 = 0; + double v10 = 0; + double v11 = 0; + double v12 = 0; + double v13 = 0; + double v20 = 0; + double v21 = 0; + double v22 = 0; + double v23 = 0; + double v30 = 0; + double v31 = 0; + double v32 = 0; + double v33 = 0; + double a0 = 0; + double a1 = 0; + double a2 = 0; + double a3 = 0; + double b0 = 0; + double b1 = 0; + double b2 = 0; + double b3 = 0; + int idxa0 = 0; + int idxa1 = 0; + int idxa2 = 0; + int idxa3 = 0; + int idxb0 = 0; + int idxb1 = 0; + int idxb2 = 0; + int idxb3 = 0; + int i0 = 0; + int i1 = 0; + int ik = 0; + int j0 = 0; + int j1 = 0; + int jk = 0; + int t = 0; + int offsa = 0; + int offsb = 0; int i_ = 0; int i1_ = 0; - if( is1>is2 || js1>js2 ) + alglib.ap.assert((double)(alpha)!=(double)(0), "RMatrixGEMMK44V00: internal error (Alpha=0)"); + + // + // if matrix size is zero + // + if( m==0 || n==0 ) { return; } - alglib.ap.assert(is2-is1==jd2-jd1, "CopyAndTranspose: different sizes!"); - alglib.ap.assert(js2-js1==id2-id1, "CopyAndTranspose: different sizes!"); - for(isrc=is1; isrc<=is2; isrc++) + + // + // A'*B' + // + i = 0; + while( ii2 || j1>j2 ) - { - return; - } - alglib.ap.assert(j2-j1==ix2-ix1, "MatrixVectorMultiply: A and X dont match!"); - alglib.ap.assert(i2-i1==iy2-iy1, "MatrixVectorMultiply: A and Y dont match!"); - - // - // beta*y - // - if( (double)(beta)==(double)(0) ) - { - for(i=iy1; i<=iy2; i++) - { - y[i] = 0; - } - } - else - { - for(i_=iy1; i_<=iy2;i_++) - { - y[i_] = beta*y[i_]; - } - } - - // - // alpha*A*x - // - for(i=i1; i<=i2; i++) - { - i1_ = (ix1)-(j1); - v = 0.0; - for(i_=j1; i_<=j2;i_++) - { - v += a[i,i_]*x[i_+i1_]; - } - y[iy1+i-i1] = y[iy1+i-i1]+alpha*v; - } - } - else - { - - // - // y := alpha*A'*x + beta*y; - // - if( i1>i2 || j1>j2 ) - { - return; - } - alglib.ap.assert(i2-i1==ix2-ix1, "MatrixVectorMultiply: A and X dont match!"); - alglib.ap.assert(j2-j1==iy2-iy1, "MatrixVectorMultiply: A and Y dont match!"); - - // - // beta*y - // - if( (double)(beta)==(double)(0) ) - { - for(i=iy1; i<=iy2; i++) - { - y[i] = 0; - } - } - else - { - for(i_=iy1; i_<=iy2;i_++) + + // + // Choose between specialized 4x4 code and general code + // + if( i+4<=m && j+4<=n ) { - y[i_] = beta*y[i_]; + + // + // Specialized 4x4 code for [I..I+3]x[J..J+3] submatrix of C. + // + // This submatrix is calculated as sum of K rank-1 products, + // with operands cached in local variables in order to speed + // up operations with arrays. + // + idxa0 = ja+i+0; + idxa1 = ja+i+1; + idxa2 = ja+i+2; + idxa3 = ja+i+3; + offsa = ia; + idxb0 = ib+j+0; + idxb1 = ib+j+1; + idxb2 = ib+j+2; + idxb3 = ib+j+3; + offsb = jb; + v00 = 0.0; + v01 = 0.0; + v02 = 0.0; + v03 = 0.0; + v10 = 0.0; + v11 = 0.0; + v12 = 0.0; + v13 = 0.0; + v20 = 0.0; + v21 = 0.0; + v22 = 0.0; + v23 = 0.0; + v30 = 0.0; + v31 = 0.0; + v32 = 0.0; + v33 = 0.0; + for(t=0; t<=k-1; t++) + { + a0 = a[offsa,idxa0]; + a1 = a[offsa,idxa1]; + b0 = b[idxb0,offsb]; + b1 = b[idxb1,offsb]; + v00 = v00+a0*b0; + v01 = v01+a0*b1; + v10 = v10+a1*b0; + v11 = v11+a1*b1; + a2 = a[offsa,idxa2]; + a3 = a[offsa,idxa3]; + v20 = v20+a2*b0; + v21 = v21+a2*b1; + v30 = v30+a3*b0; + v31 = v31+a3*b1; + b2 = b[idxb2,offsb]; + b3 = b[idxb3,offsb]; + v22 = v22+a2*b2; + v23 = v23+a2*b3; + v32 = v32+a3*b2; + v33 = v33+a3*b3; + v02 = v02+a0*b2; + v03 = v03+a0*b3; + v12 = v12+a1*b2; + v13 = v13+a1*b3; + offsa = offsa+1; + offsb = offsb+1; + } + if( (double)(beta)==(double)(0) ) + { + c[ic+i+0,jc+j+0] = alpha*v00; + c[ic+i+0,jc+j+1] = alpha*v01; + c[ic+i+0,jc+j+2] = alpha*v02; + c[ic+i+0,jc+j+3] = alpha*v03; + c[ic+i+1,jc+j+0] = alpha*v10; + c[ic+i+1,jc+j+1] = alpha*v11; + c[ic+i+1,jc+j+2] = alpha*v12; + c[ic+i+1,jc+j+3] = alpha*v13; + c[ic+i+2,jc+j+0] = alpha*v20; + c[ic+i+2,jc+j+1] = alpha*v21; + c[ic+i+2,jc+j+2] = alpha*v22; + c[ic+i+2,jc+j+3] = alpha*v23; + c[ic+i+3,jc+j+0] = alpha*v30; + c[ic+i+3,jc+j+1] = alpha*v31; + c[ic+i+3,jc+j+2] = alpha*v32; + c[ic+i+3,jc+j+3] = alpha*v33; + } + else + { + c[ic+i+0,jc+j+0] = beta*c[ic+i+0,jc+j+0]+alpha*v00; + c[ic+i+0,jc+j+1] = beta*c[ic+i+0,jc+j+1]+alpha*v01; + c[ic+i+0,jc+j+2] = beta*c[ic+i+0,jc+j+2]+alpha*v02; + c[ic+i+0,jc+j+3] = beta*c[ic+i+0,jc+j+3]+alpha*v03; + c[ic+i+1,jc+j+0] = beta*c[ic+i+1,jc+j+0]+alpha*v10; + c[ic+i+1,jc+j+1] = beta*c[ic+i+1,jc+j+1]+alpha*v11; + c[ic+i+1,jc+j+2] = beta*c[ic+i+1,jc+j+2]+alpha*v12; + c[ic+i+1,jc+j+3] = beta*c[ic+i+1,jc+j+3]+alpha*v13; + c[ic+i+2,jc+j+0] = beta*c[ic+i+2,jc+j+0]+alpha*v20; + c[ic+i+2,jc+j+1] = beta*c[ic+i+2,jc+j+1]+alpha*v21; + c[ic+i+2,jc+j+2] = beta*c[ic+i+2,jc+j+2]+alpha*v22; + c[ic+i+2,jc+j+3] = beta*c[ic+i+2,jc+j+3]+alpha*v23; + c[ic+i+3,jc+j+0] = beta*c[ic+i+3,jc+j+0]+alpha*v30; + c[ic+i+3,jc+j+1] = beta*c[ic+i+3,jc+j+1]+alpha*v31; + c[ic+i+3,jc+j+2] = beta*c[ic+i+3,jc+j+2]+alpha*v32; + c[ic+i+3,jc+j+3] = beta*c[ic+i+3,jc+j+3]+alpha*v33; + } } - } - - // - // alpha*A'*x - // - for(i=i1; i<=i2; i++) - { - v = alpha*x[ix1+i-i1]; - i1_ = (j1) - (iy1); - for(i_=iy1; i_<=iy2;i_++) + else { - y[i_] = y[i_] + v*a[i,i_+i1_]; + + // + // Determine submatrix [I0..I1]x[J0..J1] to process + // + i0 = i; + i1 = Math.Min(i+3, m-1); + j0 = j; + j1 = Math.Min(j+3, n-1); + + // + // Process submatrix + // + for(ik=i0; ik<=i1; ik++) + { + for(jk=j0; jk<=j1; jk++) + { + if( k==0 || (double)(alpha)==(double)(0) ) + { + v = 0; + } + else + { + v = 0.0; + i1_ = (jb)-(ia); + v = 0.0; + for(i_=ia; i_<=ia+k-1;i_++) + { + v += a[i_,ja+ik]*b[ib+jk,i_+i1_]; + } + } + if( (double)(beta)==(double)(0) ) + { + c[ic+ik,jc+jk] = alpha*v; + } + else + { + c[ic+ik,jc+jk] = beta*c[ic+ik,jc+jk]+alpha*v; + } + } + } } + j = j+4; } + i = i+4; } } - public static double pythag2(double x, - double y, + /************************************************************************* + Internal function that actually works with dynamic arrays. + + -- ALGLIB -- + Copyright 07.06.2023 by Bochkanov Sergey + *************************************************************************/ + private static void igrowvinternal(int newn, + ref int[] x, alglib.xparams _params) { - double result = 0; - double w = 0; - double xabs = 0; - double yabs = 0; - double z = 0; + int[] oldx = new int[0]; + int oldn = 0; - xabs = Math.Abs(x); - yabs = Math.Abs(y); - w = Math.Max(xabs, yabs); - z = Math.Min(xabs, yabs); - if( (double)(z)==(double)(0) ) + if( alglib.ap.len(x)>=newn ) { - result = w; + return; } - else + oldn = alglib.ap.len(x); + newn = Math.Max(newn, (int)Math.Round(1.8*oldn+1)); + alglib.ap.swap(ref x, ref oldx); + x = new int[newn]; + icopyv(oldn, oldx, x, _params); + } + + + /************************************************************************* + Internal function that actually works with dynamic arrays. + + -- ALGLIB -- + Copyright 07.06.2023 by Bochkanov Sergey + *************************************************************************/ + private static void bgrowvinternal(int newn, + ref bool[] x, + alglib.xparams _params) + { + bool[] oldx = new bool[0]; + int oldn = 0; + + if( alglib.ap.len(x)>=newn ) { - result = w*Math.Sqrt(1+math.sqr(z/w)); + return; } - return result; + oldn = alglib.ap.len(x); + newn = Math.Max(newn, (int)Math.Round(1.8*oldn+1)); + alglib.ap.swap(ref x, ref oldx); + x = new bool[newn]; + bcopyv(oldn, oldx, x, _params); } - public static void matrixmatrixmultiply(double[,] a, - int ai1, - int ai2, - int aj1, - int aj2, - bool transa, - double[,] b, - int bi1, - int bi2, - int bj1, - int bj2, - bool transb, + /************************************************************************* + Internal function which actually works with dynamic arrays + + -- ALGLIB -- + Copyright 07.06.2023 by Bochkanov Sergey + *************************************************************************/ + private static void rgrowvinternal(int newn, + ref double[] x, + alglib.xparams _params) + { + double[] oldx = new double[0]; + int oldn = 0; + + if( alglib.ap.len(x)>=newn ) + { + return; + } + oldn = alglib.ap.len(x); + newn = Math.Max(newn, (int)Math.Round(1.8*oldn+1)); + alglib.ap.swap(ref x, ref oldx); + x = new double[newn]; + rcopyv(oldn, oldx, x, _params); + } + + + /************************************************************************* + Internal function which actually works with dynamic arrays. We need it to + be a separate function in order to minimize penalty associated with maintaining + a local dynamically allocated variable. + + -- ALGLIB -- + Copyright 20.03.2009 by Bochkanov Sergey + *************************************************************************/ + private static void rincreaserowsfixedcolsminternal(int newrows, + ref double[,] a, + alglib.xparams _params) + { + double[,] olda = new double[0,0]; + + alglib.ap.swap(ref a, ref olda); + a = new double[(int)Math.Round(Math.Max(1.8*alglib.ap.rows(olda)+8, 1.25*newrows)), alglib.ap.cols(olda)]; + rcopym(alglib.ap.rows(olda), alglib.ap.cols(olda), olda, a, _params); + } + + + #if ALGLIB_NO_FAST_KERNELS + /************************************************************************* + Fast kernel (new version with AVX2/SSE2) + + -- ALGLIB routine -- + 19.01.2010 + Bochkanov Sergey + *************************************************************************/ + private static bool rgemm32basecase(int m, + int n, + int k, double alpha, - ref double[,] c, - int ci1, - int ci2, - int cj1, - int cj2, + double[,] a, + int ia, + int ja, + int optypea, + double[,] b, + int ib, + int jb, + int optypeb, double beta, - ref double[] work, + double[,] c, + int ic, + int jc, + alglib.xparams _params) + { + bool result = new bool(); + + result = false; + return result; + } + #endif + + + } + public partial class rotations + { + /************************************************************************* + Application of a sequence of elementary rotations to a matrix + + The algorithm pre-multiplies the matrix by a sequence of rotation + transformations which is given by arrays C and S. Depending on the value + of the IsForward parameter either 1 and 2, 3 and 4 and so on (if IsForward=true) + rows are rotated, or the rows N and N-1, N-2 and N-3 and so on, are rotated. + + Not the whole matrix but only a part of it is transformed (rows from M1 to + M2, columns from N1 to N2). Only the elements of this submatrix are changed. + + Input parameters: + IsForward - the sequence of the rotation application. + M1,M2 - the range of rows to be transformed. + N1, N2 - the range of columns to be transformed. + C,S - transformation coefficients. + Array whose index ranges within [1..M2-M1]. + A - processed matrix. + WORK - working array whose index ranges within [N1..N2]. + + Output parameters: + A - transformed matrix. + + Utility subroutine. + *************************************************************************/ + public static void applyrotationsfromtheleft(bool isforward, + int m1, + int m2, + int n1, + int n2, + double[] c, + double[] s, + double[,] a, + double[] work, alglib.xparams _params) { - int arows = 0; - int acols = 0; - int brows = 0; - int bcols = 0; - int crows = 0; - int i = 0; int j = 0; - int k = 0; - int l = 0; - int r = 0; - double v = 0; + int jp1 = 0; + double ctemp = 0; + double stemp = 0; + double temp = 0; int i_ = 0; - int i1_ = 0; - - // - // Setup - // - if( !transa ) - { - arows = ai2-ai1+1; - acols = aj2-aj1+1; - } - else - { - arows = aj2-aj1+1; - acols = ai2-ai1+1; - } - if( !transb ) - { - brows = bi2-bi1+1; - bcols = bj2-bj1+1; - } - else - { - brows = bj2-bj1+1; - bcols = bi2-bi1+1; - } - alglib.ap.assert(acols==brows, "MatrixMatrixMultiply: incorrect matrix sizes!"); - if( ((arows<=0 || acols<=0) || brows<=0) || bcols<=0 ) + if( m1>m2 || n1>n2 ) { return; } - crows = arows; - - // - // Test WORK - // - i = Math.Max(arows, acols); - i = Math.Max(brows, i); - i = Math.Max(i, bcols); - work[1] = 0; - work[i] = 0; // - // Prepare C + // Form P * A // - if( (double)(beta)==(double)(0) ) + if( isforward ) { - for(i=ci1; i<=ci2; i++) + if( n1!=n2 ) { - for(j=cj1; j<=cj2; j++) + + // + // Common case: N1<>N2 + // + for(j=m1; j<=m2-1; j++) { - c[i,j] = 0; - } - } - } - else - { - for(i=ci1; i<=ci2; i++) - { - for(i_=cj1; i_<=cj2;i_++) - { - c[i,i_] = beta*c[i,i_]; - } - } - } - - // - // A*B - // - if( !transa && !transb ) - { - for(l=ai1; l<=ai2; l++) - { - for(r=bi1; r<=bi2; r++) - { - v = alpha*a[l,aj1+r-bi1]; - k = ci1+l-ai1; - i1_ = (bj1) - (cj1); - for(i_=cj1; i_<=cj2;i_++) - { - c[k,i_] = c[k,i_] + v*b[r,i_+i1_]; - } - } - } - return; - } - - // - // A*B' - // - if( !transa && transb ) - { - if( arows*acolsm2 || n1>n2 ) - { - return; - } - - // - // Form P * A - // - if( isforward ) - { - if( n1!=n2 ) - { - - // - // Common case: N1<>N2 - // - for(j=m1; j<=m2-1; j++) - { - ctemp = c[j-m1+1]; - stemp = s[j-m1+1]; - if( (double)(ctemp)!=(double)(1) || (double)(stemp)!=(double)(0) ) - { - jp1 = j+1; - for(i_=n1; i_<=n2;i_++) - { - work[i_] = ctemp*a[jp1,i_]; - } - for(i_=n1; i_<=n2;i_++) - { - work[i_] = work[i_] - stemp*a[j,i_]; - } - for(i_=n1; i_<=n2;i_++) - { - a[j,i_] = ctemp*a[j,i_]; - } - for(i_=n1; i_<=n2;i_++) - { - a[j,i_] = a[j,i_] + stemp*a[jp1,i_]; - } - for(i_=n1; i_<=n2;i_++) - { - a[jp1,i_] = work[i_]; - } - } + ctemp = c[j-m1+1]; + stemp = s[j-m1+1]; + if( ctemp!=1 || stemp!=0 ) + { + jp1 = j+1; + for(i_=n1; i_<=n2;i_++) + { + work[i_] = ctemp*a[jp1,i_]; + } + for(i_=n1; i_<=n2;i_++) + { + work[i_] = work[i_] - stemp*a[j,i_]; + } + for(i_=n1; i_<=n2;i_++) + { + a[j,i_] = ctemp*a[j,i_]; + } + for(i_=n1; i_<=n2;i_++) + { + a[j,i_] = a[j,i_] + stemp*a[jp1,i_]; + } + for(i_=n1; i_<=n2;i_++) + { + a[jp1,i_] = work[i_]; + } + } } } else @@ -14428,7 +14055,7 @@ public static void applyrotationsfromtheleft(bool isforward, { ctemp = c[j-m1+1]; stemp = s[j-m1+1]; - if( (double)(ctemp)!=(double)(1) || (double)(stemp)!=(double)(0) ) + if( ctemp!=1 || stemp!=0 ) { temp = a[j+1,n1]; a[j+1,n1] = ctemp*temp-stemp*a[j,n1]; @@ -14449,7 +14076,7 @@ public static void applyrotationsfromtheleft(bool isforward, { ctemp = c[j-m1+1]; stemp = s[j-m1+1]; - if( (double)(ctemp)!=(double)(1) || (double)(stemp)!=(double)(0) ) + if( ctemp!=1 || stemp!=0 ) { jp1 = j+1; for(i_=n1; i_<=n2;i_++) @@ -14485,7 +14112,7 @@ public static void applyrotationsfromtheleft(bool isforward, { ctemp = c[j-m1+1]; stemp = s[j-m1+1]; - if( (double)(ctemp)!=(double)(1) || (double)(stemp)!=(double)(0) ) + if( ctemp!=1 || stemp!=0 ) { temp = a[j+1,n1]; a[j+1,n1] = ctemp*temp-stemp*a[j,n1]; @@ -14678,12 +14305,15 @@ public static void generaterotation(double f, { double f1 = 0; double g1 = 0; + double af1 = 0; + double ag1 = 0; + double v = 0; cs = 0; sn = 0; r = 0; - if( (double)(g)==(double)(0) ) + if( g==0.0 ) { cs = 1; sn = 0; @@ -14691,7 +14321,7 @@ public static void generaterotation(double f, } else { - if( (double)(f)==(double)(0) ) + if( f==0.0 ) { cs = 0; sn = 1; @@ -14701,17 +14331,21 @@ public static void generaterotation(double f, { f1 = f; g1 = g; - if( (double)(Math.Abs(f1))>(double)(Math.Abs(g1)) ) + af1 = Math.Abs(f1); + ag1 = Math.Abs(g1); + if( af1>ag1 ) { - r = Math.Abs(f1)*Math.Sqrt(1+math.sqr(g1/f1)); + v = g1/f1; + r = af1*Math.Sqrt(1+v*v); } else { - r = Math.Abs(g1)*Math.Sqrt(1+math.sqr(f1/g1)); + v = f1/g1; + r = ag1*Math.Sqrt(1+v*v); } cs = f1/r; sn = g1/r; - if( (double)(Math.Abs(f))>(double)(Math.Abs(g)) && (double)(cs)<(double)(0) ) + if( af1>ag1 && cs<0 ) { cs = -cs; sn = -sn; @@ -14723,7667 +14357,8539 @@ public static void generaterotation(double f, } - public class basicstatops + public partial class ablaspbl { /************************************************************************* - Internal tied ranking subroutine. + PBL-based kernel - 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). + -- ALGLIB routine -- + 12.10.2017 + Bochkanov Sergey *************************************************************************/ - public static void rankx(double[] x, + public static bool rmatrixgerpbl(int m, int n, - bool iscentered, - apserv.apbuffers buf, + double[,] a, + int ia, + int ja, + double alpha, + double[] u, + int iu, + double[] v, + int iv, alglib.xparams _params) { - int i = 0; - int j = 0; - int k = 0; - double tmp = 0; - double voffs = 0; + bool result = new bool(); - - // - // Prepare - // - if( n<1 ) - { - return; - } - if( n==1 ) - { - x[0] = 0; - return; - } - if( alglib.ap.len(buf.ra1)=0 and stores position in Items[] of element - I, i.e. Items[LocationOf[I]]=I. - If item I is not present, LocationOf[I]<0. + -- ALGLIB routine -- + 12.10.2017 + Bochkanov Sergey *************************************************************************/ - public class niset : apobject + public static bool rmatrixrank1pbl(int m, + int n, + double[,] a, + int ia, + int ja, + double[] u, + int iu, + double[] v, + int iv, + alglib.xparams _params) { - public int n; - public int nstored; - public int[] items; - public int[] locationof; - public int iteridx; - public niset() - { - init(); - } - public override void init() - { - items = new int[0]; - locationof = new int[0]; - } - public override alglib.apobject make_copy() - { - niset _result = new niset(); - _result.n = n; - _result.nstored = nstored; - _result.items = (int[])items.Clone(); - _result.locationof = (int[])locationof.Clone(); - _result.iteridx = iteridx; - return _result; - } - }; + bool result = new bool(); + + result = false; + return result; + } /************************************************************************* - This structure is used to store K sets of N possible integers, in [0,N) each. - The structure needs at least O(N) temporary memory. + PBL-based kernel - Storage modes: - * 0 default unsorted mode + -- ALGLIB routine -- + 12.10.2017 + Bochkanov Sergey *************************************************************************/ - public class kniset : apobject + public static bool cmatrixmvpbl(int m, + int n, + complex[,] a, + int ia, + int ja, + int opa, + complex[] x, + int ix, + complex[] y, + int iy, + alglib.xparams _params) { - public int storagemode; - public int k; - public int n; - public int[] flagarray; - public int[] vbegin; - public int[] vallocated; - public int[] vcnt; - public int[] data; - public int dataused; - public int iterrow; - public int iteridx; - public kniset() - { - init(); - } - public override void init() - { - flagarray = new int[0]; - vbegin = new int[0]; - vallocated = new int[0]; - vcnt = new int[0]; - data = new int[0]; - } - public override alglib.apobject make_copy() - { - kniset _result = new kniset(); - _result.storagemode = storagemode; - _result.k = k; - _result.n = n; - _result.flagarray = (int[])flagarray.Clone(); - _result.vbegin = (int[])vbegin.Clone(); - _result.vallocated = (int[])vallocated.Clone(); - _result.vcnt = (int[])vcnt.Clone(); - _result.data = (int[])data.Clone(); - _result.dataused = dataused; - _result.iterrow = iterrow; - _result.iteridx = iteridx; - return _result; - } - }; - + bool result = new bool(); + result = false; + return result; + } - public const int knisheadersize = 2; + /************************************************************************* + PBL-based kernel + -- ALGLIB routine -- + 12.10.2017 + Bochkanov Sergey + *************************************************************************/ + public static bool rmatrixmvpbl(int m, + int n, + double[,] a, + int ia, + int ja, + int opa, + double[] x, + int ix, + double[] y, + int iy, + alglib.xparams _params) + { + bool result = new bool(); - /************************************************************************* - Initializes n-set by empty structure. + result = false; + return result; + } - IMPORTANT: this function need O(N) time for initialization. It is recommended - to reduce its usage as much as possible, and use nisClear() - where possible. - INPUT PARAMETERS - N - possible set size - - OUTPUT PARAMETERS - SA - empty N-set + /************************************************************************* + PBL-based kernel - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 12.10.2017 + Bochkanov Sergey *************************************************************************/ - public static void nisinitemptyslow(int n, - niset sa, + public static bool rmatrixgemvpbl(int m, + int n, + double alpha, + double[,] a, + int ia, + int ja, + int opa, + double[] x, + int ix, + double beta, + double[] y, + int iy, alglib.xparams _params) { - sa.n = n; - sa.nstored = 0; - ablasf.isetallocv(n, -999999999, ref sa.locationof, _params); - ablasf.isetallocv(n, -999999999, ref sa.items, _params); + bool result = new bool(); + + result = false; + return result; } /************************************************************************* - Copies n-set to properly initialized target set. The target set has to be - properly initialized, and it can be non-empty. If it is non-empty, its - contents is quickly erased before copying. - - The cost of this function is O(max(SrcSize,DstSize)) - - INPUT PARAMETERS - SSrc - source N-set - SDst - destination N-set (has same size as SSrc) - - OUTPUT PARAMETERS - SDst - copy of SSrc + PBL kernel - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 12.10.2017 + Bochkanov Sergey *************************************************************************/ - public static void niscopy(niset ssrc, - niset sdst, + public static bool rmatrixtrsvpbl(int n, + double[,] a, + int ia, + int ja, + bool isupper, + bool isunit, + int optype, + double[] x, + int ix, alglib.xparams _params) { - int ns = 0; - int i = 0; - int k = 0; + bool result = new bool(); - nisclear(sdst, _params); - ns = ssrc.nstored; - for(i=0; i<=ns-1; i++) - { - k = ssrc.items[i]; - sdst.items[i] = k; - sdst.locationof[k] = i; - } - sdst.nstored = ns; + result = false; + return result; } /************************************************************************* - Add K-th element to the set. The element may already exist in the set. - - INPUT PARAMETERS - SA - set - K - element to add, 0<=K=0 ) - { - return; - } - ns = sa.nstored; - sa.locationof[k] = ns; - sa.items[ns] = k; - sa.nstored = ns+1; + result = false; + return result; } /************************************************************************* - Subtracts K-th set from the source structure - - INPUT PARAMETERS - SA - set - Src, K - source kn-set and set index K - - OUTPUT PARAMETERS - SA - modified SA + PBL-based kernel - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 01.10.2013 + Bochkanov Sergey *************************************************************************/ - public static void nissubtract1(niset sa, - niset src, + public static bool cmatrixherkpbl(int n, + int k, + double alpha, + complex[,] a, + int ia, + int ja, + int optypea, + double beta, + complex[,] c, + int ic, + int jc, + bool isupper, alglib.xparams _params) { - int i = 0; - int j = 0; - int loc = 0; - int item = 0; - int ns = 0; - int ss = 0; + bool result = new bool(); - ns = sa.nstored; - ss = src.nstored; - if( ss=0 ) - { - item = sa.items[ns-1]; - sa.items[loc] = item; - sa.locationof[item] = loc; - sa.locationof[j] = -1; - ns = ns-1; - } - } - } - else - { - i = 0; - while( i=0 ) - { - item = sa.items[ns-1]; - sa.items[i] = item; - sa.locationof[item] = i; - sa.locationof[j] = -1; - ns = ns-1; - } - else - { - i = i+1; - } - } - } - sa.nstored = ns; + result = false; + return result; } /************************************************************************* - Clears set - - INPUT PARAMETERS - SA - set to be cleared - + PBL-based kernel - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 01.10.2013 + Bochkanov Sergey *************************************************************************/ - public static void nisclear(niset sa, + public static bool rmatrixgemmpbl(int m, + int n, + int k, + double alpha, + double[,] a, + int ia, + int ja, + int optypea, + double[,] b, + int ib, + int jb, + int optypeb, + double beta, + double[,] c, + int ic, + int jc, alglib.xparams _params) { - int i = 0; - int ns = 0; + bool result = new bool(); - ns = sa.nstored; - for(i=0; i<=ns-1; i++) - { - sa.locationof[sa.items[i]] = -1; - } - sa.nstored = 0; + result = false; + return result; } /************************************************************************* - Counts set elements - - INPUT PARAMETERS - SA - set - - RESULT - number of elements in SA + PBL-based kernel - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 01.10.2017 + Bochkanov Sergey *************************************************************************/ - public static int niscount(niset sa, + public static bool rmatrixsymvpbl(int n, + double alpha, + double[,] a, + int ia, + int ja, + bool isupper, + double[] x, + int ix, + double beta, + double[] y, + int iy, alglib.xparams _params) { - int result = 0; + bool result = new bool(); - result = sa.nstored; + result = false; return result; } /************************************************************************* - Compare two sets, returns True for equal sets - - INPUT PARAMETERS - S0 - set 0 - S1 - set 1, must have same parameter N as set 0 - - RESULT - True, if sets are equal + PBL-based kernel - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 16.10.2014 + Bochkanov Sergey *************************************************************************/ - public static bool nisequal(niset s0, - niset s1, + public static bool cmatrixgemmpbl(int m, + int n, + int k, + complex alpha, + complex[,] a, + int ia, + int ja, + int optypea, + complex[,] b, + int ib, + int jb, + int optypeb, + complex beta, + complex[,] c, + int ic, + int jc, alglib.xparams _params) { bool result = new bool(); - int i = 0; - int ns0 = 0; - int ns1 = 0; result = false; - if( s0.n!=s1.n ) - { - return result; - } - if( s0.nstored!=s1.nstored ) - { - return result; - } - ns0 = s0.nstored; - ns1 = s1.nstored; - for(i=0; i<=ns0-1; i++) - { - if( s1.locationof[s0.items[i]]<0 ) - { - return result; - } - } - for(i=0; i<=ns1-1; i++) - { - if( s0.locationof[s1.items[i]]<0 ) - { - return result; - } - } - result = true; return result; } /************************************************************************* - Prepares iteration over set - - INPUT PARAMETERS - SA - set - - OUTPUT PARAMETERS - SA - SA ready for repeated calls of nisEnumerate() + PBL-based kernel - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 16.10.2014 + Bochkanov Sergey *************************************************************************/ - public static void nisstartenumeration(niset sa, + public static bool cmatrixlefttrsmpbl(int m, + int n, + complex[,] a, + int i1, + int j1, + bool isupper, + bool isunit, + int optype, + complex[,] x, + int i2, + int j2, alglib.xparams _params) { - sa.iteridx = 0; + bool result = new bool(); + + result = false; + return result; } /************************************************************************* - Iterates over the set. Subsequent calls return True and set J to new set - item until iteration stops and False is returned. + PBL-based kernel - INPUT PARAMETERS - SA - n-set - - OUTPUT PARAMETERS - J - if: - * Result=True - index of element in the set - * Result=False - not set + -- ALGLIB routine -- + 16.10.2014 + Bochkanov Sergey + *************************************************************************/ + public static bool cmatrixrighttrsmpbl(int m, + int n, + complex[,] a, + int i1, + int j1, + bool isupper, + bool isunit, + int optype, + complex[,] x, + int i2, + int j2, + alglib.xparams _params) + { + bool result = new bool(); + + result = false; + return result; + } - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + /************************************************************************* + PBL-based kernel + + -- ALGLIB routine -- + 16.10.2014 + Bochkanov Sergey *************************************************************************/ - public static bool nisenumerate(niset sa, - ref int i, + public static bool rmatrixlefttrsmpbl(int m, + int n, + double[,] a, + int i1, + int j1, + bool isupper, + bool isunit, + int optype, + double[,] x, + int i2, + int j2, alglib.xparams _params) { bool result = new bool(); - int k = 0; - - i = 0; - k = sa.iteridx; - if( k>=sa.nstored ) - { - result = false; - return result; - } - i = sa.items[k]; - sa.iteridx = k+1; - result = true; + result = false; return result; } /************************************************************************* - Compresses internal storage, reclaiming previously dropped blocks. To be - used internally by kn-set modification functions. - - INPUT PARAMETERS - SA - kn-set to compress + PBL-based kernel - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 16.10.2014 + Bochkanov Sergey *************************************************************************/ - public static void kniscompressstorage(kniset sa, + public static bool rmatrixrighttrsmpbl(int m, + int n, + double[,] a, + int i1, + int j1, + bool isupper, + bool isunit, + int optype, + double[,] x, + int i2, + int j2, alglib.xparams _params) { - int i = 0; - int blocklen = 0; - int setidx = 0; - int srcoffs = 0; - int dstoffs = 0; + bool result = new bool(); - alglib.ap.assert(sa.storagemode==0, "knisCompressStorage: unexpected storage mode"); - srcoffs = 0; - dstoffs = 0; - while( srcoffs=knisheadersize, "knisCompressStorage: integrity check 6385 failed"); - if( setidx<0 ) - { - srcoffs = srcoffs+blocklen; - continue; - } - if( srcoffs!=dstoffs ) - { - for(i=0; i<=blocklen-1; i++) - { - sa.data[dstoffs+i] = sa.data[srcoffs+i]; - } - sa.vbegin[setidx] = dstoffs+knisheadersize; - } - dstoffs = dstoffs+blocklen; - srcoffs = srcoffs+blocklen; - } - alglib.ap.assert(srcoffs==sa.dataused, "knisCompressStorage: integrity check 9464 failed"); - sa.dataused = dstoffs; + result = false; + return result; } /************************************************************************* - Reallocates internal storage for set #SetIdx, increasing its capacity to - NewAllocated exactly. This function may invalidate internal pointers for - ALL sets in the kn-set structure because it may perform storage - compression in order to reclaim previously freed space. + PBL-based kernel. - INPUT PARAMETERS - SA - kn-set structure - SetIdx - set to reallocate - NewAllocated - new size for the set, must be at least equal to already - allocated + NOTE: - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + if function returned False, CholResult is NOT modified. Not ever referenced! + if function returned True, CholResult is set to status of Cholesky decomposition + (True on succeess). + + -- ALGLIB routine -- + 16.10.2014 + Bochkanov Sergey *************************************************************************/ - public static void knisreallocate(kniset sa, - int setidx, - int newallocated, + public static bool spdmatrixcholeskypbl(double[,] a, + int offs, + int n, + bool isupper, + ref bool cholresult, alglib.xparams _params) { - int oldbegin = 0; - int oldcnt = 0; - int newbegin = 0; - int j = 0; + bool result = new bool(); - alglib.ap.assert(sa.storagemode==0, "knisReallocate: unexpected storage mode"); - if( alglib.ap.len(sa.data)0 - N - set size, N>=0 - kPrealloc - preallocate place per set (can be zero), >=0 - - OUTPUT PARAMETERS - SA - K sets of N elements, initially empty + PBL-based kernel. - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey *************************************************************************/ - public static void knisinitunsorted(int k, + public static bool rmatrixplupbl(double[,] a, + int offs, + int m, int n, - int kprealloc, - kniset sa, + ref int[] pivots, alglib.xparams _params) { - int i = 0; + bool result = new bool(); - alglib.ap.assert(k>0, "knisInitUnsorted: K<=0"); - alglib.ap.assert(n>=0, "knisInitUnsorted: N<0"); - alglib.ap.assert(kprealloc>=0, "knisInitUnsorted: kPrealloc<0"); - sa.storagemode = 0; - sa.k = k; - sa.n = n; - ablasf.isetallocv(n, -1, ref sa.flagarray, _params); - ablasf.isetallocv(k, kprealloc, ref sa.vallocated, _params); - apserv.ivectorsetlengthatleast(ref sa.vbegin, k, _params); - sa.vbegin[0] = knisheadersize; - for(i=1; i<=k-1; i++) - { - sa.vbegin[i] = sa.vbegin[i-1]+sa.vallocated[i-1]+knisheadersize; - } - sa.dataused = sa.vbegin[k-1]+sa.vallocated[k-1]; - apserv.ivectorsetlengthatleast(ref sa.data, sa.dataused, _params); - for(i=0; i<=k-1; i++) - { - sa.data[sa.vbegin[i]-2] = knisheadersize+sa.vallocated[i]; - sa.data[sa.vbegin[i]-1] = i; - } - ablasf.isetallocv(k, 0, ref sa.vcnt, _params); + result = false; + return result; } /************************************************************************* - Allows direct access to internal storage of kn-set structure - returns - range of elements SA.Data[idxBegin...idxEnd-1] used to store K-th set - - INPUT PARAMETERS - SA - kn-set - K - set index - - OUTPUT PARAMETERS - idxBegin, - idxEnd - half-range [idxBegin,idxEnd) of SA.Data that stores - K-th set + PBL-based kernel. + NOTE: this function needs preallocated output/temporary arrays. + D and E must be at least max(M,N)-wide. - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey *************************************************************************/ - public static void knisdirectaccess(kniset sa, - int k, - ref int idxbegin, - ref int idxend, + public static bool rmatrixbdpbl(double[,] a, + int m, + int n, + double[] d, + double[] e, + double[] tauq, + double[] taup, alglib.xparams _params) { - idxbegin = 0; - idxend = 0; + bool result = new bool(); - alglib.ap.assert(sa.storagemode==0, "knisDirectAccess: unexpected storage mode"); - idxbegin = sa.vbegin[k]; - idxend = idxbegin+sa.vcnt[k]; + result = false; + return result; } /************************************************************************* - Pops last element from the K-th set. - - INPUT PARAMETERS - SA - kn-set; K-th set must include at least one element, - otherwise an exception is generated - K - set index - - RESULT: - last element in K-th set + PBL-based kernel. + If ByQ is True, TauP is not used (can be empty array). + If ByQ is False, TauQ is not used (can be empty array). - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey *************************************************************************/ - public static int knispoplast(kniset sa, - int k, + public static bool rmatrixbdmultiplybypbl(double[,] qp, + int m, + int n, + double[] tauq, + double[] taup, + double[,] z, + int zrows, + int zcolumns, + bool byq, + bool fromtheright, + bool dotranspose, alglib.xparams _params) { - int result = 0; - int c = 0; + bool result = new bool(); - alglib.ap.assert(sa.storagemode==0, "knisPopLast: unexpected storage mode"); - c = sa.vcnt[k]; - alglib.ap.assert(c>0, "knisDirectAccess: K-th set is empty"); - result = sa.data[sa.vbegin[k]+c-1]; - sa.vcnt[k] = c-1; + result = false; return result; } /************************************************************************* - Add K-th element to I-th set. If an element already exists in the target, - a duplicate entry is added. + PBL-based kernel. - INPUT PARAMETERS - SA - kn-set - I - set index - K - element to add - - OUTPUT PARAMETERS - SA - modified SA + NOTE: Tau must be preallocated array with at least N-1 elements. - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey *************************************************************************/ - public static void knisaddnewelement(kniset sa, - int i, - int k, + public static bool rmatrixhessenbergpbl(double[,] a, + int n, + double[] tau, alglib.xparams _params) { - int cnt = 0; + bool result = new bool(); - alglib.ap.assert(sa.storagemode==0, "knisAddNewElement: unexpected storage mode"); - cnt = sa.vcnt[i]; - if( cnt==sa.vallocated[i] ) - { - knisreallocate(sa, i, 2*sa.vallocated[i]+1, _params); - } - sa.data[sa.vbegin[i]+cnt] = k; - sa.vcnt[i] = cnt+1; + result = false; + return result; } /************************************************************************* - Clear k-th kn-set in collection. - - Freed memory is NOT reclaimed for future garbage collection. + PBL-based kernel. - INPUT PARAMETERS - SA - kn-set structure - K - set index - - OUTPUT PARAMETERS - SA - K-th set was cleared + NOTE: Q must be preallocated N*N array - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. - *************************************************************************/ - public static void knisclearkthnoreclaim(kniset sa, - int k, + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey + *************************************************************************/ + public static bool rmatrixhessenbergunpackqpbl(double[,] a, + int n, + double[] tau, + double[,] q, alglib.xparams _params) { - alglib.ap.assert(sa.storagemode==0, "knisClearKthNoReclaim: unexpected storage mode"); - sa.vcnt[k] = 0; + bool result = new bool(); + + result = false; + return result; } /************************************************************************* - Clear k-th kn-set in collection. - - Freed memory is reclaimed for future garbage collection. This function is - NOT recommended if you intend to add elements to this set in some future, - because every addition will result in reallocation of previously freed - memory. Use knsClearKthNoReclaim(). + PBL-based kernel. - INPUT PARAMETERS - SA - kn-set structure - K - set index - - OUTPUT PARAMETERS - SA - K-th set was cleared + NOTE: Tau, D, E must be preallocated arrays; + length(E)=length(Tau)=N-1 (or larger) + length(D)=N (or larger) - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey *************************************************************************/ - public static void knisclearkthreclaim(kniset sa, - int k, + public static bool smatrixtdpbl(double[,] a, + int n, + bool isupper, + double[] tau, + double[] d, + double[] e, alglib.xparams _params) { - int idxbegin = 0; - int allocated = 0; + bool result = new bool(); - alglib.ap.assert(sa.storagemode==0, "knisClearKthReclaim: unexpected storage mode"); - idxbegin = sa.vbegin[k]; - allocated = sa.vallocated[k]; - sa.vcnt[k] = 0; - if( allocated>=knisheadersize ) - { - sa.data[idxbegin-2] = 2; - sa.data[idxbegin+0] = allocated; - sa.data[idxbegin+1] = -1; - sa.vallocated[k] = 0; - } + result = false; + return result; } /************************************************************************* - Counts elements of K-th set of S0 (duplicates are counted as distinct elements). + PBL-based kernel. - INPUT PARAMETERS - S0 - kn-set structure - K - set index in the structure S0 - - RESULT - K-th set element count + NOTE: Q must be preallocated N*N array - -- ALGLIB PROJECT -- - Copyright 05.10.2020 by Bochkanov Sergey. + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey *************************************************************************/ - public static int kniscountkth(kniset s0, - int k, + public static bool smatrixtdunpackqpbl(double[,] a, + int n, + bool isupper, + double[] tau, + double[,] q, alglib.xparams _params) { - int result = 0; + bool result = new bool(); - alglib.ap.assert(s0.storagemode==0, "knisCountKth: unexpected storage mode"); - result = s0.vcnt[k]; + result = false; return result; } - } - public class trlinsolve - { /************************************************************************* - Utility subroutine performing the "safe" solution of system of linear - equations with triangular coefficient matrices. + PBL-based kernel. - The subroutine uses scaling and solves the scaled system A*x=s*b (where s - is a scalar value) instead of A*x=b, choosing s so that x can be - represented by a floating-point number. The closer the system gets to a - singular, the less s is. If the system is singular, s=0 and x contains the - non-trivial solution of equation A*x=0. + NOTE: Tau, D, E must be preallocated arrays; + length(E)=length(Tau)=N-1 (or larger) + length(D)=N (or larger) - The feature of an algorithm is that it could not cause an overflow or a - division by zero regardless of the matrix used as the input. + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey + *************************************************************************/ + public static bool hmatrixtdpbl(complex[,] a, + int n, + bool isupper, + complex[] tau, + double[] d, + double[] e, + alglib.xparams _params) + { + bool result = new bool(); - The algorithm can solve systems of equations with upper/lower triangular - matrices, with/without unit diagonal, and systems of type A*x=b or A'*x=b - (where A' is a transposed matrix A). + result = false; + return result; + } - Input parameters: - A - system matrix. Array whose indexes range within [0..N-1, 0..N-1]. - N - size of matrix A. - X - right-hand member of a system. - Array whose index ranges within [0..N-1]. - IsUpper - matrix type. If it is True, the system matrix is the upper - triangular and is located in the corresponding part of - matrix A. - Trans - problem type. If it is True, the problem to be solved is - A'*x=b, otherwise it is A*x=b. - Isunit - matrix type. If it is True, the system matrix has a unit - diagonal (the elements on the main diagonal are not used - in the calculation process), otherwise the matrix is considered - to be a general triangular matrix. - Output parameters: - X - solution. Array whose index ranges within [0..N-1]. - S - scaling factor. + /************************************************************************* + PBL-based kernel. - -- LAPACK auxiliary routine (version 3.0) -- - Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., - Courant Institute, Argonne National Lab, and Rice University - June 30, 1992 + NOTE: Q must be preallocated N*N array + + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey *************************************************************************/ - public static void rmatrixtrsafesolve(double[,] a, + public static bool hmatrixtdunpackqpbl(complex[,] a, int n, - ref double[] x, - ref double s, bool isupper, - bool istrans, - bool isunit, + complex[] tau, + complex[,] q, alglib.xparams _params) { - bool normin = new bool(); - double[] cnorm = new double[0]; - double[,] a1 = new double[0,0]; - double[] x1 = new double[0]; - int i = 0; - int i_ = 0; - int i1_ = 0; - - s = 0; + bool result = new bool(); - - // - // From 0-based to 1-based - // - normin = false; - a1 = new double[n+1, n+1]; - x1 = new double[n+1]; - for(i=1; i<=n; i++) - { - i1_ = (0) - (1); - for(i_=1; i_<=n;i_++) - { - a1[i,i_] = a[i-1,i_+i1_]; - } - } - i1_ = (0) - (1); - for(i_=1; i_<=n;i_++) - { - x1[i_] = x[i_+i1_]; - } - - // - // Solve 1-based - // - safesolvetriangular(a1, n, ref x1, ref s, isupper, istrans, isunit, normin, ref cnorm, _params); - - // - // From 1-based to 0-based - // - i1_ = (1) - (0); - for(i_=0; i_<=n-1;i_++) - { - x[i_] = x1[i_+i1_]; - } + result = false; + return result; } /************************************************************************* - Obsolete 1-based subroutine. - See RMatrixTRSafeSolve for 0-based replacement. + PBL-based kernel. + + Returns True if PBL was present and handled request (PBL completion code + is returned as separate output parameter). + + D and E are pre-allocated arrays with length N (both of them!). On output, + D constraints singular values, and E is destroyed. + + SVDResult is modified if and only if PBL is present. + + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey *************************************************************************/ - public static void safesolvetriangular(double[,] a, + public static bool rmatrixbdsvdpbl(double[] d, + double[] e, int n, - ref double[] x, - ref double s, bool isupper, - bool istrans, - bool isunit, - bool normin, - ref double[] cnorm, + double[,] u, + int nru, + double[,] c, + int ncc, + double[,] vt, + int ncvt, + ref bool svdresult, alglib.xparams _params) { - int i = 0; - int imax = 0; - int j = 0; - int jfirst = 0; - int jinc = 0; - int jlast = 0; - int jm1 = 0; - int jp1 = 0; - int ip1 = 0; - int im1 = 0; - int k = 0; - int flg = 0; - double v = 0; - double vd = 0; - double bignum = 0; - double grow = 0; - double rec = 0; - double smlnum = 0; - double sumj = 0; - double tjj = 0; - double tjjs = 0; - double tmax = 0; - double tscal = 0; - double uscal = 0; - double xbnd = 0; - double xj = 0; - double xmax = 0; - bool notran = new bool(); - bool upper = new bool(); - bool nounit = new bool(); - int i_ = 0; + bool result = new bool(); + + result = false; + return result; + } + + + /************************************************************************* + PBL-based DHSEQR kernel. + + Returns True if PBL was present and handled request. + + WR and WI are pre-allocated arrays with length N. + Z is pre-allocated array[N,N]. + + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey + *************************************************************************/ + public static bool rmatrixinternalschurdecompositionpbl(double[,] h, + int n, + int tneeded, + int zneeded, + double[] wr, + double[] wi, + double[,] z, + ref int info, + alglib.xparams _params) + { + bool result = new bool(); + + result = false; + return result; + } + + + /************************************************************************* + PBL-based DTREVC kernel. + + Returns True if PBL was present and handled request. + + NOTE: this function does NOT support HOWMNY=3!!!! + + VL and VR are pre-allocated arrays with length N*N, if required. If particalar + variables is not required, it can be dummy (empty) array. + + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey + *************************************************************************/ + public static bool rmatrixinternaltrevcpbl(double[,] t, + int n, + int side, + int howmny, + double[,] vl, + double[,] vr, + ref int m, + ref int info, + alglib.xparams _params) + { + bool result = new bool(); + + result = false; + return result; + } + + + /************************************************************************* + PBL-based kernel. + + Returns True if PBL was present and handled request (PBL completion code + is returned as separate output parameter). + + D and E are pre-allocated arrays with length N (both of them!). On output, + D constraints eigenvalues, and E is destroyed. + + Z is preallocated array[N,N] for ZNeeded<>0; ignored for ZNeeded=0. + + EVDResult is modified if and only if PBL is present. + + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey + *************************************************************************/ + public static bool smatrixtdevdpbl(double[] d, + double[] e, + int n, + int zneeded, + double[,] z, + ref bool evdresult, + alglib.xparams _params) + { + bool result = new bool(); + + result = false; + return result; + } + + + /************************************************************************* + PBL-based kernel. + + Returns True if PBL was present and handled request (PBL completion code + is returned as separate output parameter). + + D and E are pre-allocated arrays with length N (both of them!). On output, + D constraints eigenvalues, and E is destroyed. + + Z is preallocated array[N,N] for ZNeeded<>0; ignored for ZNeeded=0. + + EVDResult is modified if and only if PBL is present. + + -- ALGLIB routine -- + 20.10.2014 + Bochkanov Sergey + *************************************************************************/ + public static bool sparsegemvcrspbl(int opa, + int arows, + int acols, + double alpha, + double[] vals, + int[] cidx, + int[] ridx, + double[] x, + int ix, + double beta, + double[] y, + int iy, + alglib.xparams _params) + { + bool result = new bool(); + + result = false; + return result; + } + + + } + public partial class scodes + { + public static int getrdfserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 1; + return result; + } + + + public static int getkdtreeserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 2; + return result; + } + + + public static int getmlpserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 3; + return result; + } + + + public static int getmlpeserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 4; + return result; + } + + + public static int getrbfserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 5; + return result; + } + + + public static int getspline2dserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 6; + return result; + } + + + public static int getidwserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 7; + return result; + } + + + public static int getsparsematrixserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 8; + return result; + } + + + public static int getspline2dwithmissingnodesserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 9; + return result; + } + + + public static int getspline1dserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 10; + return result; + } + + + public static int getknnserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 108; + return result; + } + + + public static int getlptestserializationcode(alglib.xparams _params) + { + int result = 0; + + result = 200; + return result; + } + + + } + public partial class tsort + { + /************************************************************************* + This function sorts array of real keys by ascending. + + Its results are: + * sorted array A + * permutation tables P1, P2 + + Algorithm outputs permutation tables using two formats: + * as usual permutation of [0..N-1]. If P1[i]=j, then sorted A[i] contains + value which was moved there from J-th position. + * as a sequence of pairwise permutations. Sorted A[] may be obtained by + swaping A[i] and A[P2[i]] for all i from 0 to N-1. + + INPUT PARAMETERS: + A - unsorted array + N - array size + + OUPUT PARAMETERS: + A - sorted array + P1, P2 - permutation tables, array[N] + + NOTES: + this function assumes that A[] is finite; it doesn't checks that + condition. All other conditions (size of input arrays, etc.) are not + checked too. - s = 0; + -- ALGLIB -- + Copyright 14.05.2008 by Bochkanov Sergey + *************************************************************************/ + public static void tagsort(ref double[] a, + int n, + ref int[] p1, + ref int[] p2, + alglib.xparams _params) + { + apserv.apbuffers buf = new apserv.apbuffers(); + + p1 = new int[0]; + p2 = new int[0]; + + tagsortbuf(ref a, n, ref p1, ref p2, buf, _params); + } + + + /************************************************************************* + 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, + alglib.xparams _params) + { + int i = 0; + int lv = 0; + int lp = 0; + int rv = 0; + int rp = 0; - upper = isupper; - notran = !istrans; - nounit = !isunit; - - // - // these initializers are not really necessary, - // but without them compiler complains about uninitialized locals - // - tjjs = 0; // - // Quick return if possible + // Special cases // - if( n==0 ) + if( n<=0 ) { return; } - - // - // Determine machine dependent parameters to control overflow. - // - smlnum = math.minrealnumber/(math.machineepsilon*2); - bignum = 1/smlnum; - s = 1; - if( !normin ) - { - cnorm = new double[n+1]; - - // - // Compute the 1-norm of each column, not including the diagonal. - // - if( upper ) - { - - // - // A is upper triangular. - // - for(j=1; j<=n; j++) - { - v = 0; - for(k=1; k<=j-1; k++) - { - v = v+Math.Abs(a[k,j]); - } - cnorm[j] = v; - } - } - else - { - - // - // A is lower triangular. - // - for(j=1; j<=n-1; j++) - { - v = 0; - for(k=j+1; k<=n; k++) - { - v = v+Math.Abs(a[k,j]); - } - cnorm[j] = v; - } - cnorm[n] = 0; - } - } - - // - // Scale the column norms by TSCAL if the maximum element in CNORM is - // greater than BIGNUM. - // - imax = 1; - for(k=2; k<=n; k++) - { - if( (double)(cnorm[k])>(double)(cnorm[imax]) ) - { - imax = k; - } - } - tmax = cnorm[imax]; - if( (double)(tmax)<=(double)(bignum) ) - { - tscal = 1; - } - else - { - tscal = 1/(smlnum*tmax); - for(i_=1; i_<=n;i_++) - { - cnorm[i_] = tscal*cnorm[i_]; - } - } - - // - // Compute a bound on the computed solution vector to see if the - // Level 2 BLAS routine DTRSV can be used. - // - j = 1; - for(k=2; k<=n; k++) - { - if( (double)(Math.Abs(x[k]))>(double)(Math.Abs(x[j])) ) - { - j = k; - } - } - xmax = Math.Abs(x[j]); - xbnd = xmax; - if( notran ) + if( n==1 ) { - - // - // Compute the growth in A * x = b. - // - if( upper ) - { - jfirst = n; - jlast = 1; - jinc = -1; - } - else - { - jfirst = 1; - jlast = n; - jinc = 1; - } - if( (double)(tscal)!=(double)(1) ) - { - grow = 0; - } - else - { - if( nounit ) - { - - // - // A is non-unit triangular. - // - // Compute GROW = 1/G(j) and XBND = 1/M(j). - // Initially, G(0) = max{x(i), i=1,...,n}. - // - grow = 1/Math.Max(xbnd, smlnum); - xbnd = grow; - j = jfirst; - while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) - { - - // - // Exit the loop if the growth factor is too small. - // - if( (double)(grow)<=(double)(smlnum) ) - { - break; - } - - // - // M(j) = G(j-1) / abs(A(j,j)) - // - tjj = Math.Abs(a[j,j]); - xbnd = Math.Min(xbnd, Math.Min(1, tjj)*grow); - if( (double)(tjj+cnorm[j])>=(double)(smlnum) ) - { - - // - // G(j) = G(j-1)*( 1 + CNORM(j) / abs(A(j,j)) ) - // - grow = grow*(tjj/(tjj+cnorm[j])); - } - else - { - - // - // G(j) could overflow, set GROW to 0. - // - grow = 0; - } - if( j==jlast ) - { - grow = xbnd; - } - j = j+jinc; - } - } - else - { - - // - // A is unit triangular. - // - // Compute GROW = 1/G(j), where G(0) = max{x(i), i=1,...,n}. - // - grow = Math.Min(1, 1/Math.Max(xbnd, smlnum)); - j = jfirst; - while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) - { - - // - // Exit the loop if the growth factor is too small. - // - if( (double)(grow)<=(double)(smlnum) ) - { - break; - } - - // - // G(j) = G(j-1)*( 1 + CNORM(j) ) - // - grow = grow*(1/(1+cnorm[j])); - j = j+jinc; - } - } - } + apserv.ivectorsetlengthatleast(ref p1, 1, _params); + apserv.ivectorsetlengthatleast(ref p2, 1, _params); + p1[0] = 0; + p2[0] = 0; + return; } - else + + // + // General case, N>1: prepare permutations table P1 + // + apserv.ivectorsetlengthatleast(ref p1, n, _params); + for(i=0; i<=n-1; i++) + { + p1[i] = i; + } + + // + // General case, N>1: sort, update P1 + // + apserv.rvectorsetlengthatleast(ref buf.ra0, n, _params); + apserv.ivectorsetlengthatleast(ref buf.ia0, n, _params); + tagsortfasti(ref a, ref p1, ref buf.ra0, ref buf.ia0, n, _params); + + // + // 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, _params); + apserv.ivectorsetlengthatleast(ref buf.ia1, n, _params); + apserv.ivectorsetlengthatleast(ref p2, n, _params); + for(i=0; i<=n-1; i++) + { + buf.ia0[i] = i; + buf.ia1[i] = i; + } + for(i=0; i<=n-1; i++) { // - // Compute the growth in A' * x = b. + // calculate LP, LV, RP, RV // - if( upper ) - { - jfirst = 1; - jlast = n; - jinc = 1; - } - else - { - jfirst = n; - jlast = 1; - jinc = -1; - } - if( (double)(tscal)!=(double)(1) ) - { - grow = 0; - } - else - { - if( nounit ) - { - - // - // A is non-unit triangular. - // - // Compute GROW = 1/G(j) and XBND = 1/M(j). - // Initially, M(0) = max{x(i), i=1,...,n}. - // - grow = 1/Math.Max(xbnd, smlnum); - xbnd = grow; - j = jfirst; - while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) - { - - // - // Exit the loop if the growth factor is too small. - // - if( (double)(grow)<=(double)(smlnum) ) - { - break; - } - - // - // G(j) = max( G(j-1), M(j-1)*( 1 + CNORM(j) ) ) - // - xj = 1+cnorm[j]; - grow = Math.Min(grow, xbnd/xj); - - // - // M(j) = M(j-1)*( 1 + CNORM(j) ) / abs(A(j,j)) - // - tjj = Math.Abs(a[j,j]); - if( (double)(xj)>(double)(tjj) ) - { - xbnd = xbnd*(tjj/xj); - } - if( j==jlast ) - { - grow = Math.Min(grow, xbnd); - } - j = j+jinc; - } - } - else - { - - // - // A is unit triangular. - // - // Compute GROW = 1/G(j), where G(0) = max{x(i), i=1,...,n}. - // - grow = Math.Min(1, 1/Math.Max(xbnd, smlnum)); - j = jfirst; - while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) - { - - // - // Exit the loop if the growth factor is too small. - // - if( (double)(grow)<=(double)(smlnum) ) - { - break; - } - - // - // G(j) = ( 1 + CNORM(j) )*G(j-1) - // - xj = 1+cnorm[j]; - grow = grow/xj; - j = j+jinc; - } - } - } - } - if( (double)(grow*tscal)>(double)(smlnum) ) - { + lp = i; + lv = buf.ia1[lp]; + rv = p1[i]; + rp = buf.ia0[rv]; // - // Use the Level 2 BLAS solve if the reciprocal of the bound on - // elements of X is not too small. + // Fill P2 // - if( (upper && notran) || (!upper && !notran) ) - { - if( nounit ) - { - vd = a[n,n]; - } - else - { - vd = 1; - } - x[n] = x[n]/vd; - for(i=n-1; i>=1; i--) - { - ip1 = i+1; - if( upper ) - { - v = 0.0; - for(i_=ip1; i_<=n;i_++) - { - v += a[i,i_]*x[i_]; - } - } - else - { - v = 0.0; - for(i_=ip1; i_<=n;i_++) - { - v += a[i_,i]*x[i_]; - } - } - if( nounit ) - { - vd = a[i,i]; - } - else - { - vd = 1; - } - x[i] = (x[i]-v)/vd; - } - } - else - { - if( nounit ) - { - vd = a[1,1]; - } - else - { - vd = 1; - } - x[1] = x[1]/vd; - for(i=2; i<=n; i++) - { - im1 = i-1; - if( upper ) - { - v = 0.0; - for(i_=1; i_<=im1;i_++) - { - v += a[i_,i]*x[i_]; - } - } - else - { - v = 0.0; - for(i_=1; i_<=im1;i_++) - { - v += a[i,i_]*x[i_]; - } - } - if( nounit ) - { - vd = a[i,i]; - } - else - { - vd = 1; - } - x[i] = (x[i]-v)/vd; - } - } + p2[i] = rp; + + // + // update PV and VP + // + buf.ia1[lp] = rv; + buf.ia1[rp] = lv; + buf.ia0[lv] = rp; + buf.ia0[rv] = lp; + } + } + + + /************************************************************************* + Same as TagSort, but optimized for real keys and integer labels. + + A is sorted, and same permutations are applied to B. + + NOTES: + 1. this function assumes that A[] is finite; it doesn't checks that + condition. All other conditions (size of input arrays, etc.) are not + checked too. + 2. this function uses two buffers, BufA and BufB, each is N elements large. + They may be preallocated (which will save some time) or not, in which + case function will automatically allocate memory. + + -- ALGLIB -- + Copyright 11.12.2008 by Bochkanov Sergey + *************************************************************************/ + public static void tagsortfasti(ref double[] a, + ref int[] b, + ref double[] bufa, + ref int[] bufb, + int n, + alglib.xparams _params) + { + int i = 0; + int j = 0; + bool isascending = new bool(); + bool isdescending = new bool(); + double tmpr = 0; + int tmpi = 0; + + + // + // Special case + // + if( n<=1 ) + { + return; } - else + + // + // Test for already sorted set + // + isascending = true; + isdescending = true; + for(i=1; i<=n-1; i++) { - - // - // Use a Level 1 BLAS solve, scaling intermediate results. - // - if( (double)(xmax)>(double)(bignum) ) - { - - // - // Scale X so that its components are less than or equal to - // BIGNUM in absolute value. - // - s = bignum/xmax; - for(i_=1; i_<=n;i_++) - { - x[i_] = s*x[i_]; - } - xmax = bignum; - } - if( notran ) - { - - // - // Solve A * x = b - // - j = jfirst; - while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) - { - - // - // Compute x(j) = b(j) / A(j,j), scaling x if necessary. - // - xj = Math.Abs(x[j]); - flg = 0; - if( nounit ) - { - tjjs = a[j,j]*tscal; - } - else - { - tjjs = tscal; - if( (double)(tscal)==(double)(1) ) - { - flg = 100; - } - } - if( flg!=100 ) - { - tjj = Math.Abs(tjjs); - if( (double)(tjj)>(double)(smlnum) ) - { - - // - // abs(A(j,j)) > SMLNUM: - // - if( (double)(tjj)<(double)(1) ) - { - if( (double)(xj)>(double)(tjj*bignum) ) - { - - // - // Scale x by 1/b(j). - // - rec = 1/xj; - for(i_=1; i_<=n;i_++) - { - x[i_] = rec*x[i_]; - } - s = s*rec; - xmax = xmax*rec; - } - } - x[j] = x[j]/tjjs; - xj = Math.Abs(x[j]); - } - else - { - if( (double)(tjj)>(double)(0) ) - { - - // - // 0 < abs(A(j,j)) <= SMLNUM: - // - if( (double)(xj)>(double)(tjj*bignum) ) - { - - // - // Scale x by (1/abs(x(j)))*abs(A(j,j))*BIGNUM - // to avoid overflow when dividing by A(j,j). - // - rec = tjj*bignum/xj; - if( (double)(cnorm[j])>(double)(1) ) - { - - // - // Scale by 1/CNORM(j) to avoid overflow when - // multiplying x(j) times column j. - // - rec = rec/cnorm[j]; - } - for(i_=1; i_<=n;i_++) - { - x[i_] = rec*x[i_]; - } - s = s*rec; - xmax = xmax*rec; - } - x[j] = x[j]/tjjs; - xj = Math.Abs(x[j]); - } - else - { - - // - // A(j,j) = 0: Set x(1:n) = 0, x(j) = 1, and - // scale = 0, and compute a solution to A*x = 0. - // - for(i=1; i<=n; i++) - { - x[i] = 0; - } - x[j] = 1; - xj = 1; - s = 0; - xmax = 0; - } - } - } - - // - // Scale x if necessary to avoid overflow when adding a - // multiple of column j of A. - // - if( (double)(xj)>(double)(1) ) - { - rec = 1/xj; - if( (double)(cnorm[j])>(double)((bignum-xmax)*rec) ) - { - - // - // Scale x by 1/(2*abs(x(j))). - // - rec = rec*0.5; - for(i_=1; i_<=n;i_++) - { - x[i_] = rec*x[i_]; - } - s = s*rec; - } - } - else - { - if( (double)(xj*cnorm[j])>(double)(bignum-xmax) ) - { - - // - // Scale x by 1/2. - // - for(i_=1; i_<=n;i_++) - { - x[i_] = 0.5*x[i_]; - } - s = s*0.5; - } - } - if( upper ) - { - if( j>1 ) - { - - // - // Compute the update - // x(1:j-1) := x(1:j-1) - x(j) * A(1:j-1,j) - // - v = x[j]*tscal; - jm1 = j-1; - for(i_=1; i_<=jm1;i_++) - { - x[i_] = x[i_] - v*a[i_,j]; - } - i = 1; - for(k=2; k<=j-1; k++) - { - if( (double)(Math.Abs(x[k]))>(double)(Math.Abs(x[i])) ) - { - i = k; - } - } - xmax = Math.Abs(x[i]); - } - } - else - { - if( j(double)(Math.Abs(x[i])) ) - { - i = k; - } - } - xmax = Math.Abs(x[i]); - } - } - j = j+jinc; - } - } - else + isascending = isascending && a[i]>=a[i-1]; + isdescending = isdescending && a[i]<=a[i-1]; + } + if( isascending ) + { + return; + } + if( isdescending ) + { + for(i=0; i<=n-1; i++) { - - // - // Solve A' * x = b - // - j = jfirst; - while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) + j = n-1-i; + if( j<=i ) { - - // - // Compute x(j) = b(j) - sum A(k,j)*x(k). - // k<>j - // - xj = Math.Abs(x[j]); - uscal = tscal; - rec = 1/Math.Max(xmax, 1); - if( (double)(cnorm[j])>(double)((bignum-xj)*rec) ) - { - - // - // If x(j) could overflow, scale x by 1/(2*XMAX). - // - rec = rec*0.5; - if( nounit ) - { - tjjs = a[j,j]*tscal; - } - else - { - tjjs = tscal; - } - tjj = Math.Abs(tjjs); - if( (double)(tjj)>(double)(1) ) - { - - // - // Divide by A(j,j) when scaling x if A(j,j) > 1. - // - rec = Math.Min(1, rec*tjj); - uscal = uscal/tjjs; - } - if( (double)(rec)<(double)(1) ) - { - for(i_=1; i_<=n;i_++) - { - x[i_] = rec*x[i_]; - } - s = s*rec; - xmax = xmax*rec; - } - } - sumj = 0; - if( (double)(uscal)==(double)(1) ) - { - - // - // If the scaling needed for A in the dot product is 1, - // call DDOT to perform the dot product. - // - if( upper ) - { - if( j>1 ) - { - jm1 = j-1; - sumj = 0.0; - for(i_=1; i_<=jm1;i_++) - { - sumj += a[i_,j]*x[i_]; - } - } - else - { - sumj = 0; - } - } - else - { - if( j(double)(smlnum) ) - { - - // - // abs(A(j,j)) > SMLNUM: - // - if( (double)(tjj)<(double)(1) ) - { - if( (double)(xj)>(double)(tjj*bignum) ) - { - - // - // Scale X by 1/abs(x(j)). - // - rec = 1/xj; - for(i_=1; i_<=n;i_++) - { - x[i_] = rec*x[i_]; - } - s = s*rec; - xmax = xmax*rec; - } - } - x[j] = x[j]/tjjs; - } - else - { - if( (double)(tjj)>(double)(0) ) - { - - // - // 0 < abs(A(j,j)) <= SMLNUM: - // - if( (double)(xj)>(double)(tjj*bignum) ) - { - - // - // Scale x by (1/abs(x(j)))*abs(A(j,j))*BIGNUM. - // - rec = tjj*bignum/xj; - for(i_=1; i_<=n;i_++) - { - x[i_] = rec*x[i_]; - } - s = s*rec; - xmax = xmax*rec; - } - x[j] = x[j]/tjjs; - } - else - { - - // - // A(j,j) = 0: Set x(1:n) = 0, x(j) = 1, and - // scale = 0, and compute a solution to A'*x = 0. - // - for(i=1; i<=n; i++) - { - x[i] = 0; - } - x[j] = 1; - s = 0; - xmax = 0; - } - } - } - } - else - { - - // - // Compute x(j) := x(j) / A(j,j) - sumj if the dot - // product has already been divided by 1/A(j,j). - // - x[j] = x[j]/tjjs-sumj; - } - xmax = Math.Max(xmax, Math.Abs(x[j])); - j = j+jinc; + break; } + tmpr = a[i]; + a[i] = a[j]; + a[j] = tmpr; + tmpi = b[i]; + b[i] = b[j]; + b[j] = tmpi; } - s = s/tscal; + return; } // - // Scale the column norms by 1/TSCAL for return. + // General case // - if( (double)(tscal)!=(double)(1) ) + if( alglib.ap.len(bufa)0, "RMatrixTRSafeSolve: incorrect N!"); - alglib.ap.assert(trans==0 || trans==1, "RMatrixTRSafeSolve: incorrect Trans!"); - result = true; - lnmax = Math.Log(math.maxrealnumber); - - // - // Quick return if possible - // - if( n<=0 ) - { - return result; - } // - // Load norms: right part and X + // Special case // - nrmb = 0; - for(i=0; i<=n-1; i++) + if( n<=1 ) { - nrmb = Math.Max(nrmb, Math.Abs(x[i])); + return; } - nrmx = 0; // - // Solve + // Test for already sorted set // - tmp = new double[n]; - result = true; - if( isupper && trans==0 ) + isascending = true; + isdescending = true; + for(i=1; i<=n-1; i++) { - - // - // U*x = b - // - for(i=n-1; i>=0; i--) - { - - // - // Task is reduced to alpha*x[i] = beta - // - if( isunit ) - { - alpha = sa; - } - else - { - alpha = a[i,i]*sa; - } - if( i=a[i-1]; + isdescending = isdescending && a[i]<=a[i-1]; } - if( !isupper && trans==0 ) + if( isascending ) { - - // - // L*x = b - // - for(i=0; i<=n-1; i++) - { - - // - // Task is reduced to alpha*x[i] = beta - // - if( isunit ) - { - alpha = sa; - } - else - { - alpha = a[i,i]*sa; - } - if( i>0 ) - { - for(i_=0; i_<=i-1;i_++) - { - tmp[i_] = sa*a[i,i_]; - } - vr = 0.0; - for(i_=0; i_<=i-1;i_++) - { - vr += tmp[i_]*x[i_]; - } - beta = x[i]-vr; - } - else - { - beta = x[i]; - } - - // - // solve alpha*x[i] = beta - // - result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref cx, _params); - if( !result ) - { - return result; - } - x[i] = cx.x; - } - return result; + return; } - if( isupper && trans==1 ) + if( isdescending ) { - - // - // U^T*x = b - // for(i=0; i<=n-1; i++) - { - - // - // Task is reduced to alpha*x[i] = beta - // - if( isunit ) - { - alpha = sa; - } - else - { - alpha = a[i,i]*sa; - } - beta = x[i]; - - // - // solve alpha*x[i] = beta - // - result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref cx, _params); - if( !result ) - { - return result; - } - x[i] = cx.x; - - // - // update the rest of right part - // - if( i=0; i--) - { - - // - // Task is reduced to alpha*x[i] = beta - // - if( isunit ) - { - alpha = sa; - } - else - { - alpha = a[i,i]*sa; - } - beta = x[i]; - - // - // solve alpha*x[i] = beta - // - result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref cx, _params); - if( !result ) - { - return result; - } - x[i] = cx.x; - - // - // update the rest of right part - // - if( i>0 ) - { - vr = cx.x; - for(i_=0; i_<=i-1;i_++) - { - tmp[i_] = sa*a[i,i_]; - } - for(i_=0; i_<=i-1;i_++) - { - x[i_] = x[i_] - vr*tmp[i_]; - } + { + j = n-1-i; + if( j<=i ) + { + break; } + tmpr = a[i]; + a[i] = a[j]; + a[j] = tmpr; + tmpr = b[i]; + b[i] = b[j]; + b[j] = tmpr; } - return result; + return; } - result = false; - return result; + + // + // General case + // + if( alglib.ap.len(bufa)0, "CMatrixTRSafeSolve: incorrect N!"); - alglib.ap.assert((trans==0 || trans==1) || trans==2, "CMatrixTRSafeSolve: incorrect Trans!"); - result = true; - lnmax = Math.Log(math.maxrealnumber); // - // Quick return if possible + // Special case // - if( n<=0 ) + if( n<=1 ) { - return result; + return; } // - // Load norms: right part and X + // Test for already sorted set // - nrmb = 0; - for(i=0; i<=n-1; i++) + isascending = true; + isdescending = true; + for(i=1; i<=n-1; i++) { - nrmb = Math.Max(nrmb, math.abscomplex(x[i])); + isascending = isascending && a[i]>=a[i-1]; + isdescending = isdescending && a[i]<=a[i-1]; } - nrmx = 0; - - // - // Solve - // - tmp = new complex[n]; - result = true; - if( isupper && trans==0 ) + if( isascending ) { - - // - // U*x = b - // - for(i=n-1; i>=0; i--) - { - - // - // Task is reduced to alpha*x[i] = beta - // - if( isunit ) - { - alpha = sa; - } - else - { - alpha = a[i,i]*sa; - } - if( i0 ) - { - for(i_=0; i_<=i-1;i_++) - { - tmp[i_] = sa*a[i,i_]; - } - vc = 0.0; - for(i_=0; i_<=i-1;i_++) - { - vc += tmp[i_]*x[i_]; - } - beta = x[i]-vc; - } - else - { - beta = x[i]; - } - - // - // solve alpha*x[i] = beta - // - result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); - if( !result ) + j = n-1-i; + if( j<=i ) { - return result; + break; } - x[i] = vc; + tmpr = a[i]; + a[i] = a[j]; + a[j] = tmpr; } - return result; + return; + } + + // + // General case + // + if( alglib.ap.len(bufa)=a[offset+i-1]; } - if( isupper && trans==1 ) + if( isascending ) { - - // - // U^T*x = b - // - for(i=0; i<=n-1; i++) + return; + } + + // + // General case, N>1: sort, update B + // + for(i=2; i<=n; i++) + { + t = i; + while( t!=1 ) { - - // - // Task is reduced to alpha*x[i] = beta - // - if( isunit ) - { - alpha = sa; - } - else - { - alpha = a[i,i]*sa; - } - beta = x[i]; - - // - // solve alpha*x[i] = beta - // - result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); - if( !result ) - { - return result; - } - x[i] = vc; - - // - // update the rest of right part - // - if( i=at ) { - for(i_=i+1; i_<=n-1;i_++) - { - tmp[i_] = sa*a[i,i_]; - } - for(i_=i+1; i_<=n-1;i_++) - { - x[i_] = x[i_] - vc*tmp[i_]; - } + break; } + a[p0] = at; + a[p1] = ak; + tmpr = b[p0]; + b[p0] = b[p1]; + b[p1] = tmpr; + t = k; } - return result; } - if( !isupper && trans==1 ) + for(i=n-1; i>=1; i--) { - - // - // L^T*x = b - // - for(i=n-1; i>=0; i--) + p0 = offset+0; + p1 = offset+i; + tmp = a[p1]; + a[p1] = a[p0]; + a[p0] = tmp; + at = tmp; + tmpr = b[p1]; + b[p1] = b[p0]; + b[p0] = tmpr; + bt = tmpr; + t = 0; + while( true ) { - - // - // Task is reduced to alpha*x[i] = beta - // - if( isunit ) - { - alpha = sa; - } - else - { - alpha = a[i,i]*sa; - } - beta = x[i]; - - // - // solve alpha*x[i] = beta - // - result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); - if( !result ) + k = 2*t+1; + if( k+1>i ) { - return result; + break; } - x[i] = vc; - - // - // update the rest of right part - // - if( i>0 ) + p0 = offset+t; + p1 = offset+k; + ak = a[p1]; + if( k+1ak ) { - x[i_] = x[i_] - vc*tmp[i_]; + ak = ak1; + p1 = p1+1; + k = k+1; } } + if( at>=ak ) + { + break; + } + a[p1] = at; + a[p0] = ak; + b[p0] = b[p1]; + b[p1] = bt; + t = k; } - return result; } - if( isupper && trans==2 ) + } + + + /************************************************************************* + Sorting function optimized for integer keys and real labels, can be used + to sort middle of the array + + A is sorted, and same permutations are applied to B and C. + + Elements beyond [offs:offs+N-1] are not modified or referenced. + + NOTES: + this function assumes that A[] is finite; it doesn't checks that + condition. All other conditions (size of input arrays, etc.) are not + checked too. + + -- ALGLIB -- + Copyright 11.12.2024 by Bochkanov Sergey + *************************************************************************/ + public static void tagsortmiddleirr(int[] a, + double[] b, + double[] c, + int offset, + int n, + alglib.xparams _params) + { + int i = 0; + int k = 0; + int t = 0; + int tmp = 0; + double tmpr = 0; + double tmpr2 = 0; + int p0 = 0; + int p1 = 0; + int at = 0; + int ak = 0; + int ak1 = 0; + double bt = 0; + double ct = 0; + + + // + // Special cases + // + if( n<=1 ) { - - // - // U^H*x = b - // - for(i=0; i<=n-1; i++) + return; + } + + // + // General case, N>1: sort, update B and C + // + for(i=2; i<=n; i++) + { + t = i; + while( t!=1 ) { - - // - // Task is reduced to alpha*x[i] = beta - // - if( isunit ) - { - alpha = sa; - } - else - { - alpha = math.conj(a[i,i])*sa; - } - beta = x[i]; - - // - // solve alpha*x[i] = beta - // - result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); - if( !result ) - { - return result; - } - x[i] = vc; - - // - // update the rest of right part - // - if( i=at ) { - for(i_=i+1; i_<=n-1;i_++) - { - tmp[i_] = sa*math.conj(a[i,i_]); - } - for(i_=i+1; i_<=n-1;i_++) - { - x[i_] = x[i_] - vc*tmp[i_]; - } + break; } + a[p0] = at; + a[p1] = ak; + tmpr = b[p0]; + b[p0] = b[p1]; + b[p1] = tmpr; + tmpr2 = c[p0]; + c[p0] = c[p1]; + c[p1] = tmpr2; + t = k; } - return result; } - if( !isupper && trans==2 ) + for(i=n-1; i>=1; i--) { - - // - // L^T*x = b - // - for(i=n-1; i>=0; i--) + p0 = offset+0; + p1 = offset+i; + tmp = a[p1]; + a[p1] = a[p0]; + a[p0] = tmp; + at = tmp; + tmpr = b[p1]; + b[p1] = b[p0]; + b[p0] = tmpr; + bt = tmpr; + tmpr2 = c[p1]; + c[p1] = c[p0]; + c[p0] = tmpr2; + ct = tmpr2; + t = 0; + while( true ) { - - // - // Task is reduced to alpha*x[i] = beta - // - if( isunit ) - { - alpha = sa; - } - else - { - alpha = math.conj(a[i,i])*sa; - } - beta = x[i]; - - // - // solve alpha*x[i] = beta - // - result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); - if( !result ) + k = 2*t+1; + if( k+1>i ) { - return result; + break; } - x[i] = vc; - - // - // update the rest of right part - // - if( i>0 ) + p0 = offset+t; + p1 = offset+k; + ak = a[p1]; + if( k+1ak ) { - x[i_] = x[i_] - vc*tmp[i_]; + ak = ak1; + p1 = p1+1; + k = k+1; } } + if( at>=ak ) + { + break; + } + a[p1] = at; + a[p0] = ak; + b[p0] = b[p1]; + b[p1] = bt; + c[p0] = c[p1]; + c[p1] = ct; + t = k; } - return result; } - result = false; - return result; } /************************************************************************* - complex basic solver-updater for reduced linear system - - alpha*x[i] = beta + Sorting function optimized for real keys and integer labels, can be used + to sort middle of the array - solves this equation and updates it in overlfow-safe manner (keeping track - of relative growth of solution). + A is sorted, and same permutations are applied to B. - Parameters: - Alpha - alpha - Beta - beta - LnMax - precomputed Ln(MaxRealNumber) - BNorm - inf-norm of b (right part of original system) - MaxGrowth- maximum growth of norm(x) relative to norm(b) - XNorm - inf-norm of other components of X (which are already processed) - it is updated by CBasicSolveAndUpdate. - X - solution + NOTES: + this function assumes that A[] is finite; it doesn't checks that + condition. All other conditions (size of input arrays, etc.) are not + checked too. - -- ALGLIB routine -- - 26.01.2009 - Bochkanov Sergey + -- ALGLIB -- + Copyright 11.12.2008 by Bochkanov Sergey *************************************************************************/ - private static bool cbasicsolveandupdate(complex alpha, - complex beta, - double lnmax, - double bnorm, - double maxgrowth, - ref double xnorm, - ref complex x, + public static void tagsortmiddleri(double[] a, + int[] b, + int offset, + int n, alglib.xparams _params) { - bool result = new bool(); - double v = 0; - - x = 0; + int i = 0; + int k = 0; + int t = 0; + double tmpa = 0; + int tmpi = 0; + int p0 = 0; + int p1 = 0; + double at = 0; + double ak = 0; + double ak1 = 0; + int bt = 0; - result = false; - if( alpha==0 ) + + // + // Special cases + // + if( n<=1 ) { - return result; + return; } - if( beta!=0 ) + + // + // General case, N>1: sort, update B + // + for(i=2; i<=n; i++) { - - // - // alpha*x[i]=beta - // - v = Math.Log(math.abscomplex(beta))-Math.Log(math.abscomplex(alpha)); - if( (double)(v)>(double)(lnmax) ) + t = i; + while( t!=1 ) { - return result; + k = t/2; + p0 = offset+k-1; + p1 = offset+t-1; + ak = a[p0]; + at = a[p1]; + if( (double)(ak)>=(double)(at) ) + { + break; + } + a[p0] = at; + a[p1] = ak; + tmpi = b[p0]; + b[p0] = b[p1]; + b[p1] = tmpi; + t = k; } - x = beta/alpha; - } - else - { - - // - // alpha*x[i]=0 - // - x = 0; } - - // - // update NrmX, test growth limit - // - xnorm = Math.Max(xnorm, math.abscomplex(x)); - if( (double)(xnorm)>(double)(maxgrowth*bnorm) ) + for(i=n-1; i>=1; i--) { - return result; + p0 = offset+0; + p1 = offset+i; + tmpa = a[p1]; + a[p1] = a[p0]; + a[p0] = tmpa; + at = tmpa; + tmpi = b[p1]; + b[p1] = b[p0]; + b[p0] = tmpi; + bt = tmpi; + t = 0; + while( true ) + { + k = 2*t+1; + if( k+1>i ) + { + break; + } + p0 = offset+t; + p1 = offset+k; + ak = a[p1]; + if( k+1(double)(ak) ) + { + ak = ak1; + p1 = p1+1; + k = k+1; + } + } + if( (double)(at)>=(double)(ak) ) + { + break; + } + a[p1] = at; + a[p0] = ak; + b[p0] = b[p1]; + b[p1] = bt; + t = k; + } } - result = true; - return result; } - } - public class xblas - { /************************************************************************* - More precise dot-product. Absolute error of subroutine result is about - 1 ulp of max(MX,V), where: - MX = max( |a[i]*b[i]| ) - V = |(a,b)| + Sorting function optimized for integer keys and integer labels, can be used + to sort middle of the array - INPUT PARAMETERS - A - array[0..N-1], vector 1 - B - array[0..N-1], vector 2 - N - vectors length, N<2^29. - Temp - array[0..N-1], pre-allocated temporary storage + A is sorted, and same permutations are applied to B. - OUTPUT PARAMETERS - R - (A,B) - RErr - estimate of error. This estimate accounts for both errors - during calculation of (A,B) and errors introduced by - rounding of A and B to fit in double (about 1 ulp). + NOTES: + this function assumes that A[] is finite; it doesn't checks that + condition. All other conditions (size of input arrays, etc.) are not + checked too. -- ALGLIB -- - Copyright 24.08.2009 by Bochkanov Sergey + Copyright 11.12.2008 by Bochkanov Sergey *************************************************************************/ - public static void xdot(double[] a, - double[] b, + public static void tagsortmiddleii(ref int[] a, + ref int[] b, + int offset, int n, - ref double[] temp, - ref double r, - ref double rerr, alglib.xparams _params) { int i = 0; - double mx = 0; - double v = 0; - - r = 0; - rerr = 0; + int k = 0; + int t = 0; + int tmp = 0; + int tmpi = 0; + int p0 = 0; + int p1 = 0; + int at = 0; + int ak = 0; + int ak1 = 0; + int bt = 0; // - // special cases: - // * N=0 + // Special cases // - if( n==0 ) + if( n<=1 ) { - r = 0; - rerr = 0; return; } - mx = 0; - for(i=0; i<=n-1; i++) + + // + // General case, N>1: sort, update B + // + for(i=2; i<=n; i++) { - v = a[i]*b[i]; - temp[i] = v; - mx = Math.Max(mx, Math.Abs(v)); + t = i; + while( t!=1 ) + { + k = t/2; + p0 = offset+k-1; + p1 = offset+t-1; + ak = a[p0]; + at = a[p1]; + if( ak>=at ) + { + break; + } + a[p0] = at; + a[p1] = ak; + tmpi = b[p0]; + b[p0] = b[p1]; + b[p1] = tmpi; + t = k; + } } - if( (double)(mx)==(double)(0) ) + for(i=n-1; i>=1; i--) { - r = 0; - rerr = 0; - return; + p0 = offset+0; + p1 = offset+i; + tmp = a[p1]; + a[p1] = a[p0]; + a[p0] = tmp; + at = tmp; + tmpi = b[p1]; + b[p1] = b[p0]; + b[p0] = tmpi; + bt = tmpi; + t = 0; + while( true ) + { + k = 2*t+1; + if( k+1>i ) + { + break; + } + p0 = offset+t; + p1 = offset+k; + ak = a[p1]; + if( k+1ak ) + { + ak = ak1; + p1 = p1+1; + k = k+1; + } + } + if( at>=ak ) + { + break; + } + a[p1] = at; + a[p0] = ak; + b[p0] = b[p1]; + b[p1] = bt; + t = k; + } } - xsum(ref temp, mx, n, ref r, ref rerr, _params); } /************************************************************************* - More precise complex dot-product. Absolute error of subroutine result is - about 1 ulp of max(MX,V), where: - MX = max( |a[i]*b[i]| ) - V = |(a,b)| + Sorting function optimized for integer keys and real labels, can be used + to sort middle of the array - INPUT PARAMETERS - A - array[0..N-1], vector 1 - B - array[0..N-1], vector 2 - N - vectors length, N<2^29. - Temp - array[0..2*N-1], pre-allocated temporary storage + A is sorted, and same permutations are applied to B. - OUTPUT PARAMETERS - R - (A,B) - RErr - estimate of error. This estimate accounts for both errors - during calculation of (A,B) and errors introduced by - rounding of A and B to fit in double (about 1 ulp). + NOTES: + this function assumes that A[] is finite; it doesn't checks that + condition. All other conditions (size of input arrays, etc.) are not + checked too. -- ALGLIB -- - Copyright 27.01.2010 by Bochkanov Sergey + Copyright 11.12.2008 by Bochkanov Sergey *************************************************************************/ - public static void xcdot(complex[] a, - complex[] b, + public static void tagsortmiddlei(ref int[] a, + int offset, int n, - ref double[] temp, - ref complex r, - ref double rerr, alglib.xparams _params) { int i = 0; - double mx = 0; - double v = 0; - double rerrx = 0; - double rerry = 0; - - r = 0; - rerr = 0; + int k = 0; + int t = 0; + int tmp = 0; + int p0 = 0; + int p1 = 0; + int at = 0; + int ak = 0; + int ak1 = 0; // - // special cases: - // * N=0 + // Special cases // - if( n==0 ) + if( n<=1 ) { - r = 0; - rerr = 0; return; } // - // calculate real part - // - mx = 0; - for(i=0; i<=n-1; i++) - { - v = a[i].x*b[i].x; - temp[2*i+0] = v; - mx = Math.Max(mx, Math.Abs(v)); - v = -(a[i].y*b[i].y); - temp[2*i+1] = v; - mx = Math.Max(mx, Math.Abs(v)); - } - if( (double)(mx)==(double)(0) ) - { - r.x = 0; - rerrx = 0; - } - else - { - xsum(ref temp, mx, 2*n, ref r.x, ref rerrx, _params); - } - - // - // calculate imaginary part - // - mx = 0; - for(i=0; i<=n-1; i++) - { - v = a[i].x*b[i].y; - temp[2*i+0] = v; - mx = Math.Max(mx, Math.Abs(v)); - v = a[i].y*b[i].x; - temp[2*i+1] = v; - mx = Math.Max(mx, Math.Abs(v)); - } - if( (double)(mx)==(double)(0) ) - { - r.y = 0; - rerry = 0; - } - else - { - xsum(ref temp, mx, 2*n, ref r.y, ref rerry, _params); - } - - // - // total error + // General case, N>1: sort, update B // - if( (double)(rerrx)==(double)(0) && (double)(rerry)==(double)(0) ) + for(i=2; i<=n; i++) { - rerr = 0; + t = i; + while( t!=1 ) + { + k = t/2; + p0 = offset+k-1; + p1 = offset+t-1; + ak = a[p0]; + at = a[p1]; + if( ak>=at ) + { + break; + } + a[p0] = at; + a[p1] = ak; + t = k; + } } - else + for(i=n-1; i>=1; i--) { - rerr = Math.Max(rerrx, rerry)*Math.Sqrt(1+math.sqr(Math.Min(rerrx, rerry)/Math.Max(rerrx, rerry))); + p0 = offset+0; + p1 = offset+i; + tmp = a[p1]; + a[p1] = a[p0]; + a[p0] = tmp; + at = tmp; + t = 0; + while( true ) + { + k = 2*t+1; + if( k+1>i ) + { + break; + } + p0 = offset+t; + p1 = offset+k; + ak = a[p1]; + if( k+1ak ) + { + ak = ak1; + p1 = p1+1; + k = k+1; + } + } + if( at>=ak ) + { + break; + } + a[p1] = at; + a[p0] = ak; + t = k; + } } } /************************************************************************* - Internal subroutine for extra-precise calculation of SUM(w[i]). - - INPUT PARAMETERS: - W - array[0..N-1], values to be added - W is modified during calculations. - MX - max(W[i]) - N - array size - - OUTPUT PARAMETERS: - R - SUM(w[i]) - RErr- error estimate for R + Sorting function optimized for integer values (only keys, no labels), can + be used to sort middle of the array -- ALGLIB -- - Copyright 24.08.2009 by Bochkanov Sergey + Copyright 11.12.2008 by Bochkanov Sergey *************************************************************************/ - private static void xsum(ref double[] w, - double mx, + public static void sortmiddlei(int[] a, + int offset, int n, - ref double r, - ref double rerr, alglib.xparams _params) { int i = 0; int k = 0; - int ks = 0; - double v = 0; - double s = 0; - double ln2 = 0; - double chunk = 0; - double invchunk = 0; - bool allzeros = new bool(); - int i_ = 0; - - r = 0; - rerr = 0; + int t = 0; + int tmp = 0; + int p0 = 0; + int p1 = 0; + int at = 0; + int ak = 0; + int ak1 = 0; // - // special cases: - // * N=0 - // * N is too large to use integer arithmetics + // Special cases // - if( n==0 ) - { - r = 0; - rerr = 0; - return; - } - if( (double)(mx)==(double)(0) ) + if( n<=1 ) { - r = 0; - rerr = 0; return; } - alglib.ap.assert(n<536870912, "XDot: N is too large!"); // - // Prepare - // - ln2 = Math.Log(2); - rerr = mx*math.machineepsilon; - - // - // 1. find S such that 0.5<=S*MX<1 - // 2. multiply W by S, so task is normalized in some sense - // 3. S:=1/S so we can obtain original vector multiplying by S + // General case, N>1: sort, update B // - k = (int)Math.Round(Math.Log(mx)/ln2); - s = xfastpow(2, -k, _params); - if( !math.isfinite(s) ) + for(i=2; i<=n; i++) { - - // - // Overflow or underflow during evaluation of S; fallback low-precision code - // - r = 0; - rerr = mx*math.machineepsilon; - for(i=0; i<=n-1; i++) + t = i; + while( t!=1 ) { - r = r+w[i]; + k = t/2; + p0 = offset+k-1; + p1 = offset+t-1; + ak = a[p0]; + at = a[p1]; + if( ak>=at ) + { + break; + } + a[p0] = at; + a[p1] = ak; + t = k; } - return; - } - while( (double)(s*mx)>=(double)(1) ) - { - s = 0.5*s; - } - while( (double)(s*mx)<(double)(0.5) ) - { - s = 2*s; - } - for(i_=0; i_<=n-1;i_++) - { - w[i_] = s*w[i_]; - } - s = 1/s; - - // - // find Chunk=2^M such that N*Chunk<2^29 - // - // we have chosen upper limit (2^29) with enough space left - // to tolerate possible problems with rounding and N's close - // to the limit, so we don't want to be very strict here. - // - k = (int)(Math.Log((double)536870912/(double)n)/ln2); - chunk = xfastpow(2, k, _params); - if( (double)(chunk)<(double)(2) ) - { - chunk = 2; } - invchunk = 1/chunk; - - // - // calculate result - // - r = 0; - for(i_=0; i_<=n-1;i_++) - { - w[i_] = chunk*w[i_]; - } - while( true ) + for(i=n-1; i>=1; i--) { - s = s*invchunk; - allzeros = true; - ks = 0; - for(i=0; i<=n-1; i++) + p0 = offset+0; + p1 = offset+i; + tmp = a[p1]; + a[p1] = a[p0]; + a[p0] = tmp; + at = tmp; + t = 0; + while( true ) { - v = w[i]; - k = (int)(v); - if( (double)(v)!=(double)(k) ) + k = 2*t+1; + if( k+1>i ) { - allzeros = false; + break; } - w[i] = chunk*(v-k); - ks = ks+k; - } - r = r+s*ks; - if( allzeros || (double)(s*n+mx)==(double)(mx) ) - { - break; + p0 = offset+t; + p1 = offset+k; + ak = a[p1]; + if( k+1ak ) + { + ak = ak1; + p1 = p1+1; + k = k+1; + } + } + if( at>=ak ) + { + break; + } + a[p1] = at; + a[p0] = ak; + t = k; } } - - // - // correct error - // - rerr = Math.Max(rerr, Math.Abs(r)*math.machineepsilon); } /************************************************************************* - Fast Pow + Heap operations: adds element to the heap + + PARAMETERS: + A - heap itself, must be at least array[0..N] + B - array of integer tags, which are updated according to + permutations in the heap + N - size of the heap (without new element). + updated on output + VA - value of the element being added + VB - value of the tag -- ALGLIB -- - Copyright 24.08.2009 by Bochkanov Sergey + Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ - private static double xfastpow(double r, - int n, + public static void tagheappushi(ref double[] a, + ref int[] b, + ref int n, + double va, + int vb, alglib.xparams _params) { - double result = 0; + int j = 0; + int k = 0; + double v = 0; - result = 0; - if( n>0 ) + if( n<0 ) { - if( n%2==0 ) + return; + } + + // + // N=0 is a special case + // + if( n==0 ) + { + a[0] = va; + b[0] = vb; + n = n+1; + return; + } + + // + // add current point to the heap + // (add to the bottom, then move up) + // + // we don't write point to the heap + // until its final position is determined + // (it allow us to reduce number of array access operations) + // + j = n; + n = n+1; + while( j>0 ) + { + k = (j-1)/2; + v = a[k]; + if( v=n ) + { + + // + // only one child. + // + // swap and terminate (because this child + // have no siblings due to heap structure) + // + v = a[k1]; + if( v>va ) + { + a[j] = v; + b[j] = b[k1]; + j = k1; + } + break; + } + else + { + + // + // two childs + // + v1 = a[k1]; + v2 = a[k2]; + if( v1>v2 ) + { + if( va=1 - /************************************************************************* - Normalizes direction/step pair: makes |D|=1, scales Stp. - If |D|=0, it returns, leavind D/Stp unchanged. + On output top element is moved to A[N-1], B[N-1], heap is reordered, N is + decreased by 1. -- ALGLIB -- - Copyright 01.04.2010 by Bochkanov Sergey + Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ - public static void linminnormalized(ref double[] d, - ref double stp, - int n, + public static void tagheappopi(ref double[] a, + ref int[] b, + ref int n, alglib.xparams _params) { - double mx = 0; - double s = 0; - int i = 0; - int i_ = 0; + double va = 0; + int vb = 0; + if( n<1 ) + { + return; + } // - // first, scale D to avoid underflow/overflow durng squaring + // N=1 is a special case // - mx = 0; - for(i=0; i<=n-1; i++) - { - mx = Math.Max(mx, Math.Abs(d[i])); - } - if( (double)(mx)==(double)(0) ) + if( n==1 ) { + n = 0; return; } - s = 1/mx; - for(i_=0; i_<=n-1;i_++) - { - d[i_] = s*d[i_]; - } - stp = stp/s; // - // normalize D + // swap top element and last element, + // then reorder heap // - s = 0.0; - for(i_=0; i_<=n-1;i_++) - { - s += d[i_]*d[i_]; - } - s = 1/Math.Sqrt(s); - for(i_=0; i_<=n-1;i_++) - { - d[i_] = s*d[i_]; - } - stp = stp/s; + va = a[n-1]; + vb = b[n-1]; + a[n-1] = a[0]; + b[n-1] = b[0]; + n = n-1; + tagheapreplacetopi(ref a, ref b, n, va, vb, _params); } /************************************************************************* - THE PURPOSE OF MCSRCH IS TO FIND A STEP WHICH SATISFIES A SUFFICIENT - DECREASE CONDITION AND A CURVATURE CONDITION. - - AT EACH STAGE THE SUBROUTINE UPDATES AN INTERVAL OF UNCERTAINTY WITH - ENDPOINTS STX AND STY. THE INTERVAL OF UNCERTAINTY IS INITIALLY CHOSEN - SO THAT IT CONTAINS A MINIMIZER OF THE MODIFIED FUNCTION - - F(X+STP*S) - F(X) - FTOL*STP*(GRADF(X)'S). - - IF A STEP IS OBTAINED FOR WHICH THE MODIFIED FUNCTION HAS A NONPOSITIVE - FUNCTION VALUE AND NONNEGATIVE DERIVATIVE, THEN THE INTERVAL OF - UNCERTAINTY IS CHOSEN SO THAT IT CONTAINS A MINIMIZER OF F(X+STP*S). - - THE ALGORITHM IS DESIGNED TO FIND A STEP WHICH SATISFIES THE SUFFICIENT - DECREASE CONDITION - - F(X+STP*S) .LE. F(X) + FTOL*STP*(GRADF(X)'S), - - AND THE CURVATURE CONDITION - - ABS(GRADF(X+STP*S)'S)) .LE. GTOL*ABS(GRADF(X)'S). - - IF FTOL IS LESS THAN GTOL AND IF, FOR EXAMPLE, THE FUNCTION IS BOUNDED - BELOW, THEN THERE IS ALWAYS A STEP WHICH SATISFIES BOTH CONDITIONS. - IF NO STEP CAN BE FOUND WHICH SATISFIES BOTH CONDITIONS, THEN THE - ALGORITHM USUALLY STOPS WHEN ROUNDING ERRORS PREVENT FURTHER PROGRESS. - IN THIS CASE STP ONLY SATISFIES THE SUFFICIENT DECREASE CONDITION. - - - :::::::::::::IMPORTANT NOTES::::::::::::: - - NOTE 1: - - This routine guarantees that it will stop at the last point where function - value was calculated. It won't make several additional function evaluations - after finding good point. So if you store function evaluations requested by - this routine, you can be sure that last one is the point where we've stopped. - - NOTE 2: - - when 0initial_point - after rounding to machine precision - - NOTE 4: - - when non-descent direction is specified, algorithm stops with MCINFO=0, - Stp=0 and initial point at X[]. - ::::::::::::::::::::::::::::::::::::::::: - - - PARAMETERS DESCRIPRION - - STAGE IS ZERO ON FIRST CALL, ZERO ON FINAL EXIT - - N IS A POSITIVE INTEGER INPUT VARIABLE SET TO THE NUMBER OF VARIABLES. - - X IS AN ARRAY OF LENGTH N. ON INPUT IT MUST CONTAIN THE BASE POINT FOR - THE LINE SEARCH. ON OUTPUT IT CONTAINS X+STP*S. - - F IS A VARIABLE. ON INPUT IT MUST CONTAIN THE VALUE OF F AT X. ON OUTPUT - IT CONTAINS THE VALUE OF F AT X + STP*S. - - G IS AN ARRAY OF LENGTH N. ON INPUT IT MUST CONTAIN THE GRADIENT OF F AT X. - ON OUTPUT IT CONTAINS THE GRADIENT OF F AT X + STP*S. - - S IS AN INPUT ARRAY OF LENGTH N WHICH SPECIFIES THE SEARCH DIRECTION. - - STP IS A NONNEGATIVE VARIABLE. ON INPUT STP CONTAINS AN INITIAL ESTIMATE - OF A SATISFACTORY STEP. ON OUTPUT STP CONTAINS THE FINAL ESTIMATE. - - FTOL AND GTOL ARE NONNEGATIVE INPUT VARIABLES. TERMINATION OCCURS WHEN THE - SUFFICIENT DECREASE CONDITION AND THE DIRECTIONAL DERIVATIVE CONDITION ARE - SATISFIED. - - XTOL IS A NONNEGATIVE INPUT VARIABLE. TERMINATION OCCURS WHEN THE RELATIVE - WIDTH OF THE INTERVAL OF UNCERTAINTY IS AT MOST XTOL. - - STPMIN AND STPMAX ARE NONNEGATIVE INPUT VARIABLES WHICH SPECIFY LOWER AND - UPPER BOUNDS FOR THE STEP. + Search first element less than T in sorted array. - MAXFEV IS A POSITIVE INTEGER INPUT VARIABLE. TERMINATION OCCURS WHEN THE - NUMBER OF CALLS TO FCN IS AT LEAST MAXFEV BY THE END OF AN ITERATION. + PARAMETERS: + A - sorted array by ascending from 0 to N-1 + N - number of elements in array + T - the desired element - INFO IS AN INTEGER OUTPUT VARIABLE SET AS FOLLOWS: - INFO = 0 IMPROPER INPUT PARAMETERS. + RESULT: + The very first element's index, which isn't less than T. + In the case when there aren't such elements, returns N. + *************************************************************************/ + public static int lowerbound(double[] a, + int n, + double t, + alglib.xparams _params) + { + int result = 0; + int l = 0; + int half = 0; + int first = 0; + int middle = 0; - INFO = 1 THE SUFFICIENT DECREASE CONDITION AND THE - DIRECTIONAL DERIVATIVE CONDITION HOLD. + l = n; + first = 0; + while( l>0 ) + { + half = l/2; + middle = first+half; + if( (double)(a[middle])<(double)(t) ) + { + first = middle+1; + l = l-half-1; + } + else + { + l = half; + } + } + result = first; + return result; + } - INFO = 2 RELATIVE WIDTH OF THE INTERVAL OF UNCERTAINTY - IS AT MOST XTOL. - INFO = 3 NUMBER OF CALLS TO FCN HAS REACHED MAXFEV. + /************************************************************************* + Search first element more than T in sorted array. - INFO = 4 THE STEP IS AT THE LOWER BOUND STPMIN. + PARAMETERS: + A - sorted array by ascending from 0 to N-1 + N - number of elements in array + T - the desired element - INFO = 5 THE STEP IS AT THE UPPER BOUND STPMAX. + RESULT: + The very first element's index, which more than T. + In the case when there aren't such elements, returns N. + *************************************************************************/ + public static int upperbound(double[] a, + int n, + double t, + alglib.xparams _params) + { + int result = 0; + int l = 0; + int half = 0; + int first = 0; + int middle = 0; - INFO = 6 ROUNDING ERRORS PREVENT FURTHER PROGRESS. - THERE MAY NOT BE A STEP WHICH SATISFIES THE - SUFFICIENT DECREASE AND CURVATURE CONDITIONS. - TOLERANCES MAY BE TOO SMALL. + l = n; + first = 0; + while( l>0 ) + { + half = l/2; + middle = first+half; + if( (double)(t)<(double)(a[middle]) ) + { + l = half; + } + else + { + first = middle+1; + l = l-half-1; + } + } + result = first; + return result; + } - NFEV IS AN INTEGER OUTPUT VARIABLE SET TO THE NUMBER OF CALLS TO FCN. - WA IS A WORK ARRAY OF LENGTH N. + /************************************************************************* + Internal TagSortFastI: sorts A[I1...I2] (both bounds are included), + applies same permutations to B. - ARGONNE NATIONAL LABORATORY. MINPACK PROJECT. JUNE 1983 - JORGE J. MORE', DAVID J. THUENTE + -- ALGLIB -- + Copyright 06.09.2010 by Bochkanov Sergey *************************************************************************/ - public static void mcsrch(int n, - ref double[] x, - ref double f, - ref double[] g, - double[] s, - ref double stp, - double stpmax, - double gtol, - ref int info, - ref int nfev, - ref double[] wa, - linminstate state, - ref int stage, + private static void tagsortfastirec(ref double[] a, + ref int[] b, + ref double[] bufa, + ref int[] bufb, + int i1, + int i2, alglib.xparams _params) { int i = 0; - double v = 0; - double p5 = 0; - double p66 = 0; - double zero = 0; - int i_ = 0; + int j = 0; + int k = 0; + int cntless = 0; + int cnteq = 0; + int cntgreater = 0; + double tmpr = 0; + int tmpi = 0; + double v0 = 0; + double v1 = 0; + double v2 = 0; + double vp = 0; // - // init + // Fast exit // - p5 = 0.5; - p66 = 0.66; - state.xtrapf = 4.0; - zero = 0; - if( (double)(stpmax)==(double)(0) ) - { - stpmax = defstpmax; - } - if( (double)(stp)<(double)(stpmin) ) - { - stp = stpmin; - } - if( (double)(stp)>(double)(stpmax) ) + if( i2<=i1 ) { - stp = stpmax; + return; } // - // Main cycle + // Non-recursive sort for small arrays // - while( true ) + if( i2-i1<=16 ) { - if( stage==0 ) + for(j=i1+1; j<=i2; j++) { // - // NEXT - // - stage = 2; - continue; - } - if( stage==2 ) - { - state.infoc = 1; - info = 0; - + // Search elements [I1..J-1] for place to insert Jth element. // - // CHECK THE INPUT PARAMETERS FOR ERRORS. + // This code stops immediately if we can leave A[J] at J-th position + // (all elements have same value of A[J] larger than any of them) // - if( (double)(stpmax)<(double)(stpmin) && (double)(stpmax)>(double)(0) ) - { - info = 5; - stp = stpmax; - stage = 0; - return; - } - if( ((((((n<=0 || (double)(stp)<=(double)(0)) || (double)(ftol)<(double)(0)) || (double)(gtol)<(double)(zero)) || (double)(xtol)<(double)(zero)) || (double)(stpmin)<(double)(zero)) || (double)(stpmax)<(double)(stpmin)) || maxfev<=0 ) + tmpr = a[j]; + tmpi = j; + for(k=j-1; k>=i1; k--) { - stage = 0; - return; + if( a[k]<=tmpr ) + { + break; + } + tmpi = k; } + k = tmpi; // - // COMPUTE THE INITIAL GRADIENT IN THE SEARCH DIRECTION - // AND CHECK THAT S IS A DESCENT DIRECTION. + // Insert Jth element into Kth position // - v = 0.0; - for(i_=0; i_<=n-1;i_++) - { - v += g[i_]*s[i_]; - } - state.dginit = v; - if( (double)(state.dginit)>=(double)(0) ) + if( k!=j ) { - stage = 0; - stp = 0; - return; + tmpr = a[j]; + tmpi = b[j]; + for(i=j-1; i>=k; i--) + { + a[i+1] = a[i]; + b[i+1] = b[i]; + } + a[k] = tmpr; + b[k] = tmpi; } + } + return; + } + + // + // Quicksort: choose pivot + // Here we assume that I2-I1>=2 + // + v0 = a[i1]; + v1 = a[i1+(i2-i1)/2]; + v2 = a[i2]; + if( v0>v1 ) + { + tmpr = v1; + v1 = v0; + v0 = tmpr; + } + if( v1>v2 ) + { + tmpr = v2; + v2 = v1; + v1 = tmpr; + } + if( v0>v1 ) + { + tmpr = v1; + v1 = v0; + v0 = tmpr; + } + vp = v1; + + // + // now pass through A/B and: + // * move elements that are LESS than VP to the left of A/B + // * move elements that are EQUAL to VP to the right of BufA/BufB (in the reverse order) + // * move elements that are GREATER than VP to the left of BufA/BufB (in the normal order + // * move elements from the tail of BufA/BufB to the middle of A/B (restoring normal order) + // * move elements from the left of BufA/BufB to the end of A/B + // + cntless = 0; + cnteq = 0; + cntgreater = 0; + for(i=i1; i<=i2; i++) + { + v0 = a[i]; + if( v0=i1; k--) { - if( (double)(state.stx)<(double)(state.sty) ) - { - state.stmin = state.stx; - state.stmax = state.sty; - } - else + if( a[k]<=tmpr ) { - state.stmin = state.sty; - state.stmax = state.stx; + break; } + tmpi = k; } - else - { - state.stmin = state.stx; - state.stmax = stp+state.xtrapf*(stp-state.stx); - } - - // - // FORCE THE STEP TO BE WITHIN THE BOUNDS STPMAX AND STPMIN. - // - if( (double)(stp)>(double)(stpmax) ) - { - stp = stpmax; - } - if( (double)(stp)<(double)(stpmin) ) - { - stp = stpmin; - } - - // - // IF AN UNUSUAL TERMINATION IS TO OCCUR THEN LET - // STP BE THE LOWEST POINT OBTAINED SO FAR. - // - if( (((state.brackt && ((double)(stp)<=(double)(state.stmin) || (double)(stp)>=(double)(state.stmax))) || nfev>=maxfev-1) || state.infoc==0) || (state.brackt && (double)(state.stmax-state.stmin)<=(double)(xtol*state.stmax)) ) - { - stp = state.stx; - } + k = tmpi; // - // EVALUATE THE FUNCTION AND GRADIENT AT STP - // AND COMPUTE THE DIRECTIONAL DERIVATIVE. + // Insert Jth element into Kth position // - for(i_=0; i_<=n-1;i_++) - { - x[i_] = wa[i_]; - } - for(i_=0; i_<=n-1;i_++) + if( k!=j ) { - x[i_] = x[i_] + stp*s[i_]; + tmpr = a[j]; + tmpr2 = b[j]; + for(i=j-1; i>=k; i--) + { + a[i+1] = a[i]; + b[i+1] = b[i]; + } + a[k] = tmpr; + b[k] = tmpr2; } - - // - // NEXT - // - stage = 4; - return; } - if( stage==4 ) + return; + } + + // + // Quicksort: choose pivot + // Here we assume that I2-I1>=16 + // + v0 = a[i1]; + v1 = a[i1+(i2-i1)/2]; + v2 = a[i2]; + if( v0>v1 ) + { + tmpr = v1; + v1 = v0; + v0 = tmpr; + } + if( v1>v2 ) + { + tmpr = v2; + v2 = v1; + v1 = tmpr; + } + if( v0>v1 ) + { + tmpr = v1; + v1 = v0; + v0 = tmpr; + } + vp = v1; + + // + // now pass through A/B and: + // * move elements that are LESS than VP to the left of A/B + // * move elements that are EQUAL to VP to the right of BufA/BufB (in the reverse order) + // * move elements that are GREATER than VP to the left of BufA/BufB (in the normal order + // * move elements from the tail of BufA/BufB to the middle of A/B (restoring normal order) + // * move elements from the left of BufA/BufB to the end of A/B + // + cntless = 0; + cnteq = 0; + cntgreater = 0; + for(i=i1; i<=i2; i++) + { + v0 = a[i]; + if( v0=(double)(state.stmax))) || state.infoc==0 ) - { - info = 6; - } - if( (((double)(stp)==(double)(stpmax) && (double)(f)<(double)(state.finit)) && (double)(f)<=(double)(state.ftest1)) && (double)(state.dg)<=(double)(state.dgtest) ) - { - info = 5; - } - if( (double)(stp)==(double)(stpmin) && (((double)(f)>=(double)(state.finit) || (double)(f)>(double)(state.ftest1)) || (double)(state.dg)>=(double)(state.dgtest)) ) - { - info = 4; - } - if( nfev>=maxfev ) - { - info = 3; - } - if( state.brackt && (double)(state.stmax-state.stmin)<=(double)(xtol*state.stmax) ) - { - info = 2; - } - if( ((double)(f)<(double)(state.finit) && (double)(f)<=(double)(state.ftest1)) && (double)(Math.Abs(state.dg))<=(double)(-(gtol*state.dginit)) ) + k = i1+cntless; + if( i!=k ) { - info = 1; + a[k] = v0; + b[k] = b[i]; } + cntless = cntless+1; + continue; + } + if( v0==vp ) + { // - // CHECK FOR TERMINATION. + // EQUAL // - if( info!=0 ) - { - - // - // Check guarantees provided by the function for INFO=1 or INFO=5 - // - if( info==1 || info==5 ) - { - v = 0.0; - for(i=0; i<=n-1; i++) - { - v = v+(wa[i]-x[i])*(wa[i]-x[i]); - } - if( (double)(f)>=(double)(state.finit) || (double)(v)==(double)(0.0) ) - { - info = 6; - } - } - stage = 0; - return; - } + k = i2-cnteq; + bufa[k] = v0; + bufb[k] = b[i]; + cnteq = cnteq+1; + continue; + } + + // + // GREATER + // + k = i1+cntgreater; + bufa[k] = v0; + bufb[k] = b[i]; + cntgreater = cntgreater+1; + } + for(i=0; i<=cnteq-1; i++) + { + j = i1+cntless+cnteq-1-i; + k = i2+i-(cnteq-1); + a[j] = bufa[k]; + b[j] = bufb[k]; + } + for(i=0; i<=cntgreater-1; i++) + { + j = i1+cntless+cnteq+i; + k = i1+i; + a[j] = bufa[k]; + b[j] = bufb[k]; + } + + // + // Sort left and right parts of the array (ignoring middle part) + // + tagsortfastrrec(ref a, ref b, ref bufa, ref bufb, i1, i1+cntless-1, _params); + tagsortfastrrec(ref a, ref b, ref bufa, ref bufb, i1+cntless+cnteq, i2, _params); + } + + + /************************************************************************* + Internal TagSortFastI: sorts A[I1...I2] (both bounds are included), + applies same permutations to B. + + -- ALGLIB -- + Copyright 06.09.2010 by Bochkanov Sergey + *************************************************************************/ + private static void tagsortfastrec(ref double[] a, + ref double[] bufa, + int i1, + int i2, + alglib.xparams _params) + { + int cntless = 0; + int cnteq = 0; + int cntgreater = 0; + int i = 0; + int j = 0; + int k = 0; + double tmpr = 0; + int tmpi = 0; + double v0 = 0; + double v1 = 0; + double v2 = 0; + double vp = 0; + + + // + // Fast exit + // + if( i2<=i1 ) + { + return; + } + + // + // Non-recursive sort for small arrays + // + if( i2-i1<=16 ) + { + for(j=i1+1; j<=i2; j++) + { // - // IN THE FIRST STAGE WE SEEK A STEP FOR WHICH THE MODIFIED - // FUNCTION HAS A NONPOSITIVE VALUE AND NONNEGATIVE DERIVATIVE. + // Search elements [I1..J-1] for place to insert Jth element. // - if( (state.stage1 && (double)(f)<=(double)(state.ftest1)) && (double)(state.dg)>=(double)(Math.Min(ftol, gtol)*state.dginit) ) + // This code stops immediatly if we can leave A[J] at J-th position + // (all elements have same value of A[J] larger than any of them) + // + tmpr = a[j]; + tmpi = j; + for(k=j-1; k>=i1; k--) { - state.stage1 = false; + if( a[k]<=tmpr ) + { + break; + } + tmpi = k; } + k = tmpi; // - // A MODIFIED FUNCTION IS USED TO PREDICT THE STEP ONLY IF - // WE HAVE NOT OBTAINED A STEP FOR WHICH THE MODIFIED - // FUNCTION HAS A NONPOSITIVE FUNCTION VALUE AND NONNEGATIVE - // DERIVATIVE, AND IF A LOWER FUNCTION VALUE HAS BEEN - // OBTAINED BUT THE DECREASE IS NOT SUFFICIENT. + // Insert Jth element into Kth position // - if( (state.stage1 && (double)(f)<=(double)(state.fx)) && (double)(f)>(double)(state.ftest1) ) - { - - // - // DEFINE THE MODIFIED FUNCTION AND DERIVATIVE VALUES. - // - state.fm = f-stp*state.dgtest; - state.fxm = state.fx-state.stx*state.dgtest; - state.fym = state.fy-state.sty*state.dgtest; - state.dgm = state.dg-state.dgtest; - state.dgxm = state.dgx-state.dgtest; - state.dgym = state.dgy-state.dgtest; - - // - // CALL CSTEP TO UPDATE THE INTERVAL OF UNCERTAINTY - // AND TO COMPUTE THE NEW STEP. - // - mcstep(ref state.stx, ref state.fxm, ref state.dgxm, ref state.sty, ref state.fym, ref state.dgym, ref stp, state.fm, state.dgm, ref state.brackt, state.stmin, state.stmax, ref state.infoc, _params); - - // - // RESET THE FUNCTION AND GRADIENT VALUES FOR F. - // - state.fx = state.fxm+state.stx*state.dgtest; - state.fy = state.fym+state.sty*state.dgtest; - state.dgx = state.dgxm+state.dgtest; - state.dgy = state.dgym+state.dgtest; - } - else + if( k!=j ) { - - // - // CALL MCSTEP TO UPDATE THE INTERVAL OF UNCERTAINTY - // AND TO COMPUTE THE NEW STEP. - // - mcstep(ref state.stx, ref state.fx, ref state.dgx, ref state.sty, ref state.fy, ref state.dgy, ref stp, f, state.dg, ref state.brackt, state.stmin, state.stmax, ref state.infoc, _params); + tmpr = a[j]; + for(i=j-1; i>=k; i--) + { + a[i+1] = a[i]; + } + a[k] = tmpr; } + } + return; + } + + // + // Quicksort: choose pivot + // Here we assume that I2-I1>=16 + // + v0 = a[i1]; + v1 = a[i1+(i2-i1)/2]; + v2 = a[i2]; + if( v0>v1 ) + { + tmpr = v1; + v1 = v0; + v0 = tmpr; + } + if( v1>v2 ) + { + tmpr = v2; + v2 = v1; + v1 = tmpr; + } + if( v0>v1 ) + { + tmpr = v1; + v1 = v0; + v0 = tmpr; + } + vp = v1; + + // + // now pass through A/B and: + // * move elements that are LESS than VP to the left of A/B + // * move elements that are EQUAL to VP to the right of BufA/BufB (in the reverse order) + // * move elements that are GREATER than VP to the left of BufA/BufB (in the normal order + // * move elements from the tail of BufA/BufB to the middle of A/B (restoring normal order) + // * move elements from the left of BufA/BufB to the end of A/B + // + cntless = 0; + cnteq = 0; + cntgreater = 0; + for(i=i1; i<=i2; i++) + { + v0 = a[i]; + if( v0=(double)(p66*state.width1) ) - { - stp = state.stx+p5*(state.sty-state.stx); - } - state.width1 = state.width; - state.width = Math.Abs(state.sty-state.stx); + a[k] = v0; } + cntless = cntless+1; + continue; + } + if( v0==vp ) + { // - // NEXT. + // EQUAL // - stage = 3; + k = i2-cnteq; + bufa[k] = v0; + cnteq = cnteq+1; continue; } + + // + // GREATER + // + k = i1+cntgreater; + bufa[k] = v0; + cntgreater = cntgreater+1; + } + for(i=0; i<=cnteq-1; i++) + { + j = i1+cntless+cnteq-1-i; + k = i2+i-(cnteq-1); + a[j] = bufa[k]; + } + for(i=0; i<=cntgreater-1; i++) + { + j = i1+cntless+cnteq+i; + k = i1+i; + a[j] = bufa[k]; + } + + // + // Sort left and right parts of the array (ignoring middle part) + // + tagsortfastrec(ref a, ref bufa, i1, i1+cntless-1, _params); + tagsortfastrec(ref a, ref bufa, i1+cntless+cnteq, i2, _params); + } + + + } + public partial class apstruct + { + /************************************************************************* + This structure is used to store set of N possible integers, in [0,N) range. + The structure needs O(N) memory, independently from the actual set size. + + This structure allows external code to use following fields: + * N - maximum set size + * NStored - number of elements currently in the set + * Items - first NStored elements are UNSORTED items + * LocationOf - array[N] that allows quick access by key. If item I is present + in the set, LocationOf[I]>=0 and stores position in Items[] of element + I, i.e. Items[LocationOf[I]]=I. + If item I is not present, LocationOf[I]<0. + *************************************************************************/ + public class niset : apobject + { + public int n; + public int nstored; + public int[] items; + public int[] locationof; + public int iteridx; + public niset() + { + init(); + } + public override void init() + { + items = new int[0]; + locationof = new int[0]; + } + public override alglib.apobject make_copy() + { + niset _result = new niset(); + _result.n = n; + _result.nstored = nstored; + _result.items = (int[])items.Clone(); + _result.locationof = (int[])locationof.Clone(); + _result.iteridx = iteridx; + return _result; + } + }; + + + /************************************************************************* + This structure is used to store K sets of N possible integers, in [0,N) each. + The structure needs at least O(N) temporary memory. + + Storage modes: + * 0 default unsorted mode + *************************************************************************/ + public class kniset : apobject + { + public int storagemode; + public int k; + public int n; + public int[] flagarray; + public int[] vbegin; + public int[] vallocated; + public int[] vcnt; + public int[] data; + public int dataused; + public int iterrow; + public int iteridx; + public kniset() + { + init(); + } + public override void init() + { + flagarray = new int[0]; + vbegin = new int[0]; + vallocated = new int[0]; + vcnt = new int[0]; + data = new int[0]; + } + public override alglib.apobject make_copy() + { + kniset _result = new kniset(); + _result.storagemode = storagemode; + _result.k = k; + _result.n = n; + _result.flagarray = (int[])flagarray.Clone(); + _result.vbegin = (int[])vbegin.Clone(); + _result.vallocated = (int[])vallocated.Clone(); + _result.vcnt = (int[])vcnt.Clone(); + _result.data = (int[])data.Clone(); + _result.dataused = dataused; + _result.iterrow = iterrow; + _result.iteridx = iteridx; + return _result; } + }; + + + + + public const int knisheadersize = 2; + + + /************************************************************************* + Initializes n-set by empty structure. + + IMPORTANT: this function need O(N) time for initialization. It is recommended + to reduce its usage as much as possible, and use nisClear() + where possible. + + INPUT PARAMETERS + N - possible set size + + OUTPUT PARAMETERS + SA - empty N-set + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void nisinitemptyslow(int n, + niset sa, + alglib.xparams _params) + { + sa.n = n; + sa.nstored = 0; + ablasf.isetallocv(n, -999999999, ref sa.locationof, _params); + ablasf.isetallocv(n, -999999999, ref sa.items, _params); } /************************************************************************* - These functions perform Armijo line search using at most FMAX function - evaluations. It doesn't enforce some kind of " sufficient decrease" - criterion - it just tries different Armijo steps and returns optimum found - so far. + Copies n-set to properly initialized target set. The target set has to be + properly initialized, and it can be non-empty. If it is non-empty, its + contents is quickly erased before copying. - Optimization is done using F-rcomm interface: - * ArmijoCreate initializes State structure - (reusing previously allocated buffers) - * ArmijoIteration is subsequently called - * ArmijoResults returns results + The cost of this function is O(max(SrcSize,DstSize)) - INPUT PARAMETERS: - N - problem size - X - array[N], starting point - F - F(X+S*STP) - S - step direction, S>0 - STP - step length - STPMAX - maximum value for STP or zero (if no limit is imposed) - FMAX - maximum number of function evaluations - State - optimization state + INPUT PARAMETERS + SSrc - source N-set + SDst - destination N-set (has same size as SSrc) + + OUTPUT PARAMETERS + SDst - copy of SSrc - -- ALGLIB -- - Copyright 05.10.2010 by Bochkanov Sergey + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. *************************************************************************/ - public static void armijocreate(int n, - double[] x, - double f, - double[] s, - double stp, - double stpmax, - int fmax, - armijostate state, + public static void niscopy(niset ssrc, + niset sdst, alglib.xparams _params) { - int i_ = 0; + int ns = 0; + int i = 0; + int k = 0; - if( alglib.ap.len(state.x)=0 ) { - state.xbase = new double[n]; + return; } - if( alglib.ap.len(state.s)=0 ) + { + item = sa.items[ns-1]; + sa.items[loc] = item; + sa.locationof[item] = loc; + sa.locationof[j] = -1; + ns = ns-1; + } + } } - state.stpmax = stpmax; - state.fmax = fmax; - state.stplen = stp; - state.fcur = f; - state.n = n; - for(i_=0; i_<=n-1;i_++) + else { - state.xbase[i_] = x[i_]; + i = 0; + while( i=0 ) + { + item = sa.items[ns-1]; + sa.items[i] = item; + sa.locationof[item] = i; + sa.locationof[j] = -1; + ns = ns-1; + } + else + { + i = i+1; + } + } } - for(i_=0; i_<=n-1;i_++) + sa.nstored = ns; + } + + + /************************************************************************* + Clears set + + INPUT PARAMETERS + SA - set to be cleared + + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void nisclear(niset sa, + alglib.xparams _params) + { + int i = 0; + int ns = 0; + + ns = sa.nstored; + for(i=0; i<=ns-1; i++) { - state.s[i_] = s[i_]; + sa.locationof[sa.items[i]] = -1; } - state.rstate.ia = new int[0+1]; - state.rstate.ra = new double[0+1]; - state.rstate.stage = -1; + sa.nstored = 0; } /************************************************************************* - This is rcomm-based search function + Counts set elements + + INPUT PARAMETERS + SA - set + + RESULT + number of elements in SA + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static int niscount(niset sa, + alglib.xparams _params) + { + int result = 0; + + result = sa.nstored; + return result; + } + + + /************************************************************************* + Compare two sets, returns True for equal sets + + INPUT PARAMETERS + S0 - set 0 + S1 - set 1, must have same parameter N as set 0 + + RESULT + True, if sets are equal - -- ALGLIB -- - Copyright 05.10.2010 by Bochkanov Sergey + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. *************************************************************************/ - public static bool armijoiteration(armijostate state, + public static bool nisequal(niset s0, + niset s1, alglib.xparams _params) { bool result = new bool(); - double v = 0; - int n = 0; - int i_ = 0; + int i = 0; + int ns0 = 0; + int ns1 = 0; - - // - // Reverse communication preparations - // I know it looks ugly, but it works the same way - // anywhere from C++ to Python. - // - // This code initializes locals by: - // * random values determined during code - // generation - on first subroutine call - // * values from previous call - on subsequent calls - // - if( state.rstate.stage>=0 ) - { - n = state.rstate.ia[0]; - v = state.rstate.ra[0]; - } - else - { - n = 359; - v = -58.0; - } - if( state.rstate.stage==0 ) - { - goto lbl_0; - } - if( state.rstate.stage==1 ) - { - goto lbl_1; - } - if( state.rstate.stage==2 ) - { - goto lbl_2; - } - if( state.rstate.stage==3 ) - { - goto lbl_3; - } - - // - // Routine body - // - if( ((double)(state.stplen)<=(double)(0) || (double)(state.stpmax)<(double)(0)) || state.fmax<2 ) + result = false; + if( s0.n!=s1.n ) { - state.info = 0; - result = false; return result; } - if( (double)(state.stplen)<=(double)(stpmin) ) + if( s0.nstored!=s1.nstored ) { - state.info = 4; - result = false; return result; } - n = state.n; - state.nfev = 0; - - // - // We always need F - // - state.needf = true; - - // - // Bound StpLen - // - if( (double)(state.stplen)>(double)(state.stpmax) && (double)(state.stpmax)!=(double)(0) ) - { - state.stplen = state.stpmax; - } - - // - // Increase length - // - v = state.stplen*armijofactor; - if( (double)(v)>(double)(state.stpmax) && (double)(state.stpmax)!=(double)(0) ) - { - v = state.stpmax; - } - for(i_=0; i_<=n-1;i_++) - { - state.x[i_] = state.xbase[i_]; - } - for(i_=0; i_<=n-1;i_++) - { - state.x[i_] = state.x[i_] + v*state.s[i_]; - } - state.rstate.stage = 0; - goto lbl_rcomm; - lbl_0: - state.nfev = state.nfev+1; - if( (double)(state.f)>=(double)(state.fcur) ) + ns0 = s0.nstored; + ns1 = s1.nstored; + for(i=0; i<=ns0-1; i++) { - goto lbl_4; + if( s1.locationof[s0.items[i]]<0 ) + { + return result; + } } - state.stplen = v; - state.fcur = state.f; - lbl_6: - if( false ) + for(i=0; i<=ns1-1; i++) { - goto lbl_7; + if( s0.locationof[s1.items[i]]<0 ) + { + return result; + } } + result = true; + return result; + } + + + /************************************************************************* + Prepares iteration over set + + INPUT PARAMETERS + SA - set - // - // test stopping conditions - // - if( state.nfev>=state.fmax ) - { - state.info = 3; - result = false; - return result; - } - if( (double)(state.stplen)>=(double)(state.stpmax) ) + OUTPUT PARAMETERS + SA - SA ready for repeated calls of nisEnumerate() + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void nisstartenumeration(niset sa, + alglib.xparams _params) + { + sa.iteridx = 0; + } + + + /************************************************************************* + Iterates over the set. Subsequent calls return True and set J to new set + item until iteration stops and False is returned. + + INPUT PARAMETERS + SA - n-set + + OUTPUT PARAMETERS + J - if: + * Result=True - index of element in the set + * Result=False - not set + + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static bool nisenumerate(niset sa, + ref int i, + alglib.xparams _params) + { + bool result = new bool(); + int k = 0; + + i = 0; + + k = sa.iteridx; + if( k>=sa.nstored ) { - state.info = 5; result = false; return result; } - - // - // evaluate F - // - v = state.stplen*armijofactor; - if( (double)(v)>(double)(state.stpmax) && (double)(state.stpmax)!=(double)(0) ) + i = sa.items[k]; + sa.iteridx = k+1; + result = true; + return result; + } + + + /************************************************************************* + Compresses internal storage, reclaiming previously dropped blocks. To be + used internally by kn-set modification functions. + + INPUT PARAMETERS + SA - kn-set to compress + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void kniscompressstorage(kniset sa, + alglib.xparams _params) + { + int i = 0; + int blocklen = 0; + int setidx = 0; + int srcoffs = 0; + int dstoffs = 0; + + alglib.ap.assert(sa.storagemode==0, "knisCompressStorage: unexpected storage mode"); + srcoffs = 0; + dstoffs = 0; + while( srcoffs=knisheadersize, "knisCompressStorage: integrity check 6385 failed"); + if( setidx<0 ) + { + srcoffs = srcoffs+blocklen; + continue; + } + if( srcoffs!=dstoffs ) + { + for(i=0; i<=blocklen-1; i++) + { + sa.data[dstoffs+i] = sa.data[srcoffs+i]; + } + sa.vbegin[setidx] = dstoffs+knisheadersize; + } + dstoffs = dstoffs+blocklen; + srcoffs = srcoffs+blocklen; } - for(i_=0; i_<=n-1;i_++) + alglib.ap.assert(srcoffs==sa.dataused, "knisCompressStorage: integrity check 9464 failed"); + sa.dataused = dstoffs; + } + + + /************************************************************************* + Reallocates internal storage for set #SetIdx, increasing its capacity to + NewAllocated exactly. This function may invalidate internal pointers for + ALL sets in the kn-set structure because it may perform storage + compression in order to reclaim previously freed space. + + INPUT PARAMETERS + SA - kn-set structure + SetIdx - set to reallocate + NewAllocated - new size for the set, must be at least equal to already + allocated + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void knisreallocate(kniset sa, + int setidx, + int newallocated, + alglib.xparams _params) + { + int oldbegin = 0; + int oldcnt = 0; + int newbegin = 0; + int j = 0; + + alglib.ap.assert(sa.storagemode==0, "knisReallocate: unexpected storage mode"); + if( alglib.ap.len(sa.data)0 + N - set size, N>=0 + kPrealloc - preallocate place per set (can be zero), >=0 - // - // make decision - // - if( (double)(state.f)<(double)(state.fcur) ) + OUTPUT PARAMETERS + SA - K sets of N elements, initially empty + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void knisinitunsorted(int k, + int n, + int kprealloc, + kniset sa, + alglib.xparams _params) + { + int i = 0; + + alglib.ap.assert(k>0, "knisInitUnsorted: K<=0"); + alglib.ap.assert(n>=0, "knisInitUnsorted: N<0"); + alglib.ap.assert(kprealloc>=0, "knisInitUnsorted: kPrealloc<0"); + sa.storagemode = 0; + sa.k = k; + sa.n = n; + ablasf.isetallocv(n, -1, ref sa.flagarray, _params); + ablasf.isetallocv(k, kprealloc, ref sa.vallocated, _params); + apserv.ivectorsetlengthatleast(ref sa.vbegin, k, _params); + sa.vbegin[0] = knisheadersize; + for(i=1; i<=k-1; i++) { - state.stplen = v; - state.fcur = state.f; + sa.vbegin[i] = sa.vbegin[i-1]+sa.vallocated[i-1]+knisheadersize; } - else + sa.dataused = sa.vbegin[k-1]+sa.vallocated[k-1]; + apserv.ivectorsetlengthatleast(ref sa.data, sa.dataused, _params); + for(i=0; i<=k-1; i++) { - state.info = 1; - result = false; - return result; + sa.data[sa.vbegin[i]-2] = knisheadersize+sa.vallocated[i]; + sa.data[sa.vbegin[i]-1] = i; } - goto lbl_6; - lbl_7: - lbl_4: + ablasf.isetallocv(k, 0, ref sa.vcnt, _params); + } + + + /************************************************************************* + Allows direct access to internal storage of kn-set structure - returns + range of elements SA.Data[idxBegin...idxEnd-1] used to store K-th set + + INPUT PARAMETERS + SA - kn-set + K - set index - // - // Decrease length - // - v = state.stplen/armijofactor; - for(i_=0; i_<=n-1;i_++) - { - state.x[i_] = state.xbase[i_]; - } - for(i_=0; i_<=n-1;i_++) - { - state.x[i_] = state.x[i_] + v*state.s[i_]; - } - state.rstate.stage = 2; - goto lbl_rcomm; - lbl_2: - state.nfev = state.nfev+1; - if( (double)(state.f)>=(double)(state.fcur) ) - { - goto lbl_8; - } - state.stplen = state.stplen/armijofactor; - state.fcur = state.f; - lbl_10: - if( false ) - { - goto lbl_11; - } + OUTPUT PARAMETERS + idxBegin, + idxEnd - half-range [idxBegin,idxEnd) of SA.Data that stores + K-th set + + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void knisdirectaccess(kniset sa, + int k, + ref int idxbegin, + ref int idxend, + alglib.xparams _params) + { + idxbegin = 0; + idxend = 0; + + alglib.ap.assert(sa.storagemode==0, "knisDirectAccess: unexpected storage mode"); + idxbegin = sa.vbegin[k]; + idxend = idxbegin+sa.vcnt[k]; + } + + + /************************************************************************* + Pops last element from the K-th set. + + INPUT PARAMETERS + SA - kn-set; K-th set must include at least one element, + otherwise an exception is generated + K - set index - // - // test stopping conditions - // - if( state.nfev>=state.fmax ) - { - state.info = 3; - result = false; - return result; - } - if( (double)(state.stplen)<=(double)(stpmin) ) - { - state.info = 4; - result = false; - return result; - } + RESULT: + last element in K-th set + + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static int knispoplast(kniset sa, + int k, + alglib.xparams _params) + { + int result = 0; + int c = 0; + + alglib.ap.assert(sa.storagemode==0, "knisPopLast: unexpected storage mode"); + c = sa.vcnt[k]; + alglib.ap.assert(c>0, "knisDirectAccess: K-th set is empty"); + result = sa.data[sa.vbegin[k]+c-1]; + sa.vcnt[k] = c-1; + return result; + } + + + /************************************************************************* + Add K-th element to I-th set. If an element already exists in the target, + a duplicate entry is added. + + INPUT PARAMETERS + SA - kn-set + I - set index + K - element to add - // - // evaluate F - // - v = state.stplen/armijofactor; - for(i_=0; i_<=n-1;i_++) - { - state.x[i_] = state.xbase[i_]; - } - for(i_=0; i_<=n-1;i_++) + OUTPUT PARAMETERS + SA - modified SA + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void knisaddnewelement(kniset sa, + int i, + int k, + alglib.xparams _params) + { + int cnt = 0; + + alglib.ap.assert(sa.storagemode==0, "knisAddNewElement: unexpected storage mode"); + cnt = sa.vcnt[i]; + if( cnt==sa.vallocated[i] ) { - state.x[i_] = state.x[i_] + v*state.s[i_]; + knisreallocate(sa, i, 2*sa.vallocated[i]+1, _params); } - state.rstate.stage = 3; - goto lbl_rcomm; - lbl_3: - state.nfev = state.nfev+1; + sa.data[sa.vbegin[i]+cnt] = k; + sa.vcnt[i] = cnt+1; + } + + + /************************************************************************* + Clear k-th kn-set in collection. + + Freed memory is NOT reclaimed for future garbage collection. + + INPUT PARAMETERS + SA - kn-set structure + K - set index + + OUTPUT PARAMETERS + SA - K-th set was cleared + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void knisclearkthnoreclaim(kniset sa, + int k, + alglib.xparams _params) + { + alglib.ap.assert(sa.storagemode==0, "knisClearKthNoReclaim: unexpected storage mode"); + sa.vcnt[k] = 0; + } + + + /************************************************************************* + Clear k-th kn-set in collection. + + Freed memory is reclaimed for future garbage collection. This function is + NOT recommended if you intend to add elements to this set in some future, + because every addition will result in reallocation of previously freed + memory. Use knsClearKthNoReclaim(). + + INPUT PARAMETERS + SA - kn-set structure + K - set index - // - // make decision - // - if( (double)(state.f)<(double)(state.fcur) ) - { - state.stplen = state.stplen/armijofactor; - state.fcur = state.f; - } - else + OUTPUT PARAMETERS + SA - K-th set was cleared + + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. + *************************************************************************/ + public static void knisclearkthreclaim(kniset sa, + int k, + alglib.xparams _params) + { + int idxbegin = 0; + int allocated = 0; + + alglib.ap.assert(sa.storagemode==0, "knisClearKthReclaim: unexpected storage mode"); + idxbegin = sa.vbegin[k]; + allocated = sa.vallocated[k]; + sa.vcnt[k] = 0; + if( allocated>=knisheadersize ) { - state.info = 1; - result = false; - return result; + sa.data[idxbegin-2] = 2; + sa.data[idxbegin+0] = allocated; + sa.data[idxbegin+1] = -1; + sa.vallocated[k] = 0; } - goto lbl_10; - lbl_11: - lbl_8: - - // - // Nothing to be done - // - state.info = 1; - result = false; - return result; - - // - // Saving state - // - lbl_rcomm: - result = true; - state.rstate.ia[0] = n; - state.rstate.ra[0] = v; - return result; } /************************************************************************* - Results of Armijo search + Counts elements of K-th set of S0 (duplicates are counted as distinct elements). - OUTPUT PARAMETERS: - INFO - on output it is set to one of the return codes: - * 0 improper input params - * 1 optimum step is found with at most FMAX evaluations - * 3 FMAX evaluations were used, - X contains optimum found so far - * 4 step is at lower bound STPMIN - * 5 step is at upper bound - STP - step length (in case of failure it is still returned) - F - function value (in case of failure it is still returned) + INPUT PARAMETERS + S0 - kn-set structure + K - set index in the structure S0 + + RESULT + K-th set element count - -- ALGLIB -- - Copyright 05.10.2010 by Bochkanov Sergey + -- ALGLIB PROJECT -- + Copyright 05.10.2020 by Bochkanov Sergey. *************************************************************************/ - public static void armijoresults(armijostate state, - ref int info, - ref double stp, - ref double f, + public static int kniscountkth(kniset s0, + int k, alglib.xparams _params) { - info = state.info; - stp = state.stplen; - f = state.fcur; + int result = 0; + + alglib.ap.assert(s0.storagemode==0, "knisCountKth: unexpected storage mode"); + result = s0.vcnt[k]; + return result; } - private static void mcstep(ref double stx, - ref double fx, - ref double dx, - ref double sty, - ref double fy, - ref double dy, - ref double stp, - double fp, - double dp, - ref bool brackt, - double stmin, - double stmax, - ref int info, + } + public partial class creflections + { + /************************************************************************* + Generation of an elementary complex reflection transformation + + The subroutine generates elementary complex reflection H of order N, so + that, for a given X, the following equality holds true: + + ( X(1) ) ( Beta ) + H' * ( .. ) = ( 0 ), H'*H = I, Beta is a real number + ( X(n) ) ( 0 ) + + where + + ( V(1) ) + H = 1 - Tau * ( .. ) * ( conj(V(1)), ..., conj(V(n)) ) + ( V(n) ) + + where the first component of vector V equals 1. + + Input parameters: + X - vector. Array with elements [1..N]. + N - reflection order. + + Output parameters: + X - components from 2 to N are replaced by vector V. + The first component is replaced with parameter Beta. + Tau - scalar value Tau. + + This subroutine is the modification of CLARFG subroutines from the LAPACK + library. It has similar functionality except for the fact that it doesn't + handle errors when intermediate results cause an overflow. + + -- LAPACK auxiliary routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + September 30, 1994 + *************************************************************************/ + public static void complexgeneratereflection(ref complex[] x, + int n, + ref complex tau, alglib.xparams _params) { - bool bound = new bool(); - double gamma = 0; - double p = 0; - double q = 0; - double r = 0; + int j = 0; + complex alpha = 0; + double alphi = 0; + double alphr = 0; + double beta = 0; + double xnorm = 0; + double mx = 0; + complex t = 0; double s = 0; - double sgnd = 0; - double stpc = 0; - double stpf = 0; - double stpq = 0; - double theta = 0; + complex v = 0; + int i_ = 0; - info = 0; - - // - // CHECK THE INPUT PARAMETERS FOR ERRORS. - // - if( ((brackt && ((double)(stp)<=(double)(Math.Min(stx, sty)) || (double)(stp)>=(double)(Math.Max(stx, sty)))) || (double)(dx*(stp-stx))>=(double)(0)) || (double)(stmax)<(double)(stmin) ) + tau = 0; + + if( n<=0 ) { + tau = 0; return; } // - // DETERMINE IF THE DERIVATIVES HAVE OPPOSITE SIGN. - // - sgnd = dp*(dx/Math.Abs(dx)); - - // - // FIRST CASE. A HIGHER FUNCTION VALUE. - // THE MINIMUM IS BRACKETED. IF THE CUBIC STEP IS CLOSER - // TO STX THAN THE QUADRATIC STEP, THE CUBIC STEP IS TAKEN, - // ELSE THE AVERAGE OF THE CUBIC AND QUADRATIC STEPS IS TAKEN. + // Scale if needed (to avoid overflow/underflow during intermediate + // calculations). // - if( (double)(fp)>(double)(fx) ) + mx = 0; + for(j=1; j<=n; j++) { - info = 1; - bound = true; - theta = 3*(fx-fp)/(stp-stx)+dx+dp; - s = Math.Max(Math.Abs(theta), Math.Max(Math.Abs(dx), Math.Abs(dp))); - gamma = s*Math.Sqrt(math.sqr(theta/s)-dx/s*(dp/s)); - if( (double)(stp)<(double)(stx) ) - { - gamma = -gamma; - } - p = gamma-dx+theta; - q = gamma-dx+gamma+dp; - r = p/q; - stpc = stx+r*(stp-stx); - stpq = stx+dx/((fx-fp)/(stp-stx)+dx)/2*(stp-stx); - if( (double)(Math.Abs(stpc-stx))<(double)(Math.Abs(stpq-stx)) ) - { - stpf = stpc; - } - else - { - stpf = stpc+(stpq-stpc)/2; - } - brackt = true; + mx = Math.Max(math.abscomplex(x[j]), mx); } - else + s = 1; + if( (double)(mx)!=(double)(0) ) { - if( (double)(sgnd)<(double)(0) ) + if( (double)(mx)<(double)(1) ) { - - // - // SECOND CASE. A LOWER FUNCTION VALUE AND DERIVATIVES OF - // OPPOSITE SIGN. THE MINIMUM IS BRACKETED. IF THE CUBIC - // STEP IS CLOSER TO STX THAN THE QUADRATIC (SECANT) STEP, - // THE CUBIC STEP IS TAKEN, ELSE THE QUADRATIC STEP IS TAKEN. - // - info = 2; - bound = false; - theta = 3*(fx-fp)/(stp-stx)+dx+dp; - s = Math.Max(Math.Abs(theta), Math.Max(Math.Abs(dx), Math.Abs(dp))); - gamma = s*Math.Sqrt(math.sqr(theta/s)-dx/s*(dp/s)); - if( (double)(stp)>(double)(stx) ) - { - gamma = -gamma; - } - p = gamma-dp+theta; - q = gamma-dp+gamma+dx; - r = p/q; - stpc = stp+r*(stx-stp); - stpq = stp+dp/(dp-dx)*(stx-stp); - if( (double)(Math.Abs(stpc-stp))>(double)(Math.Abs(stpq-stp)) ) - { - stpf = stpc; - } - else + s = Math.Sqrt(math.minrealnumber); + v = 1/s; + for(i_=1; i_<=n;i_++) { - stpf = stpq; + x[i_] = v*x[i_]; } - brackt = true; } else { - if( (double)(Math.Abs(dp))<(double)(Math.Abs(dx)) ) - { - - // - // THIRD CASE. A LOWER FUNCTION VALUE, DERIVATIVES OF THE - // SAME SIGN, AND THE MAGNITUDE OF THE DERIVATIVE DECREASES. - // THE CUBIC STEP IS ONLY USED IF THE CUBIC TENDS TO INFINITY - // IN THE DIRECTION OF THE STEP OR IF THE MINIMUM OF THE CUBIC - // IS BEYOND STP. OTHERWISE THE CUBIC STEP IS DEFINED TO BE - // EITHER STPMIN OR STPMAX. THE QUADRATIC (SECANT) STEP IS ALSO - // COMPUTED AND IF THE MINIMUM IS BRACKETED THEN THE THE STEP - // CLOSEST TO STX IS TAKEN, ELSE THE STEP FARTHEST AWAY IS TAKEN. - // - info = 3; - bound = true; - theta = 3*(fx-fp)/(stp-stx)+dx+dp; - s = Math.Max(Math.Abs(theta), Math.Max(Math.Abs(dx), Math.Abs(dp))); - - // - // THE CASE GAMMA = 0 ONLY ARISES IF THE CUBIC DOES NOT TEND - // TO INFINITY IN THE DIRECTION OF THE STEP. - // - gamma = s*Math.Sqrt(Math.Max(0, math.sqr(theta/s)-dx/s*(dp/s))); - if( (double)(stp)>(double)(stx) ) - { - gamma = -gamma; - } - p = gamma-dp+theta; - q = gamma+(dx-dp)+gamma; - r = p/q; - if( (double)(r)<(double)(0) && (double)(gamma)!=(double)(0) ) - { - stpc = stp+r*(stx-stp); - } - else - { - if( (double)(stp)>(double)(stx) ) - { - stpc = stmax; - } - else - { - stpc = stmin; - } - } - stpq = stp+dp/(dp-dx)*(stx-stp); - if( brackt ) - { - if( (double)(Math.Abs(stp-stpc))<(double)(Math.Abs(stp-stpq)) ) - { - stpf = stpc; - } - else - { - stpf = stpq; - } - } - else - { - if( (double)(Math.Abs(stp-stpc))>(double)(Math.Abs(stp-stpq)) ) - { - stpf = stpc; - } - else - { - stpf = stpq; - } - } - } - else + s = Math.Sqrt(math.maxrealnumber); + v = 1/s; + for(i_=1; i_<=n;i_++) { - - // - // FOURTH CASE. A LOWER FUNCTION VALUE, DERIVATIVES OF THE - // SAME SIGN, AND THE MAGNITUDE OF THE DERIVATIVE DOES - // NOT DECREASE. IF THE MINIMUM IS NOT BRACKETED, THE STEP - // IS EITHER STPMIN OR STPMAX, ELSE THE CUBIC STEP IS TAKEN. - // - info = 4; - bound = false; - if( brackt ) - { - theta = 3*(fp-fy)/(sty-stp)+dy+dp; - s = Math.Max(Math.Abs(theta), Math.Max(Math.Abs(dy), Math.Abs(dp))); - gamma = s*Math.Sqrt(math.sqr(theta/s)-dy/s*(dp/s)); - if( (double)(stp)>(double)(sty) ) - { - gamma = -gamma; - } - p = gamma-dp+theta; - q = gamma-dp+gamma+dy; - r = p/q; - stpc = stp+r*(sty-stp); - stpf = stpc; - } - else - { - if( (double)(stp)>(double)(stx) ) - { - stpf = stmax; - } - else - { - stpf = stmin; - } - } + x[i_] = v*x[i_]; } } } // - // UPDATE THE INTERVAL OF UNCERTAINTY. THIS UPDATE DOES NOT - // DEPEND ON THE NEW STEP OR THE CASE ANALYSIS ABOVE. + // calculate // - if( (double)(fp)>(double)(fx) ) + alpha = x[1]; + mx = 0; + for(j=2; j<=n; j++) { - sty = stp; - fy = fp; - dy = dp; + mx = Math.Max(math.abscomplex(x[j]), mx); } - else + xnorm = 0; + if( (double)(mx)!=(double)(0) ) { - if( (double)(sgnd)<(double)(0.0) ) + for(j=2; j<=n; j++) { - sty = stx; - fy = fx; - dy = dx; + t = x[j]/mx; + xnorm = xnorm+(t*math.conj(t)).x; } - stx = stp; - fx = fp; - dx = dp; + xnorm = Math.Sqrt(xnorm)*mx; } - - // - // COMPUTE THE NEW STEP AND SAFEGUARD IT. - // - stpf = Math.Min(stmax, stpf); - stpf = Math.Max(stmin, stpf); - stp = stpf; - if( brackt && bound ) + alphr = alpha.x; + alphi = alpha.y; + if( (double)(xnorm)==(double)(0) && (double)(alphi)==(double)(0) ) { - if( (double)(sty)>(double)(stx) ) - { - stp = Math.Min(stx+0.66*(sty-stx), stp); - } - else + tau = 0; + x[1] = x[1]*s; + return; + } + mx = Math.Max(Math.Abs(alphr), Math.Abs(alphi)); + mx = Math.Max(mx, Math.Abs(xnorm)); + beta = -(mx*Math.Sqrt(math.sqr(alphr/mx)+math.sqr(alphi/mx)+math.sqr(xnorm/mx))); + if( (double)(alphr)<(double)(0) ) + { + beta = -beta; + } + tau.x = (beta-alphr)/beta; + tau.y = -(alphi/beta); + alpha = 1/(alpha-beta); + if( n>1 ) + { + for(i_=2; i_<=n;i_++) { - stp = Math.Max(stx+0.66*(sty-stx), stp); + x[i_] = alpha*x[i_]; } } + alpha = beta; + x[1] = alpha; + + // + // Scale back + // + x[1] = x[1]*s; } - } - public class nearunityunit - { - public static double nulog1p(double x, - alglib.xparams _params) - { - double result = 0; - double z = 0; - double lp = 0; - double lq = 0; - - z = 1.0+x; - if( (double)(z)<(double)(0.70710678118654752440) || (double)(z)>(double)(1.41421356237309504880) ) - { - result = Math.Log(z); - return result; - } - z = x*x; - lp = 4.5270000862445199635215E-5; - lp = lp*x+4.9854102823193375972212E-1; - lp = lp*x+6.5787325942061044846969E0; - lp = lp*x+2.9911919328553073277375E1; - lp = lp*x+6.0949667980987787057556E1; - lp = lp*x+5.7112963590585538103336E1; - lp = lp*x+2.0039553499201281259648E1; - lq = 1.0000000000000000000000E0; - lq = lq*x+1.5062909083469192043167E1; - lq = lq*x+8.3047565967967209469434E1; - lq = lq*x+2.2176239823732856465394E2; - lq = lq*x+3.0909872225312059774938E2; - lq = lq*x+2.1642788614495947685003E2; - lq = lq*x+6.0118660497603843919306E1; - z = -(0.5*z)+x*(z*lp/lq); - result = x+z; - return result; - } + /************************************************************************* + Application of an elementary reflection to a rectangular matrix of size MxN + The algorithm pre-multiplies the matrix by an elementary reflection + transformation which is given by column V and scalar Tau (see the + description of the GenerateReflection). Not the whole matrix but only a + part of it is transformed (rows from M1 to M2, columns from N1 to N2). Only + the elements of this submatrix are changed. - public static double nuexpm1(double x, - alglib.xparams _params) - { - double result = 0; - double r = 0; - double xx = 0; - double ep = 0; - double eq = 0; + Note: the matrix is multiplied by H, not by H'. If it is required to + multiply the matrix by H', it is necessary to pass Conj(Tau) instead of Tau. - if( (double)(x)<(double)(-0.5) || (double)(x)>(double)(0.5) ) - { - result = Math.Exp(x)-1.0; - return result; - } - xx = x*x; - ep = 1.2617719307481059087798E-4; - ep = ep*xx+3.0299440770744196129956E-2; - ep = ep*xx+9.9999999999999999991025E-1; - eq = 3.0019850513866445504159E-6; - eq = eq*xx+2.5244834034968410419224E-3; - eq = eq*xx+2.2726554820815502876593E-1; - eq = eq*xx+2.0000000000000000000897E0; - r = x*ep; - r = r/(eq-r); - result = r+r; - return result; - } + Input parameters: + C - matrix to be transformed. + Tau - scalar defining transformation. + V - column defining transformation. + Array whose index ranges within [1..M2-M1+1] + M1, M2 - range of rows to be transformed. + N1, N2 - range of columns to be transformed. + WORK - working array whose index goes from N1 to N2. + Output parameters: + C - the result of multiplying the input matrix C by the + transformation matrix which is given by Tau and V. + If N1>N2 or M1>M2, C is not modified. - public static double nucosm1(double x, + -- LAPACK auxiliary routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + September 30, 1994 + *************************************************************************/ + public static void complexapplyreflectionfromtheleft(complex[,] c, + complex tau, + complex[] v, + int m1, + int m2, + int n1, + int n2, + ref complex[] work, alglib.xparams _params) { - double result = 0; - double xx = 0; - double c = 0; + complex t = 0; + int i = 0; + int i_ = 0; - if( (double)(x)<(double)(-(0.25*Math.PI)) || (double)(x)>(double)(0.25*Math.PI) ) + if( (tau==0 || n1>n2) || m1>m2 ) { - result = Math.Cos(x)-1; - return result; + return; } - xx = x*x; - c = 4.7377507964246204691685E-14; - c = c*xx-1.1470284843425359765671E-11; - c = c*xx+2.0876754287081521758361E-9; - c = c*xx-2.7557319214999787979814E-7; - c = c*xx+2.4801587301570552304991E-5; - c = c*xx-1.3888888888888872993737E-3; - c = c*xx+4.1666666666666666609054E-2; - result = -(0.5*xx)+xx*xx*c; - return result; - } - - - } - public class ntheory - { - public static void findprimitiverootandinverse(int n, - ref int proot, - ref int invproot, - alglib.xparams _params) - { - int candroot = 0; - int phin = 0; - int q = 0; - int f = 0; - bool allnonone = new bool(); - int x = 0; - int lastx = 0; - int y = 0; - int lasty = 0; - int a = 0; - int b = 0; - int t = 0; - int n2 = 0; - - proot = 0; - invproot = 0; - - alglib.ap.assert(n>=3, "FindPrimitiveRootAndInverse: N<3"); - proot = 0; - invproot = 0; - - // - // check that N is prime - // - alglib.ap.assert(isprime(n, _params), "FindPrimitiveRoot: N is not prime"); - - // - // Because N is prime, Euler totient function is equal to N-1 - // - phin = n-1; // - // Test different values of PRoot - from 2 to N-1. - // One of these values MUST be primitive root. - // - // For testing we use algorithm from Wiki (Primitive root modulo n): - // * compute phi(N) - // * determine the different prime factors of phi(N), say p1, ..., pk - // * for every element m of Zn*, compute m^(phi(N)/pi) mod N for i=1..k - // using a fast algorithm for modular exponentiation. - // * a number m for which these k results are all different from 1 is a - // primitive root. + // w := C^T * conj(v) // - for(candroot=2; candroot<=n-1; candroot++) + for(i=n1; i<=n2; i++) { - - // - // We have current candidate root in CandRoot. - // - // Scan different prime factors of PhiN. Here: - // * F is a current candidate factor - // * Q is a current quotient - amount which was left after dividing PhiN - // by all previous factors - // - // For each factor, perform test mentioned above. - // - q = phin; - f = 2; - allnonone = true; - while( q>1 ) - { - if( q%f==0 ) - { - t = modexp(candroot, phin/f, n, _params); - if( t==1 ) - { - allnonone = false; - break; - } - while( q%f==0 ) - { - q = q/f; - } - } - f = f+1; - } - if( allnonone ) + work[i] = 0; + } + for(i=m1; i<=m2; i++) + { + t = math.conj(v[i+1-m1]); + for(i_=n1; i_<=n2;i_++) { - proot = candroot; - break; + work[i_] = work[i_] + t*c[i,i_]; } } - alglib.ap.assert(proot>=2, "FindPrimitiveRoot: internal error (root not found)"); // - // Use extended Euclidean algorithm to find multiplicative inverse of primitive root + // C := C - tau * v * w^T // - x = 0; - lastx = 1; - y = 1; - lasty = 0; - a = proot; - b = n; - while( b!=0 ) - { - q = a/b; - t = a%b; - a = b; - b = t; - t = lastx-q*x; - lastx = x; - x = t; - t = lasty-q*y; - lasty = y; - y = t; - } - while( lastx<0 ) + for(i=m1; i<=m2; i++) { - lastx = lastx+n; + t = v[i-m1+1]*tau; + for(i_=n1; i_<=n2;i_++) + { + c[i,i_] = c[i,i_] - t*work[i_]; + } } - invproot = lastx; - - // - // Check that it is safe to perform multiplication modulo N. - // Check results for consistency. - // - n2 = (n-1)*(n-1); - alglib.ap.assert(n2/(n-1)==n-1, "FindPrimitiveRoot: internal error"); - alglib.ap.assert(proot*invproot/proot==invproot, "FindPrimitiveRoot: internal error"); - alglib.ap.assert(proot*invproot/invproot==proot, "FindPrimitiveRoot: internal error"); - alglib.ap.assert(proot*invproot%n==1, "FindPrimitiveRoot: internal error"); } - private static bool isprime(int n, + /************************************************************************* + Application of an elementary reflection to a rectangular matrix of size MxN + + The algorithm post-multiplies the matrix by an elementary reflection + transformation which is given by column V and scalar Tau (see the + description of the GenerateReflection). Not the whole matrix but only a + part of it is transformed (rows from M1 to M2, columns from N1 to N2). + Only the elements of this submatrix are changed. + + Input parameters: + C - matrix to be transformed. + Tau - scalar defining transformation. + V - column defining transformation. + Array whose index ranges within [1..N2-N1+1] + M1, M2 - range of rows to be transformed. + N1, N2 - range of columns to be transformed. + WORK - working array whose index goes from M1 to M2. + + Output parameters: + C - the result of multiplying the input matrix C by the + transformation matrix which is given by Tau and V. + If N1>N2 or M1>M2, C is not modified. + + -- LAPACK auxiliary routine (version 3.0) -- + Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., + Courant Institute, Argonne National Lab, and Rice University + September 30, 1994 + *************************************************************************/ + public static void complexapplyreflectionfromtheright(complex[,] c, + complex tau, + complex[] v, + int m1, + int m2, + int n1, + int n2, + ref complex[] work, alglib.xparams _params) { - bool result = new bool(); - int p = 0; + complex t = 0; + int i = 0; + int vm = 0; + int i_ = 0; + int i1_ = 0; - result = false; - p = 2; - while( p*p<=n ) + if( (tau==0 || n1>n2) || m1>m2 ) { - if( n%p==0 ) + return; + } + + // + // w := C * v + // + vm = n2-n1+1; + for(i=m1; i<=m2; i++) + { + i1_ = (1)-(n1); + t = 0.0; + for(i_=n1; i_<=n2;i_++) + { + t += c[i,i_]*v[i_+i1_]; + } + work[i] = t; + } + + // + // C := C - w * conj(v^T) + // + for(i_=1; i_<=vm;i_++) + { + v[i_] = math.conj(v[i_]); + } + for(i=m1; i<=m2; i++) + { + t = work[i]*tau; + i1_ = (1) - (n1); + for(i_=n1; i_<=n2;i_++) { - return result; + c[i,i_] = c[i,i_] - t*v[i_+i1_]; } - p = p+1; } - result = true; - return result; + for(i_=1; i_<=vm;i_++) + { + v[i_] = math.conj(v[i_]); + } } - private static int modmul(int a, - int b, - int n, + } + public partial class hblas + { + public static void hermitianmatrixvectormultiply(complex[,] a, + bool isupper, + int i1, + int i2, + complex[] x, + complex alpha, + ref complex[] y, alglib.xparams _params) { - int result = 0; - int t = 0; - double ra = 0; - double rb = 0; + int i = 0; + int ba1 = 0; + int by1 = 0; + int by2 = 0; + int bx1 = 0; + int bx2 = 0; + int n = 0; + complex v = 0; + int i_ = 0; + int i1_ = 0; - alglib.ap.assert(a>=0 && a=N"); - alglib.ap.assert(b>=0 && b=N"); + n = i2-i1+1; + if( n<=0 ) + { + return; + } // - // Base cases + // Let A = L + D + U, where + // L is strictly lower triangular (main diagonal is zero) + // D is diagonal + // U is strictly upper triangular (main diagonal is zero) // - ra = a; - rb = b; - if( b==0 || a==0 ) - { - result = 0; - return result; - } - if( b==1 || a==1 ) - { - result = a*b; - return result; - } - if( (double)(ra*rb)==(double)(a*b) ) + // A*x = L*x + D*x + U*x + // + // Calculate D*x first + // + for(i=i1; i<=i2; i++) { - result = a*b%n; - return result; + y[i-i1+1] = a[i,i]*x[i-i1+1]; } // - // Non-base cases + // Add L*x + U*x // - if( b%2==0 ) + if( isupper ) { - - // - // A*B = (A*(B/2)) * 2 - // - // Product T=A*(B/2) is calculated recursively, product T*2 is - // calculated as follows: - // * result:=T-N - // * result:=result+T - // * if result<0 then result:=result+N - // - // In case integer result overflows, we generate exception - // - t = modmul(a, b/2, n, _params); - result = t-n; - result = result+t; - if( result<0 ) + for(i=i1; i<=i2-1; i++) { - result = result+n; + + // + // Add L*x to the result + // + v = x[i-i1+1]; + by1 = i-i1+2; + by2 = n; + ba1 = i+1; + i1_ = (ba1) - (by1); + for(i_=by1; i_<=by2;i_++) + { + y[i_] = y[i_] + v*math.conj(a[i,i_+i1_]); + } + + // + // Add U*x to the result + // + bx1 = i-i1+2; + bx2 = n; + ba1 = i+1; + i1_ = (ba1)-(bx1); + v = 0.0; + for(i_=bx1; i_<=bx2;i_++) + { + v += x[i_]*a[i,i_+i1_]; + } + y[i-i1+1] = y[i-i1+1]+v; } } else { - - // - // A*B = (A*(B div 2)) * 2 + A - // - // Product T=A*(B/2) is calculated recursively, product T*2 is - // calculated as follows: - // * result:=T-N - // * result:=result+T - // * if result<0 then result:=result+N - // - // In case integer result overflows, we generate exception - // - t = modmul(a, b/2, n, _params); - result = t-n; - result = result+t; - if( result<0 ) + for(i=i1+1; i<=i2; i++) { - result = result+n; + + // + // Add L*x to the result + // + bx1 = 1; + bx2 = i-i1; + ba1 = i1; + i1_ = (ba1)-(bx1); + v = 0.0; + for(i_=bx1; i_<=bx2;i_++) + { + v += x[i_]*a[i,i_+i1_]; + } + y[i-i1+1] = y[i-i1+1]+v; + + // + // Add U*x to the result + // + v = x[i-i1+1]; + by1 = 1; + by2 = i-i1; + ba1 = i1; + i1_ = (ba1) - (by1); + for(i_=by1; i_<=by2;i_++) + { + y[i_] = y[i_] + v*math.conj(a[i,i_+i1_]); + } } - result = result-n; - result = result+a; - if( result<0 ) + } + for(i_=1; i_<=n;i_++) + { + y[i_] = alpha*y[i_]; + } + } + + + public static void hermitianrank2update(complex[,] a, + bool isupper, + int i1, + int i2, + complex[] x, + complex[] y, + ref complex[] t, + complex alpha, + alglib.xparams _params) + { + int i = 0; + int tp1 = 0; + int tp2 = 0; + complex v = 0; + int i_ = 0; + int i1_ = 0; + + if( isupper ) + { + for(i=i1; i<=i2; i++) { - result = result+n; + tp1 = i+1-i1; + tp2 = i2-i1+1; + v = alpha*x[i+1-i1]; + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = v*math.conj(y[i_]); + } + v = math.conj(alpha)*y[i+1-i1]; + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = t[i_] + v*math.conj(x[i_]); + } + i1_ = (tp1) - (i); + for(i_=i; i_<=i2;i_++) + { + a[i,i_] = a[i,i_] + t[i_+i1_]; + } + } + } + else + { + for(i=i1; i<=i2; i++) + { + tp1 = 1; + tp2 = i+1-i1; + v = alpha*x[i+1-i1]; + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = v*math.conj(y[i_]); + } + v = math.conj(alpha)*y[i+1-i1]; + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = t[i_] + v*math.conj(x[i_]); + } + i1_ = (tp1) - (i1); + for(i_=i1; i_<=i;i_++) + { + a[i,i_] = a[i,i_] + t[i_+i1_]; + } } } - return result; } - private static int modexp(int a, - int b, - int n, + } + public partial class sblas + { + public static void symmetricmatrixvectormultiply(double[,] a, + bool isupper, + int i1, + int i2, + double[] x, + double alpha, + ref double[] y, alglib.xparams _params) { - int result = 0; - int t = 0; + int i = 0; + int ba1 = 0; + int ba2 = 0; + int by1 = 0; + int by2 = 0; + int bx1 = 0; + int bx2 = 0; + int n = 0; + double v = 0; + int i_ = 0; + int i1_ = 0; - alglib.ap.assert(a>=0 && a=N"); - alglib.ap.assert(b>=0, "ModExp: B<0"); + n = i2-i1+1; + if( n<=0 ) + { + return; + } // - // Base cases + // Let A = L + D + U, where + // L is strictly lower triangular (main diagonal is zero) + // D is diagonal + // U is strictly upper triangular (main diagonal is zero) // - if( b==0 ) - { - result = 1; - return result; - } - if( b==1 ) + // A*x = L*x + D*x + U*x + // + // Calculate D*x first + // + for(i=i1; i<=i2; i++) { - result = a; - return result; + y[i-i1+1] = a[i,i]*x[i-i1+1]; } // - // Non-base cases + // Add L*x + U*x // - if( b%2==0 ) + if( isupper ) { - t = modmul(a, a, n, _params); - result = modexp(t, b/2, n, _params); + for(i=i1; i<=i2-1; i++) + { + + // + // Add L*x to the result + // + v = x[i-i1+1]; + by1 = i-i1+2; + by2 = n; + ba1 = i+1; + ba2 = i2; + i1_ = (ba1) - (by1); + for(i_=by1; i_<=by2;i_++) + { + y[i_] = y[i_] + v*a[i,i_+i1_]; + } + + // + // Add U*x to the result + // + bx1 = i-i1+2; + bx2 = n; + ba1 = i+1; + ba2 = i2; + i1_ = (ba1)-(bx1); + v = 0.0; + for(i_=bx1; i_<=bx2;i_++) + { + v += x[i_]*a[i,i_+i1_]; + } + y[i-i1+1] = y[i-i1+1]+v; + } } else { - t = modmul(a, a, n, _params); - result = modexp(t, b/2, n, _params); - result = modmul(result, a, n, _params); - } - return result; - } - - - } - public class ftbase - { - /************************************************************************* - This record stores execution plan for the fast transformation along with - preallocated temporary buffers and precalculated values. - - FIELDS: - Entries - plan entries, one row = one entry (see below for - description). - Buf0,Buf1,Buf2 - global temporary buffers; some of them are allocated, - some of them are not (as decided by plan generation - subroutine). - Buffer - global buffer whose size is equal to plan size. - There is one-to-one correspondence between elements - of global buffer and elements of array transformed. - Because of it global buffer can be used as temporary - thread-safe storage WITHOUT ACQUIRING LOCK - each - worker thread works with its part of input array, - and each part of input array corresponds to distinct - part of buffer. - - FORMAT OF THE ENTRIES TABLE: - - Entries table is 2D array which stores one entry per row. Row format is: - row[0] operation type: - * 0 for "end of plan/subplan" - * +1 for "reference O(N^2) complex FFT" - * -1 for complex transposition - * -2 for multiplication by twiddle factors of complex FFT - * -3 for "start of plan/subplan" - row[1] repetition count, >=1 - row[2] base operand size (number of microvectors), >=1 - row[3] microvector size (measured in real numbers), >=1 - row[4] parameter0, meaning depends on row[0] - row[5] parameter1, meaning depends on row[0] - - FORMAT OF THE DATA: - - Transformation plan works with row[1]*row[2]*row[3] real numbers, which - are (in most cases) interpreted as sequence of complex numbers. These data - are grouped as follows: - * we have row[1] contiguous OPERANDS, which can be treated separately - * each operand includes row[2] contiguous MICROVECTORS - * each microvector includes row[3] COMPONENTS, which can be treated separately - * pair of components form complex number, so in most cases row[3] will be even - - Say, if you want to perform complex FFT of length 3, then: - * you have 1 operand: row[1]=1 - * operand consists of 3 microvectors: row[2]=3 - * each microvector has two components: row[3]=2 - * a pair of subsequent components is treated as complex number - - if you want to perform TWO simultaneous complex FFT's of length 3, then you - can choose between two representations: - * 1 operand, 3 microvectors, 4 components; storage format is given below: - [ A0X A0Y B0X B0Y A1X A1Y B1X B1Y ... ] - (here A denotes first sequence, B - second one). - * 2 operands, 3 microvectors, 2 components; storage format is given below: - [ A0X A0Y A1X A2Y ... B0X B0Y B1X B1Y ... ] - Most FFT operations are supported only for the second format, but you - should remember that first format sometimes can be used too. - - SUPPORTED OPERATIONS: - - row[0]=0: - * "end of plan/subplan" - * in case we meet entry with such type, FFT transformation is finished - (or we return from recursive FFT subplan, in case it was subplan). - - row[0]=+1: - * "reference 1D complex FFT" - * we perform reference O(N^2) complex FFT on input data, which are treated - as row[1] arrays, each of row[2] complex numbers, and row[3] must be - equal to 2 - * transformation is performed using temporary buffer - - row[0]=opBluesteinsFFT: - * input array is handled with Bluestein's algorithm (by zero-padding to - Param0 complex numbers). - * this plan calls Param0-point subplan which is located at offset Param1 - (offset is measured with respect to location of the calling entry) - * this plan uses precomputed quantities stored in Plan.PrecR at - offset Param2. - * transformation is performed using 4 temporary buffers, which are - retrieved from Plan.BluesteinPool. - - row[0]=+3: - * "optimized 1D complex FFT" - * this function supports only several operand sizes: from 1 to 5. - These transforms are hard-coded and performed very efficiently - - row[0]=opRadersFFT: - * input array is handled with Rader's algorithm (permutation and - reduction to N-1-point FFT) - * this plan calls N-1-point subplan which is located at offset Param0 - (offset is measured with respect to location of the calling entry) - * this plan uses precomputed primitive root and its inverse (modulo N) - which are stored in Param1 and Param2. - * Param3 stores offset of the precomputed data for the plan - * plan length must be prime, (N-1)*(N-1) must fit into integer variable - - row[0]=-1 - * "complex transposition" - * input data are treated as row[1] independent arrays, which are processed - separately - * each of operands is treated as matrix with row[4] rows and row[2]/row[4] - columns. Each element of the matrix is microvector with row[3] components. - * transposition is performed using temporary buffer - - row[0]=-2 - * "multiplication by twiddle factors of complex FFT" - * input data are treated as row[1] independent arrays, which are processed - separately - * row[4] contains N1 - length of the "first FFT" in a Cooley-Tukey FFT - algorithm - * this function does not require temporary buffers - - row[0]=-3 - * "start of the plan" - * each subplan must start from this entry - * param0 is ignored - * param1 stores approximate (optimistic) estimate of KFLOPs required to - transform one operand of the plan. Total cost of the plan is approximately - equal to row[1]*param1 KFLOPs. - * this function does not require temporary buffers - - row[0]=-4 - * "jump" - * param0 stores relative offset of the jump site - (+1 corresponds to the next entry) - - row[0]=-5 - * "parallel call" - * input data are treated as row[1] independent arrays - * child subplan is applied independently for each of arrays - row[1] times - * subplan length must be equal to row[2]*row[3] - * param0 stores relative offset of the child subplan site - (+1 corresponds to the next entry) - * param1 stores approximate total cost of plan, measured in UNITS - (1 UNIT = 100 KFLOPs). Plan cost must be rounded DOWN to nearest integer. - - - - TODO - 2. from KFLOPs to UNITs, 1 UNIT = 100 000 FLOP!!!!!!!!!!! - - 3. from IsRoot to TaskType = {0, -1, +1}; or maybe, add IsSeparatePlan - to distinguish root of child subplan from global root which uses - separate buffer - - 4. child subplans in parallel call must NOT use buffer provided by parent plan; - they must allocate their own local buffer - *************************************************************************/ - public class fasttransformplan : apobject - { - public int[,] entries; - public double[] buffer; - public double[] precr; - public double[] preci; - public alglib.smp.shared_pool bluesteinpool; - public fasttransformplan() - { - init(); - } - public override void init() - { - entries = new int[0,0]; - buffer = new double[0]; - precr = new double[0]; - preci = new double[0]; - bluesteinpool = new alglib.smp.shared_pool(); + for(i=i1+1; i<=i2; i++) + { + + // + // Add L*x to the result + // + bx1 = 1; + bx2 = i-i1; + ba1 = i1; + ba2 = i-1; + i1_ = (ba1)-(bx1); + v = 0.0; + for(i_=bx1; i_<=bx2;i_++) + { + v += x[i_]*a[i,i_+i1_]; + } + y[i-i1+1] = y[i-i1+1]+v; + + // + // Add U*x to the result + // + v = x[i-i1+1]; + by1 = 1; + by2 = i-i1; + ba1 = i1; + ba2 = i-1; + i1_ = (ba1) - (by1); + for(i_=by1; i_<=by2;i_++) + { + y[i_] = y[i_] + v*a[i,i_+i1_]; + } + } } - public override alglib.apobject make_copy() + for(i_=1; i_<=n;i_++) { - fasttransformplan _result = new fasttransformplan(); - _result.entries = (int[,])entries.Clone(); - _result.buffer = (double[])buffer.Clone(); - _result.precr = (double[])precr.Clone(); - _result.preci = (double[])preci.Clone(); - _result.bluesteinpool = bluesteinpool!=null ? (alglib.smp.shared_pool)bluesteinpool.make_copy() : null; - return _result; + y[i_] = alpha*y[i_]; } - }; - - - - - public const int coltype = 0; - public const int coloperandscnt = 1; - public const int coloperandsize = 2; - public const int colmicrovectorsize = 3; - public const int colparam0 = 4; - public const int colparam1 = 5; - public const int colparam2 = 6; - public const int colparam3 = 7; - public const int colscnt = 8; - public const int opend = 0; - public const int opcomplexreffft = 1; - public const int opbluesteinsfft = 2; - public const int opcomplexcodeletfft = 3; - public const int opcomplexcodelettwfft = 4; - public const int opradersfft = 5; - public const int opcomplextranspose = -1; - public const int opcomplexfftfactors = -2; - public const int opstart = -3; - public const int opjmp = -4; - public const int opparallelcall = -5; - public const int maxradix = 6; - public const int updatetw = 16; - public const int recursivethreshold = 1024; - public const int raderthreshold = 19; - public const int ftbasecodeletrecommended = 5; - public const double ftbaseinefficiencyfactor = 1.3; - public const int ftbasemaxsmoothfactor = 5; - - - /************************************************************************* - This subroutine generates FFT plan for K complex FFT's with length N each. + apserv.touchint(ref ba2, _params); + } - INPUT PARAMETERS: - N - FFT length (in complex numbers), N>=1 - K - number of repetitions, K>=1 - - OUTPUT PARAMETERS: - Plan - plan - -- ALGLIB -- - Copyright 05.04.2013 by Bochkanov Sergey - *************************************************************************/ - public static void ftcomplexfftplan(int n, - int k, - fasttransformplan plan, + public static void symmetricrank2update(double[,] a, + bool isupper, + int i1, + int i2, + double[] x, + double[] y, + ref double[] t, + double alpha, alglib.xparams _params) { - apserv.srealarray bluesteinbuf = new apserv.srealarray(); - int rowptr = 0; - int bluesteinsize = 0; - int precrptr = 0; - int preciptr = 0; - int precrsize = 0; - int precisize = 0; + int i = 0; + int tp1 = 0; + int tp2 = 0; + double v = 0; + int i_ = 0; + int i1_ = 0; - - // - // Initial check for parameters - // - alglib.ap.assert(n>0, "FTComplexFFTPlan: N<=0"); - alglib.ap.assert(k>0, "FTComplexFFTPlan: K<=0"); - - // - // Determine required sizes of precomputed real and integer - // buffers. This stage of code is highly dependent on internals - // of FTComplexFFTPlanRec() and must be kept synchronized with - // possible changes in internals of plan generation function. - // - // Buffer size is determined as follows: - // * N is factorized - // * we factor out anything which is less or equal to MaxRadix - // * prime factor F>RaderThreshold requires 4*FTBaseFindSmooth(2*F-1) - // real entries to store precomputed Quantities for Bluestein's - // transformation - // * prime factor F<=RaderThreshold does NOT require - // precomputed storage - // - precrsize = 0; - precisize = 0; - ftdeterminespacerequirements(n, ref precrsize, ref precisize, _params); - if( precrsize>0 ) + if( isupper ) { - plan.precr = new double[precrsize]; + for(i=i1; i<=i2; i++) + { + tp1 = i+1-i1; + tp2 = i2-i1+1; + v = x[i+1-i1]; + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = v*y[i_]; + } + v = y[i+1-i1]; + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = t[i_] + v*x[i_]; + } + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = alpha*t[i_]; + } + i1_ = (tp1) - (i); + for(i_=i; i_<=i2;i_++) + { + a[i,i_] = a[i,i_] + t[i_+i1_]; + } + } } - if( precisize>0 ) + else { - plan.preci = new double[precisize]; + for(i=i1; i<=i2; i++) + { + tp1 = 1; + tp2 = i+1-i1; + v = x[i+1-i1]; + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = v*y[i_]; + } + v = y[i+1-i1]; + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = t[i_] + v*x[i_]; + } + for(i_=tp1; i_<=tp2;i_++) + { + t[i_] = alpha*t[i_]; + } + i1_ = (tp1) - (i1); + for(i_=i1; i_<=i;i_++) + { + a[i,i_] = a[i,i_] + t[i_+i1_]; + } + } } - - // - // Generate plan - // - rowptr = 0; - precrptr = 0; - preciptr = 0; - bluesteinsize = 1; - plan.buffer = new double[2*n*k]; - ftcomplexfftplanrec(n, k, true, true, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); - bluesteinbuf.val = new double[bluesteinsize]; - alglib.smp.ae_shared_pool_set_seed(plan.bluesteinpool, bluesteinbuf); - - // - // Check that actual amount of precomputed space used by transformation - // plan is EXACTLY equal to amount of space allocated by us. - // - alglib.ap.assert(precrptr==precrsize, "FTComplexFFTPlan: internal error (PrecRPtr<>PrecRSize)"); - alglib.ap.assert(preciptr==precisize, "FTComplexFFTPlan: internal error (PrecRPtr<>PrecRSize)"); } - /************************************************************************* - This subroutine applies transformation plan to input/output array A. + } + public partial class blas + { + public static double vectornorm2(double[] x, + int i1, + int i2, + alglib.xparams _params) + { + double result = 0; + int n = 0; + int ix = 0; + double absxi = 0; + double scl = 0; + double ssq = 0; - INPUT PARAMETERS: - Plan - transformation plan - A - array, must be large enough for plan to work - OffsA - offset of the subarray to process - RepCnt - repetition count (transformation is repeatedly applied - to subsequent subarrays) - - OUTPUT PARAMETERS: - Plan - plan (temporary buffers can be modified, plan itself - is unchanged and can be reused) - A - transformed array + n = i2-i1+1; + if( n<1 ) + { + result = 0; + return result; + } + if( n==1 ) + { + result = Math.Abs(x[i1]); + return result; + } + scl = 0; + ssq = 1; + for(ix=i1; ix<=i2; ix++) + { + if( (double)(x[ix])!=(double)(0) ) + { + absxi = Math.Abs(x[ix]); + if( (double)(scl)<(double)(absxi) ) + { + ssq = 1+ssq*math.sqr(scl/absxi); + scl = absxi; + } + else + { + ssq = ssq+math.sqr(absxi/scl); + } + } + } + result = scl*Math.Sqrt(ssq); + return result; + } - -- ALGLIB -- - Copyright 05.04.2013 by Bochkanov Sergey - *************************************************************************/ - public static void ftapplyplan(fasttransformplan plan, - double[] a, - int offsa, - int repcnt, + + public static int vectoridxabsmax(double[] x, + int i1, + int i2, alglib.xparams _params) { - int plansize = 0; + int result = 0; int i = 0; - plansize = plan.entries[0,coloperandscnt]*plan.entries[0,coloperandsize]*plan.entries[0,colmicrovectorsize]; - for(i=0; i<=repcnt-1; i++) + result = i1; + for(i=i1+1; i<=i2; i++) { - ftapplysubplan(plan, 0, a, offsa+plansize*i, 0, plan.buffer, 1, _params); + if( (double)(Math.Abs(x[i]))>(double)(Math.Abs(x[result])) ) + { + result = i; + } } + return result; } - /************************************************************************* - Returns good factorization N=N1*N2. - - Usually N1<=N2 (but not always - small N's may be exception). - if N1<>1 then N2<>1. - - Factorization is chosen depending on task type and codelets we have. - - -- ALGLIB -- - Copyright 01.05.2009 by Bochkanov Sergey - *************************************************************************/ - public static void ftbasefactorize(int n, - int tasktype, - ref int n1, - ref int n2, + public static int columnidxabsmax(double[,] x, + int i1, + int i2, + int j, alglib.xparams _params) { - int j = 0; - - n1 = 0; - n2 = 0; + int result = 0; + int i = 0; - n1 = 0; - n2 = 0; - - // - // try to find good codelet - // - if( n1*n2!=n ) - { - for(j=ftbasecodeletrecommended; j>=2; j--) - { - if( n%j==0 ) - { - n1 = j; - n2 = n/j; - break; - } - } - } - - // - // try to factorize N - // - if( n1*n2!=n ) + result = i1; + for(i=i1+1; i<=i2; i++) { - for(j=ftbasecodeletrecommended+1; j<=n-1; j++) + if( (double)(Math.Abs(x[i,j]))>(double)(Math.Abs(x[result,j])) ) { - if( n%j==0 ) - { - n1 = j; - n2 = n/j; - break; - } + result = i; } } - - // - // looks like N is prime :( - // - if( n1*n2!=n ) - { - n1 = 1; - n2 = n; - } - - // - // normalize - // - if( n2==1 && n1!=1 ) - { - n2 = n1; - n1 = 1; - } + return result; } - /************************************************************************* - Is number smooth? - - -- ALGLIB -- - Copyright 01.05.2009 by Bochkanov Sergey - *************************************************************************/ - public static bool ftbaseissmooth(int n, + public static int rowidxabsmax(double[,] x, + int j1, + int j2, + int i, alglib.xparams _params) { - bool result = new bool(); - int i = 0; + int result = 0; + int j = 0; - for(i=2; i<=ftbasemaxsmoothfactor; i++) + result = j1; + for(j=j1+1; j<=j2; j++) { - while( n%i==0 ) + if( (double)(Math.Abs(x[i,j]))>(double)(Math.Abs(x[i,result])) ) { - n = n/i; + result = j; } } - result = n==1; return result; } - /************************************************************************* - Returns smallest smooth (divisible only by 2, 3, 5) number that is greater - than or equal to max(N,2) - - -- ALGLIB -- - Copyright 01.05.2009 by Bochkanov Sergey - *************************************************************************/ - public static int ftbasefindsmooth(int n, + public static double upperhessenberg1norm(double[,] a, + int i1, + int i2, + int j1, + int j2, + ref double[] work, alglib.xparams _params) { - int result = 0; - int best = 0; + double result = 0; + int i = 0; + int j = 0; - best = 2; - while( bestJ2-J1!"); + for(j=j1; j<=j2; j++) { - best = 2*best; + work[j] = 0; + } + for(i=i1; i<=i2; i++) + { + for(j=Math.Max(j1, j1+i-i1-1); j<=j2; j++) + { + work[j] = work[j]+Math.Abs(a[i,j]); + } + } + result = 0; + for(j=j1; j<=j2; j++) + { + result = Math.Max(result, work[j]); } - ftbasefindsmoothrec(n, 1, 2, ref best, _params); - result = best; return result; } - /************************************************************************* - Returns smallest smooth (divisible only by 2, 3, 5) even number that is - greater than or equal to max(N,2) - - -- ALGLIB -- - Copyright 01.05.2009 by Bochkanov Sergey - *************************************************************************/ - public static int ftbasefindsmootheven(int n, + public static void copymatrix(double[,] a, + int is1, + int is2, + int js1, + int js2, + ref double[,] b, + int id1, + int id2, + int jd1, + int jd2, alglib.xparams _params) { - int result = 0; - int best = 0; + int isrc = 0; + int idst = 0; + int i_ = 0; + int i1_ = 0; - best = 2; - while( bestis2 || js1>js2 ) { - best = 2*best; + return; + } + alglib.ap.assert(is2-is1==id2-id1, "CopyMatrix: different sizes!"); + alglib.ap.assert(js2-js1==jd2-jd1, "CopyMatrix: different sizes!"); + for(isrc=is1; isrc<=is2; isrc++) + { + idst = isrc-is1+id1; + i1_ = (js1) - (jd1); + for(i_=jd1; i_<=jd2;i_++) + { + b[idst,i_] = a[isrc,i_+i1_]; + } } - ftbasefindsmoothrec(n, 2, 2, ref best, _params); - result = best; - return result; } - /************************************************************************* - Returns estimate of FLOP count for the FFT. + public static void inplacetranspose(ref double[,] a, + int i1, + int i2, + int j1, + int j2, + ref double[] work, + alglib.xparams _params) + { + int i = 0; + int j = 0; + int ips = 0; + int jps = 0; + int l = 0; + int i_ = 0; + int i1_ = 0; - It is only an estimate based on operations count for the PERFECT FFT - and relative inefficiency of the algorithm actually used. + if( i1>i2 || j1>j2 ) + { + return; + } + alglib.ap.assert(i1-i2==j1-j2, "InplaceTranspose error: incorrect array size!"); + for(i=i1; i<=i2-1; i++) + { + j = j1+i-i1; + ips = i+1; + jps = j1+ips-i1; + l = i2-i; + i1_ = (ips) - (1); + for(i_=1; i_<=l;i_++) + { + work[i_] = a[i_+i1_,j]; + } + i1_ = (jps) - (ips); + for(i_=ips; i_<=i2;i_++) + { + a[i_,j] = a[i,i_+i1_]; + } + i1_ = (1) - (jps); + for(i_=jps; i_<=j2;i_++) + { + a[i,i_] = work[i_+i1_]; + } + } + } - N should be power of 2, estimates are badly wrong for non-power-of-2 N's. - -- ALGLIB -- - Copyright 01.05.2009 by Bochkanov Sergey - *************************************************************************/ - public static double ftbasegetflopestimate(int n, + public static void copyandtranspose(double[,] a, + int is1, + int is2, + int js1, + int js2, + ref double[,] b, + int id1, + int id2, + int jd1, + int jd2, alglib.xparams _params) { - double result = 0; + int isrc = 0; + int jdst = 0; + int i_ = 0; + int i1_ = 0; - result = ftbaseinefficiencyfactor*(4*n*Math.Log(n)/Math.Log(2)-6*n+8); - return result; + if( is1>is2 || js1>js2 ) + { + return; + } + alglib.ap.assert(is2-is1==jd2-jd1, "CopyAndTranspose: different sizes!"); + alglib.ap.assert(js2-js1==id2-id1, "CopyAndTranspose: different sizes!"); + for(isrc=is1; isrc<=is2; isrc++) + { + jdst = isrc-is1+jd1; + i1_ = (js1) - (id1); + for(i_=id1; i_<=id2;i_++) + { + b[i_,jdst] = a[isrc,i_+i1_]; + } + } } - /************************************************************************* - This function returns EXACT estimate of the space requirements for N-point - FFT. Internals of this function are highly dependent on details of different - FFTs employed by this unit, so every time algorithm is changed this function - has to be rewritten. - - INPUT PARAMETERS: - N - transform length - PrecRSize - must be set to zero - PrecISize - must be set to zero - - OUTPUT PARAMETERS: - PrecRSize - number of real temporaries required for transformation - PrecISize - number of integer temporaries required for transformation - - - -- ALGLIB -- - Copyright 05.04.2013 by Bochkanov Sergey - *************************************************************************/ - private static void ftdeterminespacerequirements(int n, - ref int precrsize, - ref int precisize, + public static void matrixvectormultiply(double[,] a, + int i1, + int i2, + int j1, + int j2, + bool trans, + double[] x, + int ix1, + int ix2, + double alpha, + ref double[] y, + int iy1, + int iy2, + double beta, alglib.xparams _params) { - int ncur = 0; - int f = 0; int i = 0; + double v = 0; + int i_ = 0; + int i1_ = 0; - - // - // Determine required sizes of precomputed real and integer - // buffers. This stage of code is highly dependent on internals - // of FTComplexFFTPlanRec() and must be kept synchronized with - // possible changes in internals of plan generation function. - // - // Buffer size is determined as follows: - // * N is factorized - // * we factor out anything which is less or equal to MaxRadix - // * prime factor F>RaderThreshold requires 4*FTBaseFindSmooth(2*F-1) - // real entries to store precomputed Quantities for Bluestein's - // transformation - // * prime factor F<=RaderThreshold requires 2*(F-1)+ESTIMATE(F-1) - // precomputed storage - // - ncur = n; - for(i=2; i<=maxradix; i++) + if( !trans ) { - while( ncur%i==0 ) + + // + // y := alpha*A*x + beta*y; + // + if( i1>i2 || j1>j2 ) { - ncur = ncur/i; + return; + } + alglib.ap.assert(j2-j1==ix2-ix1, "MatrixVectorMultiply: A and X dont match!"); + alglib.ap.assert(i2-i1==iy2-iy1, "MatrixVectorMultiply: A and Y dont match!"); + + // + // beta*y + // + if( (double)(beta)==(double)(0) ) + { + for(i=iy1; i<=iy2; i++) + { + y[i] = 0; + } + } + else + { + for(i_=iy1; i_<=iy2;i_++) + { + y[i_] = beta*y[i_]; + } + } + + // + // alpha*A*x + // + for(i=i1; i<=i2; i++) + { + i1_ = (ix1)-(j1); + v = 0.0; + for(i_=j1; i_<=j2;i_++) + { + v += a[i,i_]*x[i_+i1_]; + } + y[iy1+i-i1] = y[iy1+i-i1]+alpha*v; } } - f = 2; - while( f<=ncur ) + else { - while( ncur%f==0 ) + + // + // y := alpha*A'*x + beta*y; + // + if( i1>i2 || j1>j2 ) { - if( f>raderthreshold ) + return; + } + alglib.ap.assert(i2-i1==ix2-ix1, "MatrixVectorMultiply: A and X dont match!"); + alglib.ap.assert(j2-j1==iy2-iy1, "MatrixVectorMultiply: A and Y dont match!"); + + // + // beta*y + // + if( (double)(beta)==(double)(0) ) + { + for(i=iy1; i<=iy2; i++) { - precrsize = precrsize+4*ftbasefindsmooth(2*f-1, _params); + y[i] = 0; } - else + } + else + { + for(i_=iy1; i_<=iy2;i_++) { - precrsize = precrsize+2*(f-1); - ftdeterminespacerequirements(f-1, ref precrsize, ref precisize, _params); + y[i_] = beta*y[i_]; + } + } + + // + // alpha*A'*x + // + for(i=i1; i<=i2; i++) + { + v = alpha*x[ix1+i-i1]; + i1_ = (j1) - (iy1); + for(i_=iy1; i_<=iy2;i_++) + { + y[i_] = y[i_] + v*a[i,i_+i1_]; } - ncur = ncur/f; } - f = f+1; } } - /************************************************************************* - Recurrent function called by FTComplexFFTPlan() and other functions. It - recursively builds transformation plan + public static double pythag2(double x, + double y, + alglib.xparams _params) + { + double result = 0; + double w = 0; + double xabs = 0; + double yabs = 0; + double z = 0; - INPUT PARAMETERS: - N - FFT length (in complex numbers), N>=1 - K - number of repetitions, K>=1 - ChildPlan - if True, plan generator inserts OpStart/opEnd in the - plan header/footer. - TopmostPlan - if True, plan generator assumes that it is topmost plan: - * it may use global buffer for transpositions - and there is no other plan which executes in parallel - RowPtr - index which points to past-the-last entry generated so far - BluesteinSize- amount of storage (in real numbers) required for Bluestein buffer - PrecRPtr - pointer to unused part of precomputed real buffer (Plan.PrecR): - * when this function stores some data to precomputed buffer, - it advances pointer. - * it is responsibility of the function to assert that - Plan.PrecR has enough space to store data before actually - writing to buffer. - * it is responsibility of the caller to allocate enough - space before calling this function - PrecIPtr - pointer to unused part of precomputed integer buffer (Plan.PrecI): - * when this function stores some data to precomputed buffer, - it advances pointer. - * it is responsibility of the function to assert that - Plan.PrecR has enough space to store data before actually - writing to buffer. - * it is responsibility of the caller to allocate enough - space before calling this function - Plan - plan (generated so far) - - OUTPUT PARAMETERS: - RowPtr - updated pointer (advanced by number of entries generated - by function) - BluesteinSize- updated amount - (may be increased, but may never be decreased) - - NOTE: in case TopmostPlan is True, ChildPlan is also must be True. - - -- ALGLIB -- - Copyright 05.04.2013 by Bochkanov Sergey - *************************************************************************/ - private static void ftcomplexfftplanrec(int n, - int k, - bool childplan, - bool topmostplan, - ref int rowptr, - ref int bluesteinsize, - ref int precrptr, - ref int preciptr, - fasttransformplan plan, + xabs = Math.Abs(x); + yabs = Math.Abs(y); + w = Math.Max(xabs, yabs); + z = Math.Min(xabs, yabs); + if( (double)(z)==(double)(0) ) + { + result = w; + } + else + { + result = w*Math.Sqrt(1+math.sqr(z/w)); + } + return result; + } + + + public static void matrixmatrixmultiply(double[,] a, + int ai1, + int ai2, + int aj1, + int aj2, + bool transa, + double[,] b, + int bi1, + int bi2, + int bj1, + int bj2, + bool transb, + double alpha, + ref double[,] c, + int ci1, + int ci2, + int cj1, + int cj2, + double beta, + ref double[] work, alglib.xparams _params) { - apserv.srealarray localbuf = new apserv.srealarray(); - int m = 0; - int n1 = 0; - int n2 = 0; - int gq = 0; - int giq = 0; - int row0 = 0; - int row1 = 0; - int row2 = 0; - int row3 = 0; + int arows = 0; + int acols = 0; + int brows = 0; + int bcols = 0; + int crows = 0; + int i = 0; + int j = 0; + int k = 0; + int l = 0; + int r = 0; + double v = 0; + int i_ = 0; + int i1_ = 0; - alglib.ap.assert(n>0, "FTComplexFFTPlan: N<=0"); - alglib.ap.assert(k>0, "FTComplexFFTPlan: K<=0"); - alglib.ap.assert(!topmostplan || childplan, "FTComplexFFTPlan: ChildPlan is inconsistent with TopmostPlan"); // - // Try to generate "topmost" plan + // Setup // - if( topmostplan && n>recursivethreshold ) + if( !transa ) + { + arows = ai2-ai1+1; + acols = aj2-aj1+1; + } + else + { + arows = aj2-aj1+1; + acols = ai2-ai1+1; + } + if( !transb ) + { + brows = bi2-bi1+1; + bcols = bj2-bj1+1; + } + else + { + brows = bj2-bj1+1; + bcols = bi2-bi1+1; + } + alglib.ap.assert(acols==brows, "MatrixMatrixMultiply: incorrect matrix sizes!"); + if( ((arows<=0 || acols<=0) || brows<=0) || bcols<=0 ) { - ftfactorize(n, false, ref n1, ref n2, _params); - if( n1*n2==0 ) - { - - // - // Handle prime-factor FFT with Bluestein's FFT. - // Determine size of Bluestein's buffer. - // - m = ftbasefindsmooth(2*n-1, _params); - bluesteinsize = Math.Max(2*m, bluesteinsize); - - // - // Generate plan - // - ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); - ftpushentry4(plan, ref rowptr, opbluesteinsfft, k, n, 2, m, 2, precrptr, 0, _params); - row0 = rowptr; - ftpushentry(plan, ref rowptr, opjmp, 0, 0, 0, 0, _params); - ftcomplexfftplanrec(m, 1, true, true, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); - row1 = rowptr; - plan.entries[row0,colparam0] = row1-row0; - ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); - - // - // Fill precomputed buffer - // - ftprecomputebluesteinsfft(n, m, plan.precr, precrptr, _params); - - // - // Update pointer to the precomputed area - // - precrptr = precrptr+4*m; - } - else - { - - // - // Handle composite FFT with recursive Cooley-Tukey which - // uses global buffer instead of local one. - // - ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); - ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1, _params); - row0 = rowptr; - ftpushentry2(plan, ref rowptr, opparallelcall, k*n2, n1, 2, 0, ftoptimisticestimate(n, _params), _params); - ftpushentry(plan, ref rowptr, opcomplexfftfactors, k, n, 2, n1, _params); - ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n2, _params); - row2 = rowptr; - ftpushentry2(plan, ref rowptr, opparallelcall, k*n1, n2, 2, 0, ftoptimisticestimate(n, _params), _params); - ftpushentry(plan, ref rowptr, opcomplextranspose, k, n, 2, n1, _params); - ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); - row1 = rowptr; - ftcomplexfftplanrec(n1, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); - plan.entries[row0,colparam0] = row1-row0; - row3 = rowptr; - ftcomplexfftplanrec(n2, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); - plan.entries[row2,colparam0] = row3-row2; - } return; } + crows = arows; // - // Prepare "non-topmost" plan: - // * calculate factorization - // * use local (shared) buffer - // * update buffer size - ANY plan will need at least - // 2*N temporaries, additional requirements can be - // applied later + // Test WORK // - ftfactorize(n, false, ref n1, ref n2, _params); + i = Math.Max(arows, acols); + i = Math.Max(brows, i); + i = Math.Max(i, bcols); + work[1] = 0; + work[i] = 0; // - // Handle FFT's with N1*N2=0: either small-N or prime-factor + // Prepare C // - if( n1*n2==0 ) + if( (double)(beta)==(double)(0) ) { - if( n<=maxradix ) + for(i=ci1; i<=ci2; i++) { - - // - // Small-N FFT - // - if( childplan ) + for(j=cj1; j<=cj2; j++) { - ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + c[i,j] = 0; } - ftpushentry(plan, ref rowptr, opcomplexcodeletfft, k, n, 2, 0, _params); - if( childplan ) + } + } + else + { + for(i=ci1; i<=ci2; i++) + { + for(i_=cj1; i_<=cj2;i_++) { - ftpushentry(plan, ref rowptr, opend, k, n, 2, 0, _params); + c[i,i_] = beta*c[i,i_]; } - return; } - if( n<=raderthreshold ) + } + + // + // A*B + // + if( !transa && !transb ) + { + for(l=ai1; l<=ai2; l++) { - - // - // Handle prime-factor FFT's with Rader's FFT - // - m = n-1; - if( childplan ) + for(r=bi1; r<=bi2; r++) { - ftpushentry2(plan, ref rowptr, opstart, k, n, 2, -1, ftoptimisticestimate(n, _params), _params); + v = alpha*a[l,aj1+r-bi1]; + k = ci1+l-ai1; + i1_ = (bj1) - (cj1); + for(i_=cj1; i_<=cj2;i_++) + { + c[k,i_] = c[k,i_] + v*b[r,i_+i1_]; + } } - ntheory.findprimitiverootandinverse(n, ref gq, ref giq, _params); - ftpushentry4(plan, ref rowptr, opradersfft, k, n, 2, 2, gq, giq, precrptr, _params); - ftprecomputeradersfft(n, gq, giq, plan.precr, precrptr, _params); - precrptr = precrptr+2*(n-1); - row0 = rowptr; - ftpushentry(plan, ref rowptr, opjmp, 0, 0, 0, 0, _params); - ftcomplexfftplanrec(m, 1, true, false, ref rowptr, ref bluesteinsize, ref precrptr, ref preciptr, plan, _params); - row1 = rowptr; - plan.entries[row0,colparam0] = row1-row0; - if( childplan ) + } + return; + } + + // + // A*B' + // + if( !transa && transb ) + { + if( arows*acols=alglib.ap.rows(plan.entries) ) + int i = 0; + + + // + // Prepare + // + if( n<1 ) { - apserv.imatrixresize(ref plan.entries, Math.Max(2*alglib.ap.rows(plan.entries), 1), colscnt, _params); + return; + } + if( n==1 ) + { + x[0] = 0; + return; + } + if( alglib.ap.len(buf.ra1)=alglib.ap.rows(plan.entries) ) - { - apserv.imatrixresize(ref plan.entries, Math.Max(2*alglib.ap.rows(plan.entries), 1), colscnt, _params); - } - plan.entries[rowptr,coltype] = etype; - plan.entries[rowptr,coloperandscnt] = eopcnt; - plan.entries[rowptr,coloperandsize] = eopsize; - plan.entries[rowptr,colmicrovectorsize] = emcvsize; - plan.entries[rowptr,colparam0] = eparam0; - plan.entries[rowptr,colparam1] = eparam1; - plan.entries[rowptr,colparam2] = eparam2; - plan.entries[rowptr,colparam3] = eparam3; - rowptr = rowptr+1; - } - + bool normin = new bool(); + double[] cnorm = new double[0]; + double[,] a1 = new double[0,0]; + double[] x1 = new double[0]; + int i = 0; + int i_ = 0; + int i1_ = 0; - /************************************************************************* - This subroutine applies subplan to input/output array A. + s = 0; - INPUT PARAMETERS: - Plan - transformation plan - SubPlan - subplan index - A - array, must be large enough for plan to work - ABase - base offset in array A, this value points to start of - subarray whose length is equal to length of the plan - AOffset - offset with respect to ABase, 0<=AOffset(double)(cnorm[imax]) ) + { + imax = k; + } + } + tmax = cnorm[imax]; + if( (double)(tmax)<=(double)(bignum) ) + { + tscal = 1; + } + else + { + tscal = 1/(smlnum*tmax); + for(i_=1; i_<=n;i_++) + { + cnorm[i_] = tscal*cnorm[i_]; } + } + + // + // Compute a bound on the computed solution vector to see if the + // Level 2 BLAS routine DTRSV can be used. + // + j = 1; + for(k=2; k<=n; k++) + { + if( (double)(Math.Abs(x[k]))>(double)(Math.Abs(x[j])) ) + { + j = k; + } + } + xmax = Math.Abs(x[j]); + xbnd = xmax; + if( notran ) + { // - // Process "parallel call" operation: - // * we perform initial check for consistency between parent and child plans - // * we call FTSplitAndApplyParallelPlan(), which splits parallel plan into - // several parallel tasks + // Compute the growth in A * x = b. // - if( operation==opparallelcall ) + if( upper ) { - parentsize = operandsize*microvectorsize; - childsize = plan.entries[rowidx+param0,coloperandscnt]*plan.entries[rowidx+param0,coloperandsize]*plan.entries[rowidx+param0,colmicrovectorsize]; - alglib.ap.assert(plan.entries[rowidx+param0,coltype]==opstart, "FTApplySubPlan: incorrect child subplan header"); - alglib.ap.assert(parentsize==childsize, "FTApplySubPlan: incorrect child subplan header"); - chunksize = Math.Max(recursivethreshold/childsize, 1); - i = 0; - while( i0 && j<=jlast) || (jinc<0 && j>=jlast) ) + { + + // + // Exit the loop if the growth factor is too small. + // + if( (double)(grow)<=(double)(smlnum) ) + { + break; + } + + // + // M(j) = G(j-1) / abs(A(j,j)) + // + tjj = Math.Abs(a[j,j]); + xbnd = Math.Min(xbnd, Math.Min(1, tjj)*grow); + if( (double)(tjj+cnorm[j])>=(double)(smlnum) ) + { + + // + // G(j) = G(j-1)*( 1 + CNORM(j) / abs(A(j,j)) ) + // + grow = grow*(tjj/(tjj+cnorm[j])); + } + else + { + + // + // G(j) could overflow, set GROW to 0. + // + grow = 0; + } + if( j==jlast ) + { + grow = xbnd; + } + j = j+jinc; + } + } + else { - chunksize = Math.Min(chunksize, operandscnt-i); - ftapplysubplan(plan, rowidx+param0, a, abase, aoffset+i*childsize, buf, chunksize, _params); - i = i+chunksize; + + // + // A is unit triangular. + // + // Compute GROW = 1/G(j), where G(0) = max{x(i), i=1,...,n}. + // + grow = Math.Min(1, 1/Math.Max(xbnd, smlnum)); + j = jfirst; + while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) + { + + // + // Exit the loop if the growth factor is too small. + // + if( (double)(grow)<=(double)(smlnum) ) + { + break; + } + + // + // G(j) = G(j-1)*( 1 + CNORM(j) ) + // + grow = grow*(1/(1+cnorm[j])); + j = j+jinc; + } } - rowidx = rowidx+1; - continue; } + } + else + { // - // Process "reference complex FFT" operation + // Compute the growth in A' * x = b. // - if( operation==opcomplexreffft ) + if( upper ) { - ftapplycomplexreffft(a, abase+aoffset, operandscnt, operandsize, microvectorsize, buf, _params); - rowidx = rowidx+1; - continue; + jfirst = 1; + jlast = n; + jinc = 1; } - - // - // Process "codelet FFT" operation - // - if( operation==opcomplexcodeletfft ) + else { - ftapplycomplexcodeletfft(a, abase+aoffset, operandscnt, operandsize, microvectorsize, _params); - rowidx = rowidx+1; - continue; + jfirst = n; + jlast = 1; + jinc = -1; } - - // - // Process "integrated codelet FFT" operation - // - if( operation==opcomplexcodelettwfft ) + if( (double)(tscal)!=(double)(1) ) { - ftapplycomplexcodelettwfft(a, abase+aoffset, operandscnt, operandsize, microvectorsize, _params); - rowidx = rowidx+1; - continue; + grow = 0; } - - // - // Process Bluestein's FFT operation - // - if( operation==opbluesteinsfft ) + else { - alglib.ap.assert(microvectorsize==2, "FTApplySubPlan: microvectorsize!=2 for Bluesteins FFT"); - alglib.smp.ae_shared_pool_retrieve(plan.bluesteinpool, ref bufa); - alglib.smp.ae_shared_pool_retrieve(plan.bluesteinpool, ref bufb); - alglib.smp.ae_shared_pool_retrieve(plan.bluesteinpool, ref bufc); - alglib.smp.ae_shared_pool_retrieve(plan.bluesteinpool, ref bufd); - ftbluesteinsfft(plan, a, abase, aoffset, operandscnt, operandsize, plan.entries[rowidx,colparam0], plan.entries[rowidx,colparam2], rowidx+plan.entries[rowidx,colparam1], bufa.val, bufb.val, bufc.val, bufd.val, _params); - alglib.smp.ae_shared_pool_recycle(plan.bluesteinpool, ref bufa); - alglib.smp.ae_shared_pool_recycle(plan.bluesteinpool, ref bufb); - alglib.smp.ae_shared_pool_recycle(plan.bluesteinpool, ref bufc); - alglib.smp.ae_shared_pool_recycle(plan.bluesteinpool, ref bufd); - rowidx = rowidx+1; - continue; + if( nounit ) + { + + // + // A is non-unit triangular. + // + // Compute GROW = 1/G(j) and XBND = 1/M(j). + // Initially, M(0) = max{x(i), i=1,...,n}. + // + grow = 1/Math.Max(xbnd, smlnum); + xbnd = grow; + j = jfirst; + while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) + { + + // + // Exit the loop if the growth factor is too small. + // + if( (double)(grow)<=(double)(smlnum) ) + { + break; + } + + // + // G(j) = max( G(j-1), M(j-1)*( 1 + CNORM(j) ) ) + // + xj = 1+cnorm[j]; + grow = Math.Min(grow, xbnd/xj); + + // + // M(j) = M(j-1)*( 1 + CNORM(j) ) / abs(A(j,j)) + // + tjj = Math.Abs(a[j,j]); + if( (double)(xj)>(double)(tjj) ) + { + xbnd = xbnd*(tjj/xj); + } + if( j==jlast ) + { + grow = Math.Min(grow, xbnd); + } + j = j+jinc; + } + } + else + { + + // + // A is unit triangular. + // + // Compute GROW = 1/G(j), where G(0) = max{x(i), i=1,...,n}. + // + grow = Math.Min(1, 1/Math.Max(xbnd, smlnum)); + j = jfirst; + while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) + { + + // + // Exit the loop if the growth factor is too small. + // + if( (double)(grow)<=(double)(smlnum) ) + { + break; + } + + // + // G(j) = ( 1 + CNORM(j) )*G(j-1) + // + xj = 1+cnorm[j]; + grow = grow/xj; + j = j+jinc; + } + } } + } + if( (double)(grow*tscal)>(double)(smlnum) ) + { // - // Process Rader's FFT + // Use the Level 2 BLAS solve if the reciprocal of the bound on + // elements of X is not too small. // - if( operation==opradersfft ) + if( (upper && notran) || (!upper && !notran) ) { - ftradersfft(plan, a, abase, aoffset, operandscnt, operandsize, rowidx+plan.entries[rowidx,colparam0], plan.entries[rowidx,colparam1], plan.entries[rowidx,colparam2], plan.entries[rowidx,colparam3], buf, _params); - rowidx = rowidx+1; - continue; + if( nounit ) + { + vd = a[n,n]; + } + else + { + vd = 1; + } + x[n] = x[n]/vd; + for(i=n-1; i>=1; i--) + { + ip1 = i+1; + if( upper ) + { + v = 0.0; + for(i_=ip1; i_<=n;i_++) + { + v += a[i,i_]*x[i_]; + } + } + else + { + v = 0.0; + for(i_=ip1; i_<=n;i_++) + { + v += a[i_,i]*x[i_]; + } + } + if( nounit ) + { + vd = a[i,i]; + } + else + { + vd = 1; + } + x[i] = (x[i]-v)/vd; + } } - - // - // Process "complex twiddle factors" operation - // - if( operation==opcomplexfftfactors ) + else { - alglib.ap.assert(microvectorsize==2, "FTApplySubPlan: MicrovectorSize<>1"); - n1 = plan.entries[rowidx,colparam0]; - n2 = operandsize/n1; - for(i=0; i<=operandscnt-1; i++) + if( nounit ) { - ffttwcalc(a, abase+aoffset+i*operandsize*2, n1, n2, _params); + vd = a[1,1]; + } + else + { + vd = 1; + } + x[1] = x[1]/vd; + for(i=2; i<=n; i++) + { + im1 = i-1; + if( upper ) + { + v = 0.0; + for(i_=1; i_<=im1;i_++) + { + v += a[i_,i]*x[i_]; + } + } + else + { + v = 0.0; + for(i_=1; i_<=im1;i_++) + { + v += a[i,i_]*x[i_]; + } + } + if( nounit ) + { + vd = a[i,i]; + } + else + { + vd = 1; + } + x[i] = (x[i]-v)/vd; } - rowidx = rowidx+1; - continue; } + } + else + { // - // Process "complex transposition" operation + // Use a Level 1 BLAS solve, scaling intermediate results. // - if( operation==opcomplextranspose ) + if( (double)(xmax)>(double)(bignum) ) { - alglib.ap.assert(microvectorsize==2, "FTApplySubPlan: MicrovectorSize<>1"); - n1 = plan.entries[rowidx,colparam0]; - n2 = operandsize/n1; - for(i=0; i<=operandscnt-1; i++) + + // + // Scale X so that its components are less than or equal to + // BIGNUM in absolute value. + // + s = bignum/xmax; + for(i_=1; i_<=n;i_++) { - internalcomplexlintranspose(a, n1, n2, abase+aoffset+i*operandsize*2, buf, _params); + x[i_] = s*x[i_]; } - rowidx = rowidx+1; - continue; + xmax = bignum; } - - // - // Error - // - alglib.ap.assert(false, "FTApplySubPlan: unexpected plan type"); - } - } - - - /************************************************************************* - This subroutine applies complex reference FFT to input/output array A. - - VERY SLOW OPERATION, do not use it in real life plans :) - - INPUT PARAMETERS: - A - array, must be large enough for plan to work - Offs - offset of the subarray to process - OperandsCnt - operands count (see description of FastTransformPlan) - OperandSize - operand size (see description of FastTransformPlan) - MicrovectorSize-microvector size (see description of FastTransformPlan) - Buf - temporary array, must be at least OperandsCnt*OperandSize*MicrovectorSize - - OUTPUT PARAMETERS: - A - transformed array - - -- ALGLIB -- - Copyright 05.04.2013 by Bochkanov Sergey - *************************************************************************/ - private static void ftapplycomplexreffft(double[] a, - int offs, - int operandscnt, - int operandsize, - int microvectorsize, - double[] buf, - alglib.xparams _params) - { - int opidx = 0; - int i = 0; - int k = 0; - double hre = 0; - double him = 0; - double c = 0; - double s = 0; - double re = 0; - double im = 0; - int n = 0; - - alglib.ap.assert(operandscnt>=1, "FTApplyComplexRefFFT: OperandsCnt<1"); - alglib.ap.assert(operandsize>=1, "FTApplyComplexRefFFT: OperandSize<1"); - alglib.ap.assert(microvectorsize==2, "FTApplyComplexRefFFT: MicrovectorSize<>2"); - n = operandsize; - for(opidx=0; opidx<=operandscnt-1; opidx++) - { - for(i=0; i<=n-1; i++) + if( notran ) + { + + // + // Solve A * x = b + // + j = jfirst; + while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) + { + + // + // Compute x(j) = b(j) / A(j,j), scaling x if necessary. + // + xj = Math.Abs(x[j]); + flg = 0; + if( nounit ) + { + tjjs = a[j,j]*tscal; + } + else + { + tjjs = tscal; + if( (double)(tscal)==(double)(1) ) + { + flg = 100; + } + } + if( flg!=100 ) + { + tjj = Math.Abs(tjjs); + if( (double)(tjj)>(double)(smlnum) ) + { + + // + // abs(A(j,j)) > SMLNUM: + // + if( (double)(tjj)<(double)(1) ) + { + if( (double)(xj)>(double)(tjj*bignum) ) + { + + // + // Scale x by 1/b(j). + // + rec = 1/xj; + for(i_=1; i_<=n;i_++) + { + x[i_] = rec*x[i_]; + } + s = s*rec; + xmax = xmax*rec; + } + } + x[j] = x[j]/tjjs; + xj = Math.Abs(x[j]); + } + else + { + if( (double)(tjj)>(double)(0) ) + { + + // + // 0 < abs(A(j,j)) <= SMLNUM: + // + if( (double)(xj)>(double)(tjj*bignum) ) + { + + // + // Scale x by (1/abs(x(j)))*abs(A(j,j))*BIGNUM + // to avoid overflow when dividing by A(j,j). + // + rec = tjj*bignum/xj; + if( (double)(cnorm[j])>(double)(1) ) + { + + // + // Scale by 1/CNORM(j) to avoid overflow when + // multiplying x(j) times column j. + // + rec = rec/cnorm[j]; + } + for(i_=1; i_<=n;i_++) + { + x[i_] = rec*x[i_]; + } + s = s*rec; + xmax = xmax*rec; + } + x[j] = x[j]/tjjs; + xj = Math.Abs(x[j]); + } + else + { + + // + // A(j,j) = 0: Set x(1:n) = 0, x(j) = 1, and + // scale = 0, and compute a solution to A*x = 0. + // + for(i=1; i<=n; i++) + { + x[i] = 0; + } + x[j] = 1; + xj = 1; + s = 0; + xmax = 0; + } + } + } + + // + // Scale x if necessary to avoid overflow when adding a + // multiple of column j of A. + // + if( (double)(xj)>(double)(1) ) + { + rec = 1/xj; + if( (double)(cnorm[j])>(double)((bignum-xmax)*rec) ) + { + + // + // Scale x by 1/(2*abs(x(j))). + // + rec = rec*0.5; + for(i_=1; i_<=n;i_++) + { + x[i_] = rec*x[i_]; + } + s = s*rec; + } + } + else + { + if( (double)(xj*cnorm[j])>(double)(bignum-xmax) ) + { + + // + // Scale x by 1/2. + // + for(i_=1; i_<=n;i_++) + { + x[i_] = 0.5*x[i_]; + } + s = s*0.5; + } + } + if( upper ) + { + if( j>1 ) + { + + // + // Compute the update + // x(1:j-1) := x(1:j-1) - x(j) * A(1:j-1,j) + // + v = x[j]*tscal; + jm1 = j-1; + for(i_=1; i_<=jm1;i_++) + { + x[i_] = x[i_] - v*a[i_,j]; + } + i = 1; + for(k=2; k<=j-1; k++) + { + if( (double)(Math.Abs(x[k]))>(double)(Math.Abs(x[i])) ) + { + i = k; + } + } + xmax = Math.Abs(x[i]); + } + } + else + { + if( j(double)(Math.Abs(x[i])) ) + { + i = k; + } + } + xmax = Math.Abs(x[i]); + } + } + j = j+jinc; + } + } + else { - hre = 0; - him = 0; - for(k=0; k<=n-1; k++) + + // + // Solve A' * x = b + // + j = jfirst; + while( (jinc>0 && j<=jlast) || (jinc<0 && j>=jlast) ) { - re = a[offs+opidx*operandsize*2+2*k+0]; - im = a[offs+opidx*operandsize*2+2*k+1]; - c = Math.Cos(-(2*Math.PI*k*i/n)); - s = Math.Sin(-(2*Math.PI*k*i/n)); - hre = hre+c*re-s*im; - him = him+c*im+s*re; - } - buf[2*i+0] = hre; - buf[2*i+1] = him; + + // + // Compute x(j) = b(j) - sum A(k,j)*x(k). + // k<>j + // + xj = Math.Abs(x[j]); + uscal = tscal; + rec = 1/Math.Max(xmax, 1); + if( (double)(cnorm[j])>(double)((bignum-xj)*rec) ) + { + + // + // If x(j) could overflow, scale x by 1/(2*XMAX). + // + rec = rec*0.5; + if( nounit ) + { + tjjs = a[j,j]*tscal; + } + else + { + tjjs = tscal; + } + tjj = Math.Abs(tjjs); + if( (double)(tjj)>(double)(1) ) + { + + // + // Divide by A(j,j) when scaling x if A(j,j) > 1. + // + rec = Math.Min(1, rec*tjj); + uscal = uscal/tjjs; + } + if( (double)(rec)<(double)(1) ) + { + for(i_=1; i_<=n;i_++) + { + x[i_] = rec*x[i_]; + } + s = s*rec; + xmax = xmax*rec; + } + } + sumj = 0; + if( (double)(uscal)==(double)(1) ) + { + + // + // If the scaling needed for A in the dot product is 1, + // call DDOT to perform the dot product. + // + if( upper ) + { + if( j>1 ) + { + jm1 = j-1; + sumj = 0.0; + for(i_=1; i_<=jm1;i_++) + { + sumj += a[i_,j]*x[i_]; + } + } + else + { + sumj = 0; + } + } + else + { + if( j(double)(smlnum) ) + { + + // + // abs(A(j,j)) > SMLNUM: + // + if( (double)(tjj)<(double)(1) ) + { + if( (double)(xj)>(double)(tjj*bignum) ) + { + + // + // Scale X by 1/abs(x(j)). + // + rec = 1/xj; + for(i_=1; i_<=n;i_++) + { + x[i_] = rec*x[i_]; + } + s = s*rec; + xmax = xmax*rec; + } + } + x[j] = x[j]/tjjs; + } + else + { + if( (double)(tjj)>(double)(0) ) + { + + // + // 0 < abs(A(j,j)) <= SMLNUM: + // + if( (double)(xj)>(double)(tjj*bignum) ) + { + + // + // Scale x by (1/abs(x(j)))*abs(A(j,j))*BIGNUM. + // + rec = tjj*bignum/xj; + for(i_=1; i_<=n;i_++) + { + x[i_] = rec*x[i_]; + } + s = s*rec; + xmax = xmax*rec; + } + x[j] = x[j]/tjjs; + } + else + { + + // + // A(j,j) = 0: Set x(1:n) = 0, x(j) = 1, and + // scale = 0, and compute a solution to A'*x = 0. + // + for(i=1; i<=n; i++) + { + x[i] = 0; + } + x[j] = 1; + s = 0; + xmax = 0; + } + } + } + } + else + { + + // + // Compute x(j) := x(j) / A(j,j) - sumj if the dot + // product has already been divided by 1/A(j,j). + // + x[j] = x[j]/tjjs-sumj; + } + xmax = Math.Max(xmax, Math.Abs(x[j])); + j = j+jinc; + } } - for(i=0; i<=operandsize*2-1; i++) + s = s/tscal; + } + + // + // Scale the column norms by 1/TSCAL for return. + // + if( (double)(tscal)!=(double)(1) ) + { + v = 1/tscal; + for(i_=1; i_<=n;i_++) { - a[offs+opidx*operandsize*2+i] = buf[i]; + cnorm[i_] = v*cnorm[i_]; } } } + } + public partial class safesolve + { /************************************************************************* - This subroutine applies complex codelet FFT to input/output array A. - - INPUT PARAMETERS: - A - array, must be large enough for plan to work - Offs - offset of the subarray to process - OperandsCnt - operands count (see description of FastTransformPlan) - OperandSize - operand size (see description of FastTransformPlan) - MicrovectorSize-microvector size, must be 2 - - OUTPUT PARAMETERS: - A - transformed array + Real implementation of CMatrixScaledTRSafeSolve - -- ALGLIB -- - Copyright 05.04.2013 by Bochkanov Sergey + -- ALGLIB routine -- + 21.01.2010 + Bochkanov Sergey *************************************************************************/ - private static void ftapplycomplexcodeletfft(double[] a, - int offs, - int operandscnt, - int operandsize, - int microvectorsize, + public static bool rmatrixscaledtrsafesolve(double[,] a, + double sa, + int n, + ref double[] x, + bool isupper, + int trans, + bool isunit, + double maxgrowth, alglib.xparams _params) { - int opidx = 0; - int n = 0; - int aoffset = 0; - double a0x = 0; - double a0y = 0; - double a1x = 0; - double a1y = 0; - double a2x = 0; - double a2y = 0; - double a3x = 0; - double a3y = 0; - double a4x = 0; - double a4y = 0; - double a5x = 0; - double a5y = 0; - double v0 = 0; - double v1 = 0; - double v2 = 0; - double v3 = 0; - double t1x = 0; - double t1y = 0; - double t2x = 0; - double t2y = 0; - double t3x = 0; - double t3y = 0; - double t4x = 0; - double t4y = 0; - double t5x = 0; - double t5y = 0; - double m1x = 0; - double m1y = 0; - double m2x = 0; - double m2y = 0; - double m3x = 0; - double m3y = 0; - double m4x = 0; - double m4y = 0; - double m5x = 0; - double m5y = 0; - double s1x = 0; - double s1y = 0; - double s2x = 0; - double s2y = 0; - double s3x = 0; - double s3y = 0; - double s4x = 0; - double s4y = 0; - double s5x = 0; - double s5y = 0; - double c1 = 0; - double c2 = 0; - double c3 = 0; - double c4 = 0; - double c5 = 0; - double v = 0; + bool result = new bool(); + double lnmax = 0; + double nrmb = 0; + double nrmx = 0; + int i = 0; + complex alpha = 0; + complex beta = 0; + double vr = 0; + complex cx = 0; + double[] tmp = new double[0]; + int i_ = 0; - alglib.ap.assert(operandscnt>=1, "FTApplyComplexCodeletFFT: OperandsCnt<1"); - alglib.ap.assert(operandsize>=1, "FTApplyComplexCodeletFFT: OperandSize<1"); - alglib.ap.assert(microvectorsize==2, "FTApplyComplexCodeletFFT: MicrovectorSize<>2"); - n = operandsize; + alglib.ap.assert(n>0, "RMatrixTRSafeSolve: incorrect N!"); + alglib.ap.assert(trans==0 || trans==1, "RMatrixTRSafeSolve: incorrect Trans!"); + result = true; + lnmax = Math.Log(math.maxrealnumber); // - // Hard-coded transforms for different N's + // Quick return if possible // - alglib.ap.assert(n<=maxradix, "FTApplyComplexCodeletFFT: N>MaxRadix"); - if( n==2 ) + if( n<=0 ) { - for(opidx=0; opidx<=operandscnt-1; opidx++) - { - aoffset = offs+opidx*operandsize*2; - a0x = a[aoffset+0]; - a0y = a[aoffset+1]; - a1x = a[aoffset+2]; - a1y = a[aoffset+3]; - v0 = a0x+a1x; - v1 = a0y+a1y; - v2 = a0x-a1x; - v3 = a0y-a1y; - a[aoffset+0] = v0; - a[aoffset+1] = v1; - a[aoffset+2] = v2; - a[aoffset+3] = v3; - } - return; + return result; } - if( n==3 ) + + // + // Load norms: right part and X + // + nrmb = 0; + for(i=0; i<=n-1; i++) { - c1 = Math.Cos(2*Math.PI/3)-1; - c2 = Math.Sin(2*Math.PI/3); - for(opidx=0; opidx<=operandscnt-1; opidx++) - { - aoffset = offs+opidx*operandsize*2; - a0x = a[aoffset+0]; - a0y = a[aoffset+1]; - a1x = a[aoffset+2]; - a1y = a[aoffset+3]; - a2x = a[aoffset+4]; - a2y = a[aoffset+5]; - t1x = a1x+a2x; - t1y = a1y+a2y; - a0x = a0x+t1x; - a0y = a0y+t1y; - m1x = c1*t1x; - m1y = c1*t1y; - m2x = c2*(a1y-a2y); - m2y = c2*(a2x-a1x); - s1x = a0x+m1x; - s1y = a0y+m1y; - a1x = s1x+m2x; - a1y = s1y+m2y; - a2x = s1x-m2x; - a2y = s1y-m2y; - a[aoffset+0] = a0x; - a[aoffset+1] = a0y; - a[aoffset+2] = a1x; - a[aoffset+3] = a1y; - a[aoffset+4] = a2x; - a[aoffset+5] = a2y; - } - return; + nrmb = Math.Max(nrmb, Math.Abs(x[i])); } - if( n==4 ) + nrmx = 0; + + // + // Solve + // + tmp = new double[n]; + result = true; + if( isupper && trans==0 ) { - for(opidx=0; opidx<=operandscnt-1; opidx++) + + // + // U*x = b + // + for(i=n-1; i>=0; i--) { - aoffset = offs+opidx*operandsize*2; - a0x = a[aoffset+0]; - a0y = a[aoffset+1]; - a1x = a[aoffset+2]; - a1y = a[aoffset+3]; - a2x = a[aoffset+4]; - a2y = a[aoffset+5]; - a3x = a[aoffset+6]; - a3y = a[aoffset+7]; - t1x = a0x+a2x; - t1y = a0y+a2y; - t2x = a1x+a3x; - t2y = a1y+a3y; - m2x = a0x-a2x; - m2y = a0y-a2y; - m3x = a1y-a3y; - m3y = a3x-a1x; - a[aoffset+0] = t1x+t2x; - a[aoffset+1] = t1y+t2y; - a[aoffset+4] = t1x-t2x; - a[aoffset+5] = t1y-t2y; - a[aoffset+2] = m2x+m3x; - a[aoffset+3] = m2y+m3y; - a[aoffset+6] = m2x-m3x; - a[aoffset+7] = m2y-m3y; + + // + // Task is reduced to alpha*x[i] = beta + // + if( isunit ) + { + alpha = sa; + } + else + { + alpha = a[i,i]*sa; + } + if( i0 ) + { + for(i_=0; i_<=i-1;i_++) + { + tmp[i_] = sa*a[i,i_]; + } + vr = 0.0; + for(i_=0; i_<=i-1;i_++) + { + vr += tmp[i_]*x[i_]; + } + beta = x[i]-vr; + } + else + { + beta = x[i]; + } + + // + // solve alpha*x[i] = beta + // + result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref cx, _params); + if( !result ) + { + return result; + } + x[i] = cx.x; } - return; + return result; } - if( n==6 ) + if( isupper && trans==1 ) { - c1 = Math.Cos(2*Math.PI/3)-1; - c2 = Math.Sin(2*Math.PI/3); - c3 = Math.Cos(-(Math.PI/3)); - c4 = Math.Sin(-(Math.PI/3)); - for(opidx=0; opidx<=operandscnt-1; opidx++) + + // + // U^T*x = b + // + for(i=0; i<=n-1; i++) { - aoffset = offs+opidx*operandsize*2; - a0x = a[aoffset+0]; - a0y = a[aoffset+1]; - a1x = a[aoffset+2]; - a1y = a[aoffset+3]; - a2x = a[aoffset+4]; - a2y = a[aoffset+5]; - a3x = a[aoffset+6]; - a3y = a[aoffset+7]; - a4x = a[aoffset+8]; - a4y = a[aoffset+9]; - a5x = a[aoffset+10]; - a5y = a[aoffset+11]; - v0 = a0x; - v1 = a0y; - a0x = a0x+a3x; - a0y = a0y+a3y; - a3x = v0-a3x; - a3y = v1-a3y; - v0 = a1x; - v1 = a1y; - a1x = a1x+a4x; - a1y = a1y+a4y; - a4x = v0-a4x; - a4y = v1-a4y; - v0 = a2x; - v1 = a2y; - a2x = a2x+a5x; - a2y = a2y+a5y; - a5x = v0-a5x; - a5y = v1-a5y; - t4x = a4x*c3-a4y*c4; - t4y = a4x*c4+a4y*c3; - a4x = t4x; - a4y = t4y; - t5x = -(a5x*c3)-a5y*c4; - t5y = a5x*c4-a5y*c3; - a5x = t5x; - a5y = t5y; - t1x = a1x+a2x; - t1y = a1y+a2y; - a0x = a0x+t1x; - a0y = a0y+t1y; - m1x = c1*t1x; - m1y = c1*t1y; - m2x = c2*(a1y-a2y); - m2y = c2*(a2x-a1x); - s1x = a0x+m1x; - s1y = a0y+m1y; - a1x = s1x+m2x; - a1y = s1y+m2y; - a2x = s1x-m2x; - a2y = s1y-m2y; - t1x = a4x+a5x; - t1y = a4y+a5y; - a3x = a3x+t1x; - a3y = a3y+t1y; - m1x = c1*t1x; - m1y = c1*t1y; - m2x = c2*(a4y-a5y); - m2y = c2*(a5x-a4x); - s1x = a3x+m1x; - s1y = a3y+m1y; - a4x = s1x+m2x; - a4y = s1y+m2y; - a5x = s1x-m2x; - a5y = s1y-m2y; - a[aoffset+0] = a0x; - a[aoffset+1] = a0y; - a[aoffset+2] = a3x; - a[aoffset+3] = a3y; - a[aoffset+4] = a1x; - a[aoffset+5] = a1y; - a[aoffset+6] = a4x; - a[aoffset+7] = a4y; - a[aoffset+8] = a2x; - a[aoffset+9] = a2y; - a[aoffset+10] = a5x; - a[aoffset+11] = a5y; + + // + // Task is reduced to alpha*x[i] = beta + // + if( isunit ) + { + alpha = sa; + } + else + { + alpha = a[i,i]*sa; + } + beta = x[i]; + + // + // solve alpha*x[i] = beta + // + result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref cx, _params); + if( !result ) + { + return result; + } + x[i] = cx.x; + + // + // update the rest of right part + // + if( i=0; i--) + { + + // + // Task is reduced to alpha*x[i] = beta + // + if( isunit ) + { + alpha = sa; + } + else + { + alpha = a[i,i]*sa; + } + beta = x[i]; + + // + // solve alpha*x[i] = beta + // + result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref cx, _params); + if( !result ) + { + return result; + } + x[i] = cx.x; + + // + // update the rest of right part + // + if( i>0 ) + { + vr = cx.x; + for(i_=0; i_<=i-1;i_++) + { + tmp[i_] = sa*a[i,i_]; + } + for(i_=0; i_<=i-1;i_++) + { + x[i_] = x[i_] - vr*tmp[i_]; + } + } } - return; + return result; } + result = false; + return result; } /************************************************************************* - This subroutine applies complex "integrated" codelet FFT to input/output - array A. "Integrated" codelet differs from "normal" one in following ways: - * it can work with MicrovectorSize>1 - * hence, it can be used in Cooley-Tukey FFT without transpositions - * it performs inlined multiplication by twiddle factors of Cooley-Tukey - FFT with N2=MicrovectorSize/2. + Internal subroutine for safe solution of - INPUT PARAMETERS: - A - array, must be large enough for plan to work - Offs - offset of the subarray to process - OperandsCnt - operands count (see description of FastTransformPlan) - OperandSize - operand size (see description of FastTransformPlan) - MicrovectorSize-microvector size, must be 1 + SA*op(A)=b - OUTPUT PARAMETERS: - A - transformed array + where A is NxN upper/lower triangular/unitriangular matrix, op(A) is + either identity transform, transposition or Hermitian transposition, SA is + a scaling factor such that max(|SA*A[i,j]|) is close to 1.0 in magnutude. - -- ALGLIB -- - Copyright 05.04.2013 by Bochkanov Sergey + This subroutine limits relative growth of solution (in inf-norm) by + MaxGrowth, returning False if growth exceeds MaxGrowth. Degenerate or + near-degenerate matrices are handled correctly (False is returned) as long + as MaxGrowth is significantly less than MaxRealNumber/norm(b). + + -- ALGLIB routine -- + 21.01.2010 + Bochkanov Sergey *************************************************************************/ - private static void ftapplycomplexcodelettwfft(double[] a, - int offs, - int operandscnt, - int operandsize, - int microvectorsize, + public static bool cmatrixscaledtrsafesolve(complex[,] a, + double sa, + int n, + ref complex[] x, + bool isupper, + int trans, + bool isunit, + double maxgrowth, alglib.xparams _params) { - int opidx = 0; - int mvidx = 0; - int n = 0; - int m = 0; - int aoffset0 = 0; - int aoffset2 = 0; - int aoffset4 = 0; - int aoffset6 = 0; - int aoffset8 = 0; - int aoffset10 = 0; - double a0x = 0; - double a0y = 0; - double a1x = 0; - double a1y = 0; - double a2x = 0; - double a2y = 0; - double a3x = 0; - double a3y = 0; - double a4x = 0; - double a4y = 0; - double a5x = 0; - double a5y = 0; - double v0 = 0; - double v1 = 0; - double v2 = 0; - double v3 = 0; - double q0x = 0; - double q0y = 0; - double t1x = 0; - double t1y = 0; - double t2x = 0; - double t2y = 0; - double t3x = 0; - double t3y = 0; - double t4x = 0; - double t4y = 0; - double t5x = 0; - double t5y = 0; - double m1x = 0; - double m1y = 0; - double m2x = 0; - double m2y = 0; - double m3x = 0; - double m3y = 0; - double m4x = 0; - double m4y = 0; - double m5x = 0; - double m5y = 0; - double s1x = 0; - double s1y = 0; - double s2x = 0; - double s2y = 0; - double s3x = 0; - double s3y = 0; - double s4x = 0; - double s4y = 0; - double s5x = 0; - double s5y = 0; - double c1 = 0; - double c2 = 0; - double c3 = 0; - double c4 = 0; - double c5 = 0; - double v = 0; - double tw0 = 0; - double tw1 = 0; - double twx = 0; - double twxm1 = 0; - double twy = 0; - double tw2x = 0; - double tw2y = 0; - double tw3x = 0; - double tw3y = 0; - double tw4x = 0; - double tw4y = 0; - double tw5x = 0; - double tw5y = 0; + bool result = new bool(); + double lnmax = 0; + double nrmb = 0; + double nrmx = 0; + int i = 0; + complex alpha = 0; + complex beta = 0; + complex vc = 0; + complex[] tmp = new complex[0]; + int i_ = 0; - alglib.ap.assert(operandscnt>=1, "FTApplyComplexCodeletFFT: OperandsCnt<1"); - alglib.ap.assert(operandsize>=1, "FTApplyComplexCodeletFFT: OperandSize<1"); - alglib.ap.assert(microvectorsize>=1, "FTApplyComplexCodeletFFT: MicrovectorSize<>1"); - alglib.ap.assert(microvectorsize%2==0, "FTApplyComplexCodeletFFT: MicrovectorSize is not even"); - n = operandsize; - m = microvectorsize/2; + alglib.ap.assert(n>0, "CMatrixTRSafeSolve: incorrect N!"); + alglib.ap.assert((trans==0 || trans==1) || trans==2, "CMatrixTRSafeSolve: incorrect Trans!"); + result = true; + lnmax = Math.Log(math.maxrealnumber); // - // Hard-coded transforms for different N's + // Quick return if possible // - alglib.ap.assert(n<=maxradix, "FTApplyComplexCodeletTwFFT: N>MaxRadix"); - if( n==2 ) + if( n<=0 ) { - v = -(2*Math.PI/(n*m)); - tw0 = -(2*math.sqr(Math.Sin(0.5*v))); - tw1 = Math.Sin(v); - for(opidx=0; opidx<=operandscnt-1; opidx++) + return result; + } + + // + // Load norms: right part and X + // + nrmb = 0; + for(i=0; i<=n-1; i++) + { + nrmb = Math.Max(nrmb, math.abscomplex(x[i])); + } + nrmx = 0; + + // + // Solve + // + tmp = new complex[n]; + result = true; + if( isupper && trans==0 ) + { + + // + // U*x = b + // + for(i=n-1; i>=0; i--) { - aoffset0 = offs+opidx*operandsize*microvectorsize; - aoffset2 = aoffset0+microvectorsize; - twxm1 = 0.0; - twy = 0.0; - for(mvidx=0; mvidx<=m-1; mvidx++) + + // + // Task is reduced to alpha*x[i] = beta + // + if( isunit ) { - a0x = a[aoffset0]; - a0y = a[aoffset0+1]; - a1x = a[aoffset2]; - a1y = a[aoffset2+1]; - v0 = a0x+a1x; - v1 = a0y+a1y; - v2 = a0x-a1x; - v3 = a0y-a1y; - a[aoffset0] = v0; - a[aoffset0+1] = v1; - a[aoffset2] = v2*(1+twxm1)-v3*twy; - a[aoffset2+1] = v3*(1+twxm1)+v2*twy; - aoffset0 = aoffset0+2; - aoffset2 = aoffset2+2; - if( (mvidx+1)%updatetw==0 ) + alpha = sa; + } + else + { + alpha = a[i,i]*sa; + } + if( i0 ) + { + for(i_=0; i_<=i-1;i_++) { - v = -(2*Math.PI*(mvidx+1)/(n*m)); - twxm1 = Math.Sin(0.5*v); - twxm1 = -(2*twxm1*twxm1); - twy = Math.Sin(v); - twx = twxm1+1; + tmp[i_] = sa*a[i,i_]; } - else + vc = 0.0; + for(i_=0; i_<=i-1;i_++) { - v = twxm1+tw0+twxm1*tw0-twy*tw1; - twy = twy+tw1+twxm1*tw1+twy*tw0; - twxm1 = v; - twx = v+1; + vc += tmp[i_]*x[i_]; } + beta = x[i]-vc; + } + else + { + beta = x[i]; + } + + // + // solve alpha*x[i] = beta + // + result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); + if( !result ) + { + return result; } + x[i] = vc; } - return; + return result; } - if( n==4 ) + if( isupper && trans==1 ) { - v = -(2*Math.PI/(n*m)); - tw0 = -(2*math.sqr(Math.Sin(0.5*v))); - tw1 = Math.Sin(v); - for(opidx=0; opidx<=operandscnt-1; opidx++) + + // + // U^T*x = b + // + for(i=0; i<=n-1; i++) { - aoffset0 = offs+opidx*operandsize*microvectorsize; - aoffset2 = aoffset0+microvectorsize; - aoffset4 = aoffset2+microvectorsize; - aoffset6 = aoffset4+microvectorsize; - twx = 1.0; - twxm1 = 0.0; - twy = 0.0; - for(mvidx=0; mvidx<=m-1; mvidx++) + + // + // Task is reduced to alpha*x[i] = beta + // + if( isunit ) { - a0x = a[aoffset0]; - a0y = a[aoffset0+1]; - a1x = a[aoffset2]; - a1y = a[aoffset2+1]; - a2x = a[aoffset4]; - a2y = a[aoffset4+1]; - a3x = a[aoffset6]; - a3y = a[aoffset6+1]; - t1x = a0x+a2x; - t1y = a0y+a2y; - t2x = a1x+a3x; - t2y = a1y+a3y; - m2x = a0x-a2x; - m2y = a0y-a2y; - m3x = a1y-a3y; - m3y = a3x-a1x; - tw2x = twx*twx-twy*twy; - tw2y = 2*twx*twy; - tw3x = twx*tw2x-twy*tw2y; - tw3y = twx*tw2y+twy*tw2x; - a1x = m2x+m3x; - a1y = m2y+m3y; - a2x = t1x-t2x; - a2y = t1y-t2y; - a3x = m2x-m3x; - a3y = m2y-m3y; - a[aoffset0] = t1x+t2x; - a[aoffset0+1] = t1y+t2y; - a[aoffset2] = a1x*twx-a1y*twy; - a[aoffset2+1] = a1y*twx+a1x*twy; - a[aoffset4] = a2x*tw2x-a2y*tw2y; - a[aoffset4+1] = a2y*tw2x+a2x*tw2y; - a[aoffset6] = a3x*tw3x-a3y*tw3y; - a[aoffset6+1] = a3y*tw3x+a3x*tw3y; - aoffset0 = aoffset0+2; - aoffset2 = aoffset2+2; - aoffset4 = aoffset4+2; - aoffset6 = aoffset6+2; - if( (mvidx+1)%updatetw==0 ) + alpha = sa; + } + else + { + alpha = a[i,i]*sa; + } + beta = x[i]; + + // + // solve alpha*x[i] = beta + // + result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); + if( !result ) + { + return result; + } + x[i] = vc; + + // + // update the rest of right part + // + if( i=0; i--) { - aoffset0 = offs+opidx*operandsize*microvectorsize; - aoffset2 = aoffset0+microvectorsize; - aoffset4 = aoffset2+microvectorsize; - aoffset6 = aoffset4+microvectorsize; - aoffset8 = aoffset6+microvectorsize; - twx = 1.0; - twxm1 = 0.0; - twy = 0.0; - for(mvidx=0; mvidx<=m-1; mvidx++) + + // + // Task is reduced to alpha*x[i] = beta + // + if( isunit ) + { + alpha = sa; + } + else + { + alpha = a[i,i]*sa; + } + beta = x[i]; + + // + // solve alpha*x[i] = beta + // + result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); + if( !result ) { - a0x = a[aoffset0]; - a0y = a[aoffset0+1]; - a1x = a[aoffset2]; - a1y = a[aoffset2+1]; - a2x = a[aoffset4]; - a2y = a[aoffset4+1]; - a3x = a[aoffset6]; - a3y = a[aoffset6+1]; - a4x = a[aoffset8]; - a4y = a[aoffset8+1]; - t1x = a1x+a4x; - t1y = a1y+a4y; - t2x = a2x+a3x; - t2y = a2y+a3y; - t3x = a1x-a4x; - t3y = a1y-a4y; - t4x = a3x-a2x; - t4y = a3y-a2y; - t5x = t1x+t2x; - t5y = t1y+t2y; - q0x = a0x+t5x; - q0y = a0y+t5y; - m1x = c1*t5x; - m1y = c1*t5y; - m2x = c2*(t1x-t2x); - m2y = c2*(t1y-t2y); - m3x = -(c3*(t3y+t4y)); - m3y = c3*(t3x+t4x); - m4x = -(c4*t4y); - m4y = c4*t4x; - m5x = -(c5*t3y); - m5y = c5*t3x; - s3x = m3x-m4x; - s3y = m3y-m4y; - s5x = m3x+m5x; - s5y = m3y+m5y; - s1x = q0x+m1x; - s1y = q0y+m1y; - s2x = s1x+m2x; - s2y = s1y+m2y; - s4x = s1x-m2x; - s4y = s1y-m2y; - tw2x = twx*twx-twy*twy; - tw2y = 2*twx*twy; - tw3x = twx*tw2x-twy*tw2y; - tw3y = twx*tw2y+twy*tw2x; - tw4x = tw2x*tw2x-tw2y*tw2y; - tw4y = tw2x*tw2y+tw2y*tw2x; - a1x = s2x+s3x; - a1y = s2y+s3y; - a2x = s4x+s5x; - a2y = s4y+s5y; - a3x = s4x-s5x; - a3y = s4y-s5y; - a4x = s2x-s3x; - a4y = s2y-s3y; - a[aoffset0] = q0x; - a[aoffset0+1] = q0y; - a[aoffset2] = a1x*twx-a1y*twy; - a[aoffset2+1] = a1x*twy+a1y*twx; - a[aoffset4] = a2x*tw2x-a2y*tw2y; - a[aoffset4+1] = a2x*tw2y+a2y*tw2x; - a[aoffset6] = a3x*tw3x-a3y*tw3y; - a[aoffset6+1] = a3x*tw3y+a3y*tw3x; - a[aoffset8] = a4x*tw4x-a4y*tw4y; - a[aoffset8+1] = a4x*tw4y+a4y*tw4x; - aoffset0 = aoffset0+2; - aoffset2 = aoffset2+2; - aoffset4 = aoffset4+2; - aoffset6 = aoffset6+2; - aoffset8 = aoffset8+2; - if( (mvidx+1)%updatetw==0 ) + return result; + } + x[i] = vc; + + // + // update the rest of right part + // + if( i>0 ) + { + for(i_=0; i_<=i-1;i_++) { - v = -(2*Math.PI*(mvidx+1)/(n*m)); - twxm1 = Math.Sin(0.5*v); - twxm1 = -(2*twxm1*twxm1); - twy = Math.Sin(v); - twx = twxm1+1; + tmp[i_] = sa*a[i,i_]; } - else + for(i_=0; i_<=i-1;i_++) { - v = twxm1+tw0+twxm1*tw0-twy*tw1; - twy = twy+tw1+twxm1*tw1+twy*tw0; - twxm1 = v; - twx = v+1; + x[i_] = x[i_] - vc*tmp[i_]; } } } - return; + return result; } - if( n==6 ) + if( isupper && trans==2 ) { - c1 = Math.Cos(2*Math.PI/3)-1; - c2 = Math.Sin(2*Math.PI/3); - c3 = Math.Cos(-(Math.PI/3)); - c4 = Math.Sin(-(Math.PI/3)); - v = -(2*Math.PI/(n*m)); - tw0 = -(2*math.sqr(Math.Sin(0.5*v))); - tw1 = Math.Sin(v); - for(opidx=0; opidx<=operandscnt-1; opidx++) + + // + // U^H*x = b + // + for(i=0; i<=n-1; i++) { - aoffset0 = offs+opidx*operandsize*microvectorsize; - aoffset2 = aoffset0+microvectorsize; - aoffset4 = aoffset2+microvectorsize; - aoffset6 = aoffset4+microvectorsize; - aoffset8 = aoffset6+microvectorsize; - aoffset10 = aoffset8+microvectorsize; - twx = 1.0; - twxm1 = 0.0; - twy = 0.0; - for(mvidx=0; mvidx<=m-1; mvidx++) + + // + // Task is reduced to alpha*x[i] = beta + // + if( isunit ) { - a0x = a[aoffset0+0]; - a0y = a[aoffset0+1]; - a1x = a[aoffset2+0]; - a1y = a[aoffset2+1]; - a2x = a[aoffset4+0]; - a2y = a[aoffset4+1]; - a3x = a[aoffset6+0]; - a3y = a[aoffset6+1]; - a4x = a[aoffset8+0]; - a4y = a[aoffset8+1]; - a5x = a[aoffset10+0]; - a5y = a[aoffset10+1]; - v0 = a0x; - v1 = a0y; - a0x = a0x+a3x; - a0y = a0y+a3y; - a3x = v0-a3x; - a3y = v1-a3y; - v0 = a1x; - v1 = a1y; - a1x = a1x+a4x; - a1y = a1y+a4y; - a4x = v0-a4x; - a4y = v1-a4y; - v0 = a2x; - v1 = a2y; - a2x = a2x+a5x; - a2y = a2y+a5y; - a5x = v0-a5x; - a5y = v1-a5y; - t4x = a4x*c3-a4y*c4; - t4y = a4x*c4+a4y*c3; - a4x = t4x; - a4y = t4y; - t5x = -(a5x*c3)-a5y*c4; - t5y = a5x*c4-a5y*c3; - a5x = t5x; - a5y = t5y; - t1x = a1x+a2x; - t1y = a1y+a2y; - a0x = a0x+t1x; - a0y = a0y+t1y; - m1x = c1*t1x; - m1y = c1*t1y; - m2x = c2*(a1y-a2y); - m2y = c2*(a2x-a1x); - s1x = a0x+m1x; - s1y = a0y+m1y; - a1x = s1x+m2x; - a1y = s1y+m2y; - a2x = s1x-m2x; - a2y = s1y-m2y; - t1x = a4x+a5x; - t1y = a4y+a5y; - a3x = a3x+t1x; - a3y = a3y+t1y; - m1x = c1*t1x; - m1y = c1*t1y; - m2x = c2*(a4y-a5y); - m2y = c2*(a5x-a4x); - s1x = a3x+m1x; - s1y = a3y+m1y; - a4x = s1x+m2x; - a4y = s1y+m2y; - a5x = s1x-m2x; - a5y = s1y-m2y; - tw2x = twx*twx-twy*twy; - tw2y = 2*twx*twy; - tw3x = twx*tw2x-twy*tw2y; - tw3y = twx*tw2y+twy*tw2x; - tw4x = tw2x*tw2x-tw2y*tw2y; - tw4y = 2*tw2x*tw2y; - tw5x = tw3x*tw2x-tw3y*tw2y; - tw5y = tw3x*tw2y+tw3y*tw2x; - a[aoffset0+0] = a0x; - a[aoffset0+1] = a0y; - a[aoffset2+0] = a3x*twx-a3y*twy; - a[aoffset2+1] = a3y*twx+a3x*twy; - a[aoffset4+0] = a1x*tw2x-a1y*tw2y; - a[aoffset4+1] = a1y*tw2x+a1x*tw2y; - a[aoffset6+0] = a4x*tw3x-a4y*tw3y; - a[aoffset6+1] = a4y*tw3x+a4x*tw3y; - a[aoffset8+0] = a2x*tw4x-a2y*tw4y; - a[aoffset8+1] = a2y*tw4x+a2x*tw4y; - a[aoffset10+0] = a5x*tw5x-a5y*tw5y; - a[aoffset10+1] = a5y*tw5x+a5x*tw5y; - aoffset0 = aoffset0+2; - aoffset2 = aoffset2+2; - aoffset4 = aoffset4+2; - aoffset6 = aoffset6+2; - aoffset8 = aoffset8+2; - aoffset10 = aoffset10+2; - if( (mvidx+1)%updatetw==0 ) + alpha = sa; + } + else + { + alpha = math.conj(a[i,i])*sa; + } + beta = x[i]; + + // + // solve alpha*x[i] = beta + // + result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); + if( !result ) + { + return result; + } + x[i] = vc; + + // + // update the rest of right part + // + if( i=0; i--) + { + + // + // Task is reduced to alpha*x[i] = beta + // + if( isunit ) + { + alpha = sa; + } + else + { + alpha = math.conj(a[i,i])*sa; + } + beta = x[i]; + + // + // solve alpha*x[i] = beta + // + result = cbasicsolveandupdate(alpha, beta, lnmax, nrmb, maxgrowth, ref nrmx, ref vc, _params); + if( !result ) + { + return result; + } + x[i] = vc; + + // + // update the rest of right part + // + if( i>0 ) + { + for(i_=0; i_<=i-1;i_++) { - v = -(2*Math.PI*(mvidx+1)/(n*m)); - twxm1 = Math.Sin(0.5*v); - twxm1 = -(2*twxm1*twxm1); - twy = Math.Sin(v); - twx = twxm1+1; + tmp[i_] = sa*math.conj(a[i,i_]); } - else + for(i_=0; i_<=i-1;i_++) { - v = twxm1+tw0+twxm1*tw0-twy*tw1; - twy = twy+tw1+twxm1*tw1+twy*tw0; - twxm1 = v; - twx = v+1; + x[i_] = x[i_] - vc*tmp[i_]; } } } - return; + return result; } + result = false; + return result; } /************************************************************************* - This subroutine precomputes data for complex Bluestein's FFT and writes - them to array PrecR[] at specified offset. It is responsibility of the - caller to make sure that PrecR[] is large enough. + complex basic solver-updater for reduced linear system - INPUT PARAMETERS: - N - original size of the transform - M - size of the "padded" Bluestein's transform - PrecR - preallocated array - Offs - offset + alpha*x[i] = beta + + solves this equation and updates it in overlfow-safe manner (keeping track + of relative growth of solution). + + Parameters: + Alpha - alpha + Beta - beta + LnMax - precomputed Ln(MaxRealNumber) + BNorm - inf-norm of b (right part of original system) + MaxGrowth- maximum growth of norm(x) relative to norm(b) + XNorm - inf-norm of other components of X (which are already processed) + it is updated by CBasicSolveAndUpdate. + X - solution + + -- ALGLIB routine -- + 26.01.2009 + Bochkanov Sergey + *************************************************************************/ + private static bool cbasicsolveandupdate(complex alpha, + complex beta, + double lnmax, + double bnorm, + double maxgrowth, + ref double xnorm, + ref complex x, + alglib.xparams _params) + { + bool result = new bool(); + double v = 0; + + x = 0; + + result = false; + if( alpha==0 ) + { + return result; + } + if( beta!=0 ) + { + + // + // alpha*x[i]=beta + // + v = Math.Log(math.abscomplex(beta))-Math.Log(math.abscomplex(alpha)); + if( (double)(v)>(double)(lnmax) ) + { + return result; + } + x = beta/alpha; + } + else + { + + // + // alpha*x[i]=0 + // + x = 0; + } - OUTPUT PARAMETERS: - PrecR - data at Offs:Offs+4*M-1 are modified: - * PrecR[Offs:Offs+2*M-1] stores Z[k]=exp(i*pi*k^2/N) - * PrecR[Offs+2*M:Offs+4*M-1] stores FFT of the Z - Other parts of PrecR are unchanged. - - NOTE: this function performs internal M-point FFT. It allocates temporary - plan which is destroyed after leaving this function. + // + // update NrmX, test growth limit + // + xnorm = Math.Max(xnorm, math.abscomplex(x)); + if( (double)(xnorm)>(double)(maxgrowth*bnorm) ) + { + return result; + } + result = true; + return result; + } + + + } + public partial class linmin + { + public class linminstate : apobject + { + public bool brackt; + public bool stage1; + public int infoc; + public double dg; + public double dgm; + public double dginit; + public double dgtest; + public double dgx; + public double dgxm; + public double dgy; + public double dgym; + public double finit; + public double ftest1; + public double fm; + public double fx; + public double fxm; + public double fy; + public double fym; + public double stx; + public double sty; + public double stmin; + public double stmax; + public double width; + public double width1; + public double xtrapf; + public linminstate() + { + init(); + } + public override void init() + { + } + public override alglib.apobject make_copy() + { + linminstate _result = new linminstate(); + _result.brackt = brackt; + _result.stage1 = stage1; + _result.infoc = infoc; + _result.dg = dg; + _result.dgm = dgm; + _result.dginit = dginit; + _result.dgtest = dgtest; + _result.dgx = dgx; + _result.dgxm = dgxm; + _result.dgy = dgy; + _result.dgym = dgym; + _result.finit = finit; + _result.ftest1 = ftest1; + _result.fm = fm; + _result.fx = fx; + _result.fxm = fxm; + _result.fy = fy; + _result.fym = fym; + _result.stx = stx; + _result.sty = sty; + _result.stmin = stmin; + _result.stmax = stmax; + _result.width = width; + _result.width1 = width1; + _result.xtrapf = xtrapf; + return _result; + } + }; + + + public class armijostate : apobject + { + public bool needf; + public double[] x; + public double f; + public int n; + public double[] xbase; + public double[] s; + public double stplen; + public double fcur; + public double stpmax; + public int fmax; + public int nfev; + public int info; + public ap.rcommstate rstate; + public armijostate() + { + init(); + } + public override void init() + { + x = new double[0]; + xbase = new double[0]; + s = new double[0]; + rstate = new ap.rcommstate(); + } + public override alglib.apobject make_copy() + { + armijostate _result = new armijostate(); + _result.needf = needf; + _result.x = (double[])x.Clone(); + _result.f = f; + _result.n = n; + _result.xbase = (double[])xbase.Clone(); + _result.s = (double[])s.Clone(); + _result.stplen = stplen; + _result.fcur = fcur; + _result.stpmax = stpmax; + _result.fmax = fmax; + _result.nfev = nfev; + _result.info = info; + _result.rstate = rstate!=null ? (ap.rcommstate)rstate.make_copy() : null; + return _result; + } + }; + + + + + public const double ftol = 0.001; + public const double xtol = 100*math.machineepsilon; + public const int maxfev = 20; + public const double stpmin = 1.0E-50; + public const double defstpmax = 1.0E+50; + public const double armijofactor = 1.3; + + + /************************************************************************* + Normalizes direction/step pair: makes |D|=1, scales Stp. + If |D|=0, it returns, leavind D/Stp unchanged. -- ALGLIB -- - Copyright 08.05.2013 by Bochkanov Sergey + Copyright 01.04.2010 by Bochkanov Sergey *************************************************************************/ - private static void ftprecomputebluesteinsfft(int n, - int m, - double[] precr, - int offs, + public static void linminnormalized(ref double[] d, + ref double stp, + int n, alglib.xparams _params) { + double mx = 0; + double s = 0; int i = 0; - double bx = 0; - double by = 0; - fasttransformplan plan = new fasttransformplan(); + int i_ = 0; // - // Fill first half of PrecR with b[k] = exp(i*pi*k^2/N) + // first, scale D to avoid underflow/overflow durng squaring // - for(i=0; i<=2*m-1; i++) + mx = 0; + for(i=0; i<=n-1; i++) { - precr[offs+i] = 0; + mx = Math.Max(mx, Math.Abs(d[i])); } - for(i=0; i<=n-1; i++) + if( (double)(mx)==(double)(0) ) { - bx = Math.Cos(Math.PI/n*i*i); - by = Math.Sin(Math.PI/n*i*i); - precr[offs+2*i+0] = bx; - precr[offs+2*i+1] = by; - precr[offs+2*((m-i)%m)+0] = bx; - precr[offs+2*((m-i)%m)+1] = by; + return; + } + s = 1/mx; + for(i_=0; i_<=n-1;i_++) + { + d[i_] = s*d[i_]; } + stp = stp/s; // - // Precomputed FFT + // normalize D // - ftcomplexfftplan(m, 1, plan, _params); - for(i=0; i<=2*m-1; i++) + s = 0.0; + for(i_=0; i_<=n-1;i_++) { - precr[offs+2*m+i] = precr[offs+i]; + s += d[i_]*d[i_]; } - ftapplysubplan(plan, 0, precr, offs+2*m, 0, plan.buffer, 1, _params); + s = 1/Math.Sqrt(s); + for(i_=0; i_<=n-1;i_++) + { + d[i_] = s*d[i_]; + } + stp = stp/s; } /************************************************************************* - This subroutine applies complex Bluestein's FFT to input/output array A. + THE PURPOSE OF MCSRCH IS TO FIND A STEP WHICH SATISFIES A SUFFICIENT + DECREASE CONDITION AND A CURVATURE CONDITION. - INPUT PARAMETERS: - Plan - transformation plan - A - array, must be large enough for plan to work - ABase - base offset in array A, this value points to start of - subarray whose length is equal to length of the plan - AOffset - offset with respect to ABase, 0<=AOffsetinitial_point - after rounding to machine precision + + NOTE 4: + + when non-descent direction is specified, algorithm stops with MCINFO=0, + Stp=0 and initial point at X[]. + ::::::::::::::::::::::::::::::::::::::::: + + + PARAMETERS DESCRIPRION + + STAGE IS ZERO ON FIRST CALL, ZERO ON FINAL EXIT + + N IS A POSITIVE INTEGER INPUT VARIABLE SET TO THE NUMBER OF VARIABLES. + + X IS AN ARRAY OF LENGTH N. ON INPUT IT MUST CONTAIN THE BASE POINT FOR + THE LINE SEARCH. ON OUTPUT IT CONTAINS X+STP*S. + + F IS A VARIABLE. ON INPUT IT MUST CONTAIN THE VALUE OF F AT X. ON OUTPUT + IT CONTAINS THE VALUE OF F AT X + STP*S. + + G IS AN ARRAY OF LENGTH N. ON INPUT IT MUST CONTAIN THE GRADIENT OF F AT X. + ON OUTPUT IT CONTAINS THE GRADIENT OF F AT X + STP*S. + + S IS AN INPUT ARRAY OF LENGTH N WHICH SPECIFIES THE SEARCH DIRECTION. + STP IS A NONNEGATIVE VARIABLE. ON INPUT STP CONTAINS AN INITIAL ESTIMATE + OF A SATISFACTORY STEP. ON OUTPUT STP CONTAINS THE FINAL ESTIMATE. - /************************************************************************* - This subroutine precomputes data for complex Rader's FFT and writes them - to array PrecR[] at specified offset. It is responsibility of the caller - to make sure that PrecR[] is large enough. + FTOL AND GTOL ARE NONNEGATIVE INPUT VARIABLES. TERMINATION OCCURS WHEN THE + SUFFICIENT DECREASE CONDITION AND THE DIRECTIONAL DERIVATIVE CONDITION ARE + SATISFIED. - INPUT PARAMETERS: - N - original size of the transform (before reduction to N-1) - RQ - primitive root modulo N - RIQ - inverse of primitive root modulo N - PrecR - preallocated array - Offs - offset - - OUTPUT PARAMETERS: - PrecR - data at Offs:Offs+2*(N-1)-1 store FFT of Rader's factors, - other parts of PrecR are unchanged. - - NOTE: this function performs internal (N-1)-point FFT. It allocates temporary - plan which is destroyed after leaving this function. + XTOL IS A NONNEGATIVE INPUT VARIABLE. TERMINATION OCCURS WHEN THE RELATIVE + WIDTH OF THE INTERVAL OF UNCERTAINTY IS AT MOST XTOL. - -- ALGLIB -- - Copyright 08.05.2013 by Bochkanov Sergey - *************************************************************************/ - private static void ftprecomputeradersfft(int n, - int rq, - int riq, - double[] precr, - int offs, - alglib.xparams _params) - { - int q = 0; - fasttransformplan plan = new fasttransformplan(); - int kiq = 0; - double v = 0; + STPMIN AND STPMAX ARE NONNEGATIVE INPUT VARIABLES WHICH SPECIFY LOWER AND + UPPER BOUNDS FOR THE STEP. - - // - // Fill PrecR with Rader factors, perform FFT - // - kiq = 1; - for(q=0; q<=n-2; q++) - { - v = -(2*Math.PI*kiq/n); - precr[offs+2*q+0] = Math.Cos(v); - precr[offs+2*q+1] = Math.Sin(v); - kiq = kiq*riq%n; - } - ftcomplexfftplan(n-1, 1, plan, _params); - ftapplysubplan(plan, 0, precr, offs, 0, plan.buffer, 1, _params); - } + MAXFEV IS A POSITIVE INTEGER INPUT VARIABLE. TERMINATION OCCURS WHEN THE + NUMBER OF CALLS TO FCN IS AT LEAST MAXFEV BY THE END OF AN ITERATION. + INFO IS AN INTEGER OUTPUT VARIABLE SET AS FOLLOWS: + INFO = 0 IMPROPER INPUT PARAMETERS. - /************************************************************************* - This subroutine applies complex Rader's FFT to input/output array A. + INFO = 1 THE SUFFICIENT DECREASE CONDITION AND THE + DIRECTIONAL DERIVATIVE CONDITION HOLD. - INPUT PARAMETERS: - A - array, must be large enough for plan to work - ABase - base offset in array A, this value points to start of - subarray whose length is equal to length of the plan - AOffset - offset with respect to ABase, 0<=AOffset=1, "FTApplyComplexRefFFT: OperandsCnt<1"); // - // Process operands + // init // - for(opidx=0; opidx<=operandscnt-1; opidx++) + p5 = 0.5; + p66 = 0.66; + state.xtrapf = 4.0; + zero = 0; + if( (double)(stpmax)==(double)(0) ) { - - // - // fill QA - // - kq = 1; - p0 = abase+aoffset+opidx*n*2; - p1 = aoffset+opidx*n*2; - rx = a[p0+0]; - ry = a[p0+1]; - x0 = rx; - y0 = ry; - for(q=0; q<=n-2; q++) - { - ax = a[p0+2*kq+0]; - ay = a[p0+2*kq+1]; - buf[p1+0] = ax; - buf[p1+1] = ay; - rx = rx+ax; - ry = ry+ay; - kq = kq*rq%n; - p1 = p1+2; - } - p0 = abase+aoffset+opidx*n*2; - p1 = aoffset+opidx*n*2; - for(q=0; q<=n-2; q++) - { - a[p0] = buf[p1]; - a[p0+1] = buf[p1+1]; - p0 = p0+2; - p1 = p1+2; - } - - // - // Convolution - // - ftapplysubplan(plan, subplan, a, abase, aoffset+opidx*n*2, buf, 1, _params); - p0 = abase+aoffset+opidx*n*2; - p1 = precoffs; - for(i=0; i<=n-2; i++) + stpmax = defstpmax; + } + if( (double)(stp)<(double)(stpmin) ) + { + stp = stpmin; + } + if( (double)(stp)>(double)(stpmax) ) + { + stp = stpmax; + } + + // + // Main cycle + // + while( true ) + { + if( stage==0 ) { - ax = a[p0+0]; - ay = a[p0+1]; - bx = plan.precr[p1+0]; - by = plan.precr[p1+1]; - a[p0+0] = ax*bx-ay*by; - a[p0+1] = -(ax*by+ay*bx); - p0 = p0+2; - p1 = p1+2; + + // + // NEXT + // + stage = 2; + continue; } - ftapplysubplan(plan, subplan, a, abase, aoffset+opidx*n*2, buf, 1, _params); - p0 = abase+aoffset+opidx*n*2; - for(i=0; i<=n-2; i++) + if( stage==2 ) { - a[p0+0] = a[p0+0]/(n-1); - a[p0+1] = -(a[p0+1]/(n-1)); - p0 = p0+2; + state.infoc = 1; + info = 0; + + // + // CHECK THE INPUT PARAMETERS FOR ERRORS. + // + if( (double)(stpmax)<(double)(stpmin) && (double)(stpmax)>(double)(0) ) + { + info = 5; + stp = stpmax; + stage = 0; + return; + } + if( ((((((n<=0 || (double)(stp)<=(double)(0)) || (double)(ftol)<(double)(0)) || (double)(gtol)<(double)(zero)) || (double)(xtol)<(double)(zero)) || (double)(stpmin)<(double)(zero)) || (double)(stpmax)<(double)(stpmin)) || maxfev<=0 ) + { + stage = 0; + return; + } + + // + // COMPUTE THE INITIAL GRADIENT IN THE SEARCH DIRECTION + // AND CHECK THAT S IS A DESCENT DIRECTION. + // + v = 0.0; + for(i_=0; i_<=n-1;i_++) + { + v += g[i_]*s[i_]; + } + state.dginit = v; + if( (double)(state.dginit)>=(double)(0) ) + { + stage = 0; + stp = 0; + return; + } + + // + // INITIALIZE LOCAL VARIABLES. + // + state.brackt = false; + state.stage1 = true; + nfev = 0; + state.finit = f; + state.dgtest = ftol*state.dginit; + state.width = stpmax-stpmin; + state.width1 = state.width/p5; + for(i_=0; i_<=n-1;i_++) + { + wa[i_] = x[i_]; + } + + // + // THE VARIABLES STX, FX, DGX CONTAIN THE VALUES OF THE STEP, + // FUNCTION, AND DIRECTIONAL DERIVATIVE AT THE BEST STEP. + // THE VARIABLES STY, FY, DGY CONTAIN THE VALUE OF THE STEP, + // FUNCTION, AND DERIVATIVE AT THE OTHER ENDPOINT OF + // THE INTERVAL OF UNCERTAINTY. + // THE VARIABLES STP, F, DG CONTAIN THE VALUES OF THE STEP, + // FUNCTION, AND DERIVATIVE AT THE CURRENT STEP. + // + state.stx = 0; + state.fx = state.finit; + state.dgx = state.dginit; + state.sty = 0; + state.fy = state.finit; + state.dgy = state.dginit; + + // + // NEXT + // + stage = 3; + continue; } - - // - // Result - // - buf[aoffset+opidx*n*2+0] = rx; - buf[aoffset+opidx*n*2+1] = ry; - kiq = 1; - p0 = aoffset+opidx*n*2; - p1 = abase+aoffset+opidx*n*2; - for(q=0; q<=n-2; q++) + if( stage==3 ) { - buf[p0+2*kiq+0] = x0+a[p1+0]; - buf[p0+2*kiq+1] = y0+a[p1+1]; - kiq = kiq*riq%n; - p1 = p1+2; + + // + // START OF ITERATION. + // + // SET THE MINIMUM AND MAXIMUM STEPS TO CORRESPOND + // TO THE PRESENT INTERVAL OF UNCERTAINTY. + // + if( state.brackt ) + { + if( (double)(state.stx)<(double)(state.sty) ) + { + state.stmin = state.stx; + state.stmax = state.sty; + } + else + { + state.stmin = state.sty; + state.stmax = state.stx; + } + } + else + { + state.stmin = state.stx; + state.stmax = stp+state.xtrapf*(stp-state.stx); + } + + // + // FORCE THE STEP TO BE WITHIN THE BOUNDS STPMAX AND STPMIN. + // + if( (double)(stp)>(double)(stpmax) ) + { + stp = stpmax; + } + if( (double)(stp)<(double)(stpmin) ) + { + stp = stpmin; + } + + // + // IF AN UNUSUAL TERMINATION IS TO OCCUR THEN LET + // STP BE THE LOWEST POINT OBTAINED SO FAR. + // + if( (((state.brackt && ((double)(stp)<=(double)(state.stmin) || (double)(stp)>=(double)(state.stmax))) || nfev>=maxfev-1) || state.infoc==0) || (state.brackt && (double)(state.stmax-state.stmin)<=(double)(xtol*state.stmax)) ) + { + stp = state.stx; + } + + // + // EVALUATE THE FUNCTION AND GRADIENT AT STP + // AND COMPUTE THE DIRECTIONAL DERIVATIVE. + // + for(i_=0; i_<=n-1;i_++) + { + x[i_] = wa[i_]; + } + for(i_=0; i_<=n-1;i_++) + { + x[i_] = x[i_] + stp*s[i_]; + } + + // + // NEXT + // + stage = 4; + return; } - p0 = abase+aoffset+opidx*n*2; - p1 = aoffset+opidx*n*2; - for(q=0; q<=n-1; q++) + if( stage==4 ) { - a[p0] = buf[p1]; - a[p0+1] = buf[p1+1]; - p0 = p0+2; - p1 = p1+2; + info = 0; + nfev = nfev+1; + v = 0.0; + for(i_=0; i_<=n-1;i_++) + { + v += g[i_]*s[i_]; + } + state.dg = v; + state.ftest1 = state.finit+stp*state.dgtest; + + // + // TEST FOR CONVERGENCE. + // + if( (state.brackt && ((double)(stp)<=(double)(state.stmin) || (double)(stp)>=(double)(state.stmax))) || state.infoc==0 ) + { + info = 6; + } + if( (((double)(stp)==(double)(stpmax) && (double)(f)<(double)(state.finit)) && (double)(f)<=(double)(state.ftest1)) && (double)(state.dg)<=(double)(state.dgtest) ) + { + info = 5; + } + if( (double)(stp)==(double)(stpmin) && (((double)(f)>=(double)(state.finit) || (double)(f)>(double)(state.ftest1)) || (double)(state.dg)>=(double)(state.dgtest)) ) + { + info = 4; + } + if( nfev>=maxfev ) + { + info = 3; + } + if( state.brackt && (double)(state.stmax-state.stmin)<=(double)(xtol*state.stmax) ) + { + info = 2; + } + if( ((double)(f)<(double)(state.finit) && (double)(f)<=(double)(state.ftest1)) && (double)(Math.Abs(state.dg))<=(double)(-(gtol*state.dginit)) ) + { + info = 1; + } + + // + // CHECK FOR TERMINATION. + // + if( info!=0 ) + { + + // + // Check guarantees provided by the function for INFO=1 or INFO=5 + // + if( info==1 || info==5 ) + { + v = 0.0; + for(i=0; i<=n-1; i++) + { + v = v+(wa[i]-x[i])*(wa[i]-x[i]); + } + if( (double)(f)>=(double)(state.finit) || (double)(v)==(double)(0.0) ) + { + info = 6; + } + } + stage = 0; + return; + } + + // + // IN THE FIRST STAGE WE SEEK A STEP FOR WHICH THE MODIFIED + // FUNCTION HAS A NONPOSITIVE VALUE AND NONNEGATIVE DERIVATIVE. + // + if( (state.stage1 && (double)(f)<=(double)(state.ftest1)) && (double)(state.dg)>=(double)(Math.Min(ftol, gtol)*state.dginit) ) + { + state.stage1 = false; + } + + // + // A MODIFIED FUNCTION IS USED TO PREDICT THE STEP ONLY IF + // WE HAVE NOT OBTAINED A STEP FOR WHICH THE MODIFIED + // FUNCTION HAS A NONPOSITIVE FUNCTION VALUE AND NONNEGATIVE + // DERIVATIVE, AND IF A LOWER FUNCTION VALUE HAS BEEN + // OBTAINED BUT THE DECREASE IS NOT SUFFICIENT. + // + if( (state.stage1 && (double)(f)<=(double)(state.fx)) && (double)(f)>(double)(state.ftest1) ) + { + + // + // DEFINE THE MODIFIED FUNCTION AND DERIVATIVE VALUES. + // + state.fm = f-stp*state.dgtest; + state.fxm = state.fx-state.stx*state.dgtest; + state.fym = state.fy-state.sty*state.dgtest; + state.dgm = state.dg-state.dgtest; + state.dgxm = state.dgx-state.dgtest; + state.dgym = state.dgy-state.dgtest; + + // + // CALL CSTEP TO UPDATE THE INTERVAL OF UNCERTAINTY + // AND TO COMPUTE THE NEW STEP. + // + mcstep(ref state.stx, ref state.fxm, ref state.dgxm, ref state.sty, ref state.fym, ref state.dgym, ref stp, state.fm, state.dgm, ref state.brackt, state.stmin, state.stmax, ref state.infoc, _params); + + // + // RESET THE FUNCTION AND GRADIENT VALUES FOR F. + // + state.fx = state.fxm+state.stx*state.dgtest; + state.fy = state.fym+state.sty*state.dgtest; + state.dgx = state.dgxm+state.dgtest; + state.dgy = state.dgym+state.dgtest; + } + else + { + + // + // CALL MCSTEP TO UPDATE THE INTERVAL OF UNCERTAINTY + // AND TO COMPUTE THE NEW STEP. + // + mcstep(ref state.stx, ref state.fx, ref state.dgx, ref state.sty, ref state.fy, ref state.dgy, ref stp, f, state.dg, ref state.brackt, state.stmin, state.stmax, ref state.infoc, _params); + } + + // + // FORCE A SUFFICIENT DECREASE IN THE SIZE OF THE + // INTERVAL OF UNCERTAINTY. + // + if( state.brackt ) + { + if( (double)(Math.Abs(state.sty-state.stx))>=(double)(p66*state.width1) ) + { + stp = state.stx+p5*(state.sty-state.stx); + } + state.width1 = state.width; + state.width = Math.Abs(state.sty-state.stx); + } + + // + // NEXT. + // + stage = 3; + continue; } } } /************************************************************************* - Factorizes task size N into product of two smaller sizes N1 and N2 + These functions perform Armijo line search using at most FMAX function + evaluations. It doesn't enforce some kind of " sufficient decrease" + criterion - it just tries different Armijo steps and returns optimum found + so far. + + Optimization is done using F-rcomm interface: + * ArmijoCreate initializes State structure + (reusing previously allocated buffers) + * ArmijoIteration is subsequently called + * ArmijoResults returns results INPUT PARAMETERS: - N - task size, N>0 - IsRoot - whether taks is root task (first one in a sequence) - - OUTPUT PARAMETERS: - N1, N2 - such numbers that: - * for prime N: N1=N2=0 - * for composite N<=MaxRadix: N1=N2=0 - * for composite N>MaxRadix: 1<=N1<=N2, N1*N2=N + N - problem size + X - array[N], starting point + F - F(X+S*STP) + S - step direction, S>0 + STP - step length + STPMAX - maximum value for STP or zero (if no limit is imposed) + FMAX - maximum number of function evaluations + State - optimization state -- ALGLIB -- - Copyright 08.04.2013 by Bochkanov Sergey + Copyright 05.10.2010 by Bochkanov Sergey *************************************************************************/ - private static void ftfactorize(int n, - bool isroot, - ref int n1, - ref int n2, + public static void armijocreate(int n, + double[] x, + double f, + double[] s, + double stp, + double stpmax, + int fmax, + armijostate state, alglib.xparams _params) { - int j = 0; - int k = 0; + int i_ = 0; - n1 = 0; - n2 = 0; + if( alglib.ap.len(state.x)0, "FTFactorize: N<=0"); - n1 = 0; - n2 = 0; + + /************************************************************************* + This is rcomm-based search function + + -- ALGLIB -- + Copyright 05.10.2010 by Bochkanov Sergey + *************************************************************************/ + public static bool armijoiteration(armijostate state, + alglib.xparams _params) + { + bool result = new bool(); + double v = 0; + int n = 0; + int i_ = 0; + + + // + // Reverse communication preparations + // + // This code initializes locals by: + // * random values determined during code + // generation - on first subroutine call + // * values from previous call - on subsequent calls + // + if( state.rstate.stage>=0 ) + { + n = state.rstate.ia[0]; + v = state.rstate.ra[0]; + } + else + { + n = 359; + v = -58.0; + } + if( state.rstate.stage==0 ) + { + goto lbl_0; + } + if( state.rstate.stage==1 ) + { + goto lbl_1; + } + if( state.rstate.stage==2 ) + { + goto lbl_2; + } + if( state.rstate.stage==3 ) + { + goto lbl_3; + } + + // + // Routine body + // + if( ((double)(state.stplen)<=(double)(0) || (double)(state.stpmax)<(double)(0)) || state.fmax<2 ) + { + state.info = 0; + result = false; + return result; + } + if( (double)(state.stplen)<=(double)(stpmin) ) + { + state.info = 4; + result = false; + return result; + } + n = state.n; + state.nfev = 0; + + // + // We always need F + // + state.needf = true; + + // + // Bound StpLen + // + if( (double)(state.stplen)>(double)(state.stpmax) && (double)(state.stpmax)!=(double)(0) ) + { + state.stplen = state.stpmax; + } + + // + // Increase length + // + v = state.stplen*armijofactor; + if( (double)(v)>(double)(state.stpmax) && (double)(state.stpmax)!=(double)(0) ) + { + v = state.stpmax; + } + for(i_=0; i_<=n-1;i_++) + { + state.x[i_] = state.xbase[i_]; + } + for(i_=0; i_<=n-1;i_++) + { + state.x[i_] = state.x[i_] + v*state.s[i_]; + } + state.rstate.stage = 0; + if( state.rstate.rcomm2_handler!=null && state.rstate.requesttype!=0 && state.rstate.requesttype<=ap._ALGLIB_MAX_RCOMMV2_REQUEST ) + state.rstate.rcomm2_handler(state.rstate, state.rstate.handler_p0, state.rstate.handler_p1, state.rstate.handler_p2, state.rstate.handler_p3, _params); + else + goto lbl_rcomm; + lbl_0: + state.nfev = state.nfev+1; + if( (double)(state.f)>=(double)(state.fcur) ) + { + goto lbl_4; + } + state.stplen = v; + state.fcur = state.f; + lbl_6: + if( false ) + { + goto lbl_7; + } + + // + // test stopping conditions + // + if( state.nfev>=state.fmax ) + { + state.info = 3; + result = false; + return result; + } + if( (double)(state.stplen)>=(double)(state.stpmax) ) + { + state.info = 5; + result = false; + return result; + } + + // + // evaluate F + // + v = state.stplen*armijofactor; + if( (double)(v)>(double)(state.stpmax) && (double)(state.stpmax)!=(double)(0) ) + { + v = state.stpmax; + } + for(i_=0; i_<=n-1;i_++) + { + state.x[i_] = state.xbase[i_]; + } + for(i_=0; i_<=n-1;i_++) + { + state.x[i_] = state.x[i_] + v*state.s[i_]; + } + state.rstate.stage = 1; + if( state.rstate.rcomm2_handler!=null && state.rstate.requesttype!=0 && state.rstate.requesttype<=ap._ALGLIB_MAX_RCOMMV2_REQUEST ) + state.rstate.rcomm2_handler(state.rstate, state.rstate.handler_p0, state.rstate.handler_p1, state.rstate.handler_p2, state.rstate.handler_p3, _params); + else + goto lbl_rcomm; + lbl_1: + state.nfev = state.nfev+1; + + // + // make decision + // + if( (double)(state.f)<(double)(state.fcur) ) + { + state.stplen = v; + state.fcur = state.f; + } + else + { + state.info = 1; + result = false; + return result; + } + goto lbl_6; + lbl_7: + lbl_4: // - // Small N + // Decrease length // - if( n<=maxradix ) + v = state.stplen/armijofactor; + for(i_=0; i_<=n-1;i_++) { - return; + state.x[i_] = state.xbase[i_]; + } + for(i_=0; i_<=n-1;i_++) + { + state.x[i_] = state.x[i_] + v*state.s[i_]; + } + state.rstate.stage = 2; + if( state.rstate.rcomm2_handler!=null && state.rstate.requesttype!=0 && state.rstate.requesttype<=ap._ALGLIB_MAX_RCOMMV2_REQUEST ) + state.rstate.rcomm2_handler(state.rstate, state.rstate.handler_p0, state.rstate.handler_p1, state.rstate.handler_p2, state.rstate.handler_p3, _params); + else + goto lbl_rcomm; + lbl_2: + state.nfev = state.nfev+1; + if( (double)(state.f)>=(double)(state.fcur) ) + { + goto lbl_8; + } + state.stplen = state.stplen/armijofactor; + state.fcur = state.f; + lbl_10: + if( false ) + { + goto lbl_11; } // - // Large N, recursive split + // test stopping conditions // - if( n>recursivethreshold ) + if( state.nfev>=state.fmax ) { - k = (int)Math.Ceiling(Math.Sqrt(n))+1; - alglib.ap.assert(k*k>=n, "FTFactorize: internal error during recursive factorization"); - for(j=k; j>=2; j--) - { - if( n%j==0 ) - { - n1 = Math.Min(n/j, j); - n2 = Math.Max(n/j, j); - return; - } - } + state.info = 3; + result = false; + return result; + } + if( (double)(state.stplen)<=(double)(stpmin) ) + { + state.info = 4; + result = false; + return result; } // - // N>MaxRadix, try to find good codelet + // evaluate F // - for(j=maxradix; j>=2; j--) + v = state.stplen/armijofactor; + for(i_=0; i_<=n-1;i_++) { - if( n%j==0 ) - { - n1 = j; - n2 = n/j; - break; - } + state.x[i_] = state.xbase[i_]; + } + for(i_=0; i_<=n-1;i_++) + { + state.x[i_] = state.x[i_] + v*state.s[i_]; } + state.rstate.stage = 3; + if( state.rstate.rcomm2_handler!=null && state.rstate.requesttype!=0 && state.rstate.requesttype<=ap._ALGLIB_MAX_RCOMMV2_REQUEST ) + state.rstate.rcomm2_handler(state.rstate, state.rstate.handler_p0, state.rstate.handler_p1, state.rstate.handler_p2, state.rstate.handler_p3, _params); + else + goto lbl_rcomm; + lbl_3: + state.nfev = state.nfev+1; // - // In case no good codelet was found, - // try to factorize N into product of ANY primes. + // make decision // - if( n1*n2!=n ) + if( (double)(state.f)<(double)(state.fcur) ) { - for(j=2; j<=n-1; j++) - { - if( n%j==0 ) - { - n1 = j; - n2 = n/j; - break; - } - if( j*j>n ) - { - break; - } - } + state.stplen = state.stplen/armijofactor; + state.fcur = state.f; + } + else + { + state.info = 1; + result = false; + return result; } + goto lbl_10; + lbl_11: + lbl_8: // - // normalize + // Nothing to be done // - if( n1>n2 ) - { - j = n1; - n1 = n2; - n2 = j; - } + state.info = 1; + result = false; + return result; + + // + // Saving state + // + lbl_rcomm: + result = true; + state.rstate.ia[0] = n; + state.rstate.ra[0] = v; + return result; } /************************************************************************* - Returns optimistic estimate of the FFT cost, in UNITs (1 UNIT = 100 KFLOPs) - - INPUT PARAMETERS: - N - task size, N>0 - - RESULU: - cost in UNITs, rounded down to nearest integer + Results of Armijo search - NOTE: If FFT cost is less than 1 UNIT, it will return 0 as result. + OUTPUT PARAMETERS: + INFO - on output it is set to one of the return codes: + * 0 improper input params + * 1 optimum step is found with at most FMAX evaluations + * 3 FMAX evaluations were used, + X contains optimum found so far + * 4 step is at lower bound STPMIN + * 5 step is at upper bound + STP - step length (in case of failure it is still returned) + F - function value (in case of failure it is still returned) -- ALGLIB -- - Copyright 08.04.2013 by Bochkanov Sergey + Copyright 05.10.2010 by Bochkanov Sergey *************************************************************************/ - private static int ftoptimisticestimate(int n, + public static void armijoresults(armijostate state, + ref int info, + ref double stp, + ref double f, alglib.xparams _params) { - int result = 0; - - alglib.ap.assert(n>0, "FTOptimisticEstimate: N<=0"); - result = (int)Math.Floor(1.0E-5*5*n*Math.Log(n)/Math.Log(2)); - return result; + info = state.info; + stp = state.stplen; + f = state.fcur; } - /************************************************************************* - Twiddle factors calculation - - -- ALGLIB -- - Copyright 01.05.2009 by Bochkanov Sergey - *************************************************************************/ - private static void ffttwcalc(double[] a, - int aoffset, - int n1, - int n2, + private static void mcstep(ref double stx, + ref double fx, + ref double dx, + ref double sty, + ref double fy, + ref double dy, + ref double stp, + double fp, + double dp, + ref bool brackt, + double stmin, + double stmax, + ref int info, alglib.xparams _params) { - int i = 0; - int j2 = 0; - int n = 0; - int halfn1 = 0; - int offs = 0; - double x = 0; - double y = 0; - double twxm1 = 0; - double twy = 0; - double twbasexm1 = 0; - double twbasey = 0; - double twrowxm1 = 0; - double twrowy = 0; - double tmpx = 0; - double tmpy = 0; - double v = 0; - int updatetw2 = 0; + bool bound = new bool(); + double gamma = 0; + double p = 0; + double q = 0; + double r = 0; + double s = 0; + double sgnd = 0; + double stpc = 0; + double stpf = 0; + double stpq = 0; + double theta = 0; + info = 0; // - // Multiplication by twiddle factors for complex Cooley-Tukey FFT - // with N factorized as N1*N2. - // - // Naive solution to this problem is given below: - // - // > for K:=1 to N2-1 do - // > for J:=1 to N1-1 do - // > begin - // > Idx:=K*N1+J; - // > X:=A[AOffset+2*Idx+0]; - // > Y:=A[AOffset+2*Idx+1]; - // > TwX:=Cos(-2*Pi()*K*J/(N1*N2)); - // > TwY:=Sin(-2*Pi()*K*J/(N1*N2)); - // > A[AOffset+2*Idx+0]:=X*TwX-Y*TwY; - // > A[AOffset+2*Idx+1]:=X*TwY+Y*TwX; - // > end; - // - // However, there are exist more efficient solutions. - // - // Each pass of the inner cycle corresponds to multiplication of one - // entry of A by W[k,j]=exp(-I*2*pi*k*j/N). This factor can be rewritten - // as exp(-I*2*pi*k/N)^j. So we can replace costly exponentiation by - // repeated multiplication: W[k,j+1]=W[k,j]*exp(-I*2*pi*k/N), with - // second factor being computed once in the beginning of the iteration. - // - // Also, exp(-I*2*pi*k/N) can be represented as exp(-I*2*pi/N)^k, i.e. - // we have W[K+1,1]=W[K,1]*W[1,1]. + // CHECK THE INPUT PARAMETERS FOR ERRORS. // - // In our loop we use following variables: - // * [TwBaseXM1,TwBaseY] = [cos(2*pi/N)-1, sin(2*pi/N)] - // * [TwRowXM1, TwRowY] = [cos(2*pi*I/N)-1, sin(2*pi*I/N)] - // * [TwXM1, TwY] = [cos(2*pi*I*J/N)-1, sin(2*pi*I*J/N)] + if( ((brackt && ((double)(stp)<=(double)(Math.Min(stx, sty)) || (double)(stp)>=(double)(Math.Max(stx, sty)))) || (double)(dx*(stp-stx))>=(double)(0)) || (double)(stmax)<(double)(stmin) ) + { + return; + } + // - // Meaning of the variables: - // * [TwXM1,TwY] is current twiddle factor W[I,J] - // * [TwRowXM1, TwRowY] is W[I,1] - // * [TwBaseXM1,TwBaseY] is W[1,1] + // DETERMINE IF THE DERIVATIVES HAVE OPPOSITE SIGN. // - // During inner loop we multiply current twiddle factor by W[I,1], - // during outer loop we update W[I,1]. + sgnd = dp*(dx/Math.Abs(dx)); + // + // FIRST CASE. A HIGHER FUNCTION VALUE. + // THE MINIMUM IS BRACKETED. IF THE CUBIC STEP IS CLOSER + // TO STX THAN THE QUADRATIC STEP, THE CUBIC STEP IS TAKEN, + // ELSE THE AVERAGE OF THE CUBIC AND QUADRATIC STEPS IS TAKEN. // - alglib.ap.assert(updatetw>=2, "FFTTwCalc: internal error - UpdateTw<2"); - updatetw2 = updatetw/2; - halfn1 = n1/2; - n = n1*n2; - v = -(2*Math.PI/n); - twbasexm1 = -(2*math.sqr(Math.Sin(0.5*v))); - twbasey = Math.Sin(v); - twrowxm1 = 0; - twrowy = 0; - offs = aoffset; - for(i=0; i<=n2-1; i++) + if( (double)(fp)>(double)(fx) ) { - - // - // Initialize twiddle factor for current row - // - twxm1 = 0; - twy = 0; - - // - // N1-point block is separated into 2-point chunks and residual 1-point chunk - // (in case N1 is odd). Unrolled loop is several times faster. - // - for(j2=0; j2<=halfn1-1; j2++) + info = 1; + bound = true; + theta = 3*(fx-fp)/(stp-stx)+dx+dp; + s = Math.Max(Math.Abs(theta), Math.Max(Math.Abs(dx), Math.Abs(dp))); + gamma = s*Math.Sqrt(math.sqr(theta/s)-dx/s*(dp/s)); + if( (double)(stp)<(double)(stx) ) + { + gamma = -gamma; + } + p = gamma-dx+theta; + q = gamma-dx+gamma+dp; + r = p/q; + stpc = stx+r*(stp-stx); + stpq = stx+dx/((fx-fp)/(stp-stx)+dx)/2*(stp-stx); + if( (double)(Math.Abs(stpc-stx))<(double)(Math.Abs(stpq-stx)) ) + { + stpf = stpc; + } + else + { + stpf = stpc+(stpq-stpc)/2; + } + brackt = true; + } + else + { + if( (double)(sgnd)<(double)(0) ) { // - // Processing: - // * process first element in a chunk. - // * update twiddle factor (unconditional update) - // * process second element - // * conditional update of the twiddle factor + // SECOND CASE. A LOWER FUNCTION VALUE AND DERIVATIVES OF + // OPPOSITE SIGN. THE MINIMUM IS BRACKETED. IF THE CUBIC + // STEP IS CLOSER TO STX THAN THE QUADRATIC (SECANT) STEP, + // THE CUBIC STEP IS TAKEN, ELSE THE QUADRATIC STEP IS TAKEN. // - x = a[offs+0]; - y = a[offs+1]; - tmpx = x*(1+twxm1)-y*twy; - tmpy = x*twy+y*(1+twxm1); - a[offs+0] = tmpx; - a[offs+1] = tmpy; - tmpx = (1+twxm1)*twrowxm1-twy*twrowy; - twy = twy+(1+twxm1)*twrowy+twy*twrowxm1; - twxm1 = twxm1+tmpx; - x = a[offs+2]; - y = a[offs+3]; - tmpx = x*(1+twxm1)-y*twy; - tmpy = x*twy+y*(1+twxm1); - a[offs+2] = tmpx; - a[offs+3] = tmpy; - offs = offs+4; - if( (j2+1)%updatetw2==0 && j2(double)(stx) ) + { + gamma = -gamma; + } + p = gamma-dp+theta; + q = gamma-dp+gamma+dx; + r = p/q; + stpc = stp+r*(stx-stp); + stpq = stp+dp/(dp-dx)*(stx-stp); + if( (double)(Math.Abs(stpc-stp))>(double)(Math.Abs(stpq-stp)) ) + { + stpf = stpc; + } + else + { + stpf = stpq; + } + brackt = true; + } + else + { + if( (double)(Math.Abs(dp))<(double)(Math.Abs(dx)) ) { // - // Recalculate twiddle factor + // THIRD CASE. A LOWER FUNCTION VALUE, DERIVATIVES OF THE + // SAME SIGN, AND THE MAGNITUDE OF THE DERIVATIVE DECREASES. + // THE CUBIC STEP IS ONLY USED IF THE CUBIC TENDS TO INFINITY + // IN THE DIRECTION OF THE STEP OR IF THE MINIMUM OF THE CUBIC + // IS BEYOND STP. OTHERWISE THE CUBIC STEP IS DEFINED TO BE + // EITHER STPMIN OR STPMAX. THE QUADRATIC (SECANT) STEP IS ALSO + // COMPUTED AND IF THE MINIMUM IS BRACKETED THEN THE THE STEP + // CLOSEST TO STX IS TAKEN, ELSE THE STEP FARTHEST AWAY IS TAKEN. // - v = -(2*Math.PI*i*2*(j2+1)/n); - twxm1 = Math.Sin(0.5*v); - twxm1 = -(2*twxm1*twxm1); - twy = Math.Sin(v); + info = 3; + bound = true; + theta = 3*(fx-fp)/(stp-stx)+dx+dp; + s = Math.Max(Math.Abs(theta), Math.Max(Math.Abs(dx), Math.Abs(dp))); + + // + // THE CASE GAMMA = 0 ONLY ARISES IF THE CUBIC DOES NOT TEND + // TO INFINITY IN THE DIRECTION OF THE STEP. + // + gamma = s*Math.Sqrt(Math.Max(0, math.sqr(theta/s)-dx/s*(dp/s))); + if( (double)(stp)>(double)(stx) ) + { + gamma = -gamma; + } + p = gamma-dp+theta; + q = gamma+(dx-dp)+gamma; + r = p/q; + if( (double)(r)<(double)(0) && (double)(gamma)!=(double)(0) ) + { + stpc = stp+r*(stx-stp); + } + else + { + if( (double)(stp)>(double)(stx) ) + { + stpc = stmax; + } + else + { + stpc = stmin; + } + } + stpq = stp+dp/(dp-dx)*(stx-stp); + if( brackt ) + { + if( (double)(Math.Abs(stp-stpc))<(double)(Math.Abs(stp-stpq)) ) + { + stpf = stpc; + } + else + { + stpf = stpq; + } + } + else + { + if( (double)(Math.Abs(stp-stpc))>(double)(Math.Abs(stp-stpq)) ) + { + stpf = stpc; + } + else + { + stpf = stpq; + } + } } else { // - // Update twiddle factor + // FOURTH CASE. A LOWER FUNCTION VALUE, DERIVATIVES OF THE + // SAME SIGN, AND THE MAGNITUDE OF THE DERIVATIVE DOES + // NOT DECREASE. IF THE MINIMUM IS NOT BRACKETED, THE STEP + // IS EITHER STPMIN OR STPMAX, ELSE THE CUBIC STEP IS TAKEN. // - tmpx = (1+twxm1)*twrowxm1-twy*twrowy; - twy = twy+(1+twxm1)*twrowy+twy*twrowxm1; - twxm1 = twxm1+tmpx; + info = 4; + bound = false; + if( brackt ) + { + theta = 3*(fp-fy)/(sty-stp)+dy+dp; + s = Math.Max(Math.Abs(theta), Math.Max(Math.Abs(dy), Math.Abs(dp))); + gamma = s*Math.Sqrt(math.sqr(theta/s)-dy/s*(dp/s)); + if( (double)(stp)>(double)(sty) ) + { + gamma = -gamma; + } + p = gamma-dp+theta; + q = gamma-dp+gamma+dy; + r = p/q; + stpc = stp+r*(sty-stp); + stpf = stpc; + } + else + { + if( (double)(stp)>(double)(stx) ) + { + stpf = stmax; + } + else + { + stpf = stmin; + } + } } } - if( n1%2==1 ) + } + + // + // UPDATE THE INTERVAL OF UNCERTAINTY. THIS UPDATE DOES NOT + // DEPEND ON THE NEW STEP OR THE CASE ANALYSIS ABOVE. + // + if( (double)(fp)>(double)(fx) ) + { + sty = stp; + fy = fp; + dy = dp; + } + else + { + if( (double)(sgnd)<(double)(0.0) ) { - - // - // Handle residual chunk - // - x = a[offs+0]; - y = a[offs+1]; - tmpx = x*(1+twxm1)-y*twy; - tmpy = x*twy+y*(1+twxm1); - a[offs+0] = tmpx; - a[offs+1] = tmpy; - offs = offs+2; + sty = stx; + fy = fx; + dy = dx; + } + stx = stp; + fx = fp; + dx = dp; + } + + // + // COMPUTE THE NEW STEP AND SAFEGUARD IT. + // + stpf = Math.Min(stmax, stpf); + stpf = Math.Max(stmin, stpf); + stp = stpf; + if( brackt && bound ) + { + if( (double)(sty)>(double)(stx) ) + { + stp = Math.Min(stx+0.66*(sty-stx), stp); } - - // - // update TwRow: TwRow(new) = TwRow(old)*TwBase - // - if( im ) + alglib.ap.assert(n<536870912, "XDot: N is too large!"); + + // + // Prepare + // + ln2 = Math.Log(2); + rerr = mx*math.machineepsilon; + + // + // 1. find S such that 0.5<=S*MX<1 + // 2. multiply W by S, so task is normalized in some sense + // 3. S:=1/S so we can obtain original vector multiplying by S + // + k = (int)Math.Round(Math.Log(mx)/ln2); + s = xfastpow(2, -k, _params); + if( !math.isfinite(s) ) { // - // New partition: - // - // "A^T -> B" becomes "(A1 A2)^T -> ( B1 ) - // ( B2 ) + // Overflow or underflow during evaluation of S; fallback low-precision code // - n1 = n/2; - if( n-n1>=8 && n1%8!=0 ) + r = 0; + rerr = mx*math.machineepsilon; + for(i=0; i<=n-1; i++) { - n1 = n1+(8-n1%8); + r = r+w[i]; } - alglib.ap.assert(n-n1>0); - ffticltrec(a, astart, astride, b, bstart, bstride, m, n1, _params); - ffticltrec(a, astart+2*n1, astride, b, bstart+2*n1*bstride, bstride, m, n-n1, _params); + return; } - else + while( (double)(s*mx)>=(double)(1) ) { - - // - // New partition: - // - // "A^T -> B" becomes "( A1 )^T -> ( B1 B2 ) - // ( A2 ) - // - m1 = m/2; - if( m-m1>=8 && m1%8!=0 ) + s = 0.5*s; + } + while( (double)(s*mx)<(double)(0.5) ) + { + s = 2*s; + } + for(i_=0; i_<=n-1;i_++) + { + w[i_] = s*w[i_]; + } + s = 1/s; + + // + // find Chunk=2^M such that N*Chunk<2^29 + // + // we have chosen upper limit (2^29) with enough space left + // to tolerate possible problems with rounding and N's close + // to the limit, so we don't want to be very strict here. + // + k = (int)(Math.Log((double)536870912/(double)n)/ln2); + chunk = xfastpow(2, k, _params); + if( (double)(chunk)<(double)(2) ) + { + chunk = 2; + } + invchunk = 1/chunk; + + // + // calculate result + // + r = 0; + for(i_=0; i_<=n-1;i_++) + { + w[i_] = chunk*w[i_]; + } + while( true ) + { + s = s*invchunk; + allzeros = true; + ks = 0; + for(i=0; i<=n-1; i++) { - m1 = m1+(8-m1%8); + v = w[i]; + k = (int)(v); + if( (double)(v)!=(double)(k) ) + { + allzeros = false; + } + w[i] = chunk*(v-k); + ks = ks+k; + } + r = r+s*ks; + if( allzeros || (double)(s*n+mx)==(double)(mx) ) + { + break; } - alglib.ap.assert(m-m1>0); - ffticltrec(a, astart, astride, b, bstart, bstride, m1, n, _params); - ffticltrec(a, astart+2*m1*astride, astride, b, bstart+2*m1, bstride, m-m1, n, _params); } + + // + // correct error + // + rerr = Math.Max(rerr, Math.Abs(r)*math.machineepsilon); } /************************************************************************* - recurrent subroutine for FFTFindSmoothRec + Fast Pow -- ALGLIB -- - Copyright 01.05.2009 by Bochkanov Sergey + Copyright 24.08.2009 by Bochkanov Sergey *************************************************************************/ - private static void ftbasefindsmoothrec(int n, - int seed, - int leastfactor, - ref int best, + private static double xfastpow(double r, + int n, alglib.xparams _params) { - alglib.ap.assert(ftbasemaxsmoothfactor<=5, "FTBaseFindSmoothRec: internal error!"); - if( seed>=n ) - { - best = Math.Min(best, seed); - return; - } - if( leastfactor<=2 ) + double result = 0; + + result = 0; + if( n>0 ) { - ftbasefindsmoothrec(n, seed*2, 2, ref best, _params); + if( n%2==0 ) + { + result = math.sqr(xfastpow(r, n/2, _params)); + } + else + { + result = r*xfastpow(r, n-1, _params); + } + return result; } - if( leastfactor<=3 ) + if( n==0 ) { - ftbasefindsmoothrec(n, seed*3, 3, ref best, _params); + result = 1; } - if( leastfactor<=5 ) + if( n<0 ) { - ftbasefindsmoothrec(n, seed*5, 5, ref best, _params); + result = xfastpow(1/r, -n, _params); } + return result; } } - public class hpccores + public partial class hpccores { /************************************************************************* This structure stores temporary buffers used by gradient calculation @@ -22642,7 +23148,99 @@ private static bool hpcfinalizechunkedgradientx(double[] buf, } - public class alglibbasics + public partial class nearunityunit + { + public static double nulog1p(double x, + alglib.xparams _params) + { + double result = 0; + double z = 0; + double lp = 0; + double lq = 0; + + z = 1.0+x; + if( (double)(z)<(double)(0.70710678118654752440) || (double)(z)>(double)(1.41421356237309504880) ) + { + result = Math.Log(z); + return result; + } + z = x*x; + lp = 4.5270000862445199635215E-5; + lp = lp*x+4.9854102823193375972212E-1; + lp = lp*x+6.5787325942061044846969E0; + lp = lp*x+2.9911919328553073277375E1; + lp = lp*x+6.0949667980987787057556E1; + lp = lp*x+5.7112963590585538103336E1; + lp = lp*x+2.0039553499201281259648E1; + lq = 1.0000000000000000000000E0; + lq = lq*x+1.5062909083469192043167E1; + lq = lq*x+8.3047565967967209469434E1; + lq = lq*x+2.2176239823732856465394E2; + lq = lq*x+3.0909872225312059774938E2; + lq = lq*x+2.1642788614495947685003E2; + lq = lq*x+6.0118660497603843919306E1; + z = -(0.5*z)+x*(z*lp/lq); + result = x+z; + return result; + } + + + public static double nuexpm1(double x, + alglib.xparams _params) + { + double result = 0; + double r = 0; + double xx = 0; + double ep = 0; + double eq = 0; + + if( (double)(x)<(double)(-0.5) || (double)(x)>(double)(0.5) ) + { + result = Math.Exp(x)-1.0; + return result; + } + xx = x*x; + ep = 1.2617719307481059087798E-4; + ep = ep*xx+3.0299440770744196129956E-2; + ep = ep*xx+9.9999999999999999991025E-1; + eq = 3.0019850513866445504159E-6; + eq = eq*xx+2.5244834034968410419224E-3; + eq = eq*xx+2.2726554820815502876593E-1; + eq = eq*xx+2.0000000000000000000897E0; + r = x*ep; + r = r/(eq-r); + result = r+r; + return result; + } + + + public static double nucosm1(double x, + alglib.xparams _params) + { + double result = 0; + double xx = 0; + double c = 0; + + if( (double)(x)<(double)(-(0.25*Math.PI)) || (double)(x)>(double)(0.25*Math.PI) ) + { + result = Math.Cos(x)-1; + return result; + } + xx = x*x; + c = 4.7377507964246204691685E-14; + c = c*xx-1.1470284843425359765671E-11; + c = c*xx+2.0876754287081521758361E-9; + c = c*xx-2.7557319214999787979814E-7; + c = c*xx+2.4801587301570552304991E-5; + c = c*xx-1.3888888888888872993737E-3; + c = c*xx+4.1666666666666666609054E-2; + result = -(0.5*xx)+xx*xx*c; + return result; + } + + + } + public partial class alglibbasics { diff --git a/alglib/alglibmisc.cs b/alglib/alglibmisc.cs index 33ba34b48..fa07429d4 100755 --- a/alglib/alglibmisc.cs +++ b/alglib/alglibmisc.cs @@ -1,5 +1,5 @@ /************************************************************************* -ALGLIB 4.07.0 (source code generated 2025-12-29) +ALGLIB 4.08.0 (source code generated 2026-06-08) Copyright (c) Sergey Bochkanov (ALGLIB project). >>> SOURCE LICENSE >>> @@ -2712,7 +2712,7 @@ public static void kdtreequeryresultsdistancesi(kdtree kdt, out double[] r, algl } public partial class alglib { - public class xdebug + public partial class xdebug { /************************************************************************* This is a debug class intended for testing ALGLIB interface generator. @@ -3817,7 +3817,7 @@ public static double xdebugmaskedbiasedproductsum(int m, } - public class hqrnd + public partial class hqrnd { /************************************************************************* Portable high quality random number generator state. @@ -4394,7 +4394,7 @@ public static double hqrndcontinuous(hqrndstate state, } - public class nearestneighbor + public partial class nearestneighbor { /************************************************************************* Buffer object which is used to perform nearest neighbor requests in the diff --git a/alglib/ap.cs b/alglib/ap.cs index 1eb0d5501..f27d3edc2 100755 --- a/alglib/ap.cs +++ b/alglib/ap.cs @@ -1,5 +1,5 @@ /************************************************************************** -ALGLIB 4.07.0 (source code generated 2025-12-29) +ALGLIB 4.08.0 (source code generated 2026-06-08) Copyright (c) Sergey Bochkanov (ALGLIB project). >>> SOURCE LICENSE >>> @@ -27,6 +27,9 @@ A copy of the GNU General Public License is available at #pragma warning disable 1691 #pragma warning disable 8981 #pragma warning disable 649 +#if NET5_0_OR_GREATER +#pragma warning disable CS8981 +#endif using System; public partial class alglib { @@ -95,6 +98,7 @@ stores result to func public delegate void integrator1_func (double x, double xminusa, double bminusx, ref double f, object obj); #endif + public delegate void rcomm2handler(ap.rcommstate state, object p0, object p1, object p2, object p3, xparams flags); /******************************************************************** IronPython compatibility wrappers @@ -528,38 +532,53 @@ public static void trace_disable() ap.trace_disable(); } - /******************************************************************** - reverse communication structure - ********************************************************************/ - public class rcommstate : apobject + public partial class sparse { - public rcommstate() - { - init(); - } - public override void init() - { - stage = -1; - ia = new int[0]; - ba = new bool[0]; - ra = new double[0]; - ca = new alglib.complex[0]; - } - public override apobject make_copy() - { - rcommstate result = new rcommstate(); - result.stage = stage; - result.ia = (int[])ia.Clone(); - result.ba = (bool[])ba.Clone(); - result.ra = (double[])ra.Clone(); - result.ca = (alglib.complex[])ca.Clone(); - return result; - } - public int stage; - public int[] ia; - public bool[] ba; - public double[] ra; - public alglib.complex[] ca; + /******************************************************************** + sparse matrix object + ********************************************************************/ + public class sparsematrix : apobject + { + public double[] vals; + public int[] idx; + public int[] ridx; + public int[] didx; + public int[] uidx; + public int matrixtype; + public int m; + public int n; + public int nfree; + public int ninitialized; + public int tablesize; + public sparsematrix() + { + init(); + } + public override void init() + { + vals = new double[0]; + idx = new int[0]; + ridx = new int[0]; + didx = new int[0]; + uidx = new int[0]; + } + public override alglib.apobject make_copy() + { + sparsematrix _result = new sparsematrix(); + _result.vals = (double[])vals.Clone(); + _result.idx = (int[])idx.Clone(); + _result.ridx = (int[])ridx.Clone(); + _result.didx = (int[])didx.Clone(); + _result.uidx = (int[])uidx.Clone(); + _result.matrixtype = matrixtype; + _result.m = m; + _result.n = n; + _result.nfree = nfree; + _result.ninitialized = ninitialized; + _result.tablesize = tablesize; + return _result; + } + }; }; /******************************************************************** @@ -567,12 +586,235 @@ internal functions ********************************************************************/ public partial class ap { + /******************************************************************** + reverse communication structure + ********************************************************************/ + public const int _ALGLIB_MAX_RCOMMV2_REQUEST = 99; + public class rcommstate : apobject + { + public rcommstate() + { + init(); + } + public override void init() + { + /* continuation */ + stage = -1; + ia = new int[0]; + ba = new bool[0]; + ra = new double[0]; + ca = new alglib.complex[0]; + + /* rcomm-v2 fields */ + requesttype = 0; + reportx = new double[0]; + reportf = 0; + querysize = 0; + queryfuncs = 0; + queryvars = 0; + querydim = 0; + queryformulasize = 0; + querydata = new double[0]; + replyfi = new double[0]; + replydj = new double[0]; + replysj = new alglib.sparse.sparsematrix(); + tmpx1 = new double[0]; + tmpc1 = new double[0]; + tmpf1 = new double[0]; + tmpg1 = new double[0]; + tmpj1 = new double[0,0]; + tmps1 = new alglib.sparse.sparsematrix(); + + /* rcomm handler */ + rcomm2_handler = null; + handler_p0 = null; + handler_p1 = null; + handler_p2 = null; + handler_p3 = null; + } + public override apobject make_copy() + { + rcommstate result = new rcommstate(); + + /* continuation */ + result.stage = stage; + result.ia = (int[])ia.Clone(); + result.ba = (bool[])ba.Clone(); + result.ra = (double[])ra.Clone(); + result.ca = (alglib.complex[])ca.Clone(); + + /* rcomm-v2 fields */ + result.requesttype = requesttype; + result.reportf = reportf; + result.querysize = querysize; + result.queryfuncs = queryfuncs; + result.queryvars = queryvars; + result.querydim = querydim; + result.queryformulasize = queryformulasize; + result.reportx = (double[])reportx.Clone(); + result.querydata = (double[])querydata.Clone(); + result.replyfi = (double[])replyfi.Clone(); + result.replydj = (double[])replydj.Clone(); + result.replysj = (alglib.sparse.sparsematrix)replysj.make_copy(); + result.tmpx1 = (double[])tmpx1.Clone(); + result.tmpc1 = (double[])tmpc1.Clone(); + result.tmpf1 = (double[])tmpf1.Clone(); + result.tmpg1 = (double[])tmpg1.Clone(); + result.tmpj1 = (double[,])tmpj1.Clone(); + result.tmps1 = (alglib.sparse.sparsematrix)tmps1.make_copy(); + + /* rcomm handler */ + result.rcomm2_handler = rcomm2_handler; + result.handler_p0 = handler_p0; + result.handler_p1 = handler_p1; + result.handler_p2 = handler_p2; + result.handler_p3 = handler_p3; + + /* done */ + return result; + } + /* continuation-related fields */ + public int stage; + public int[] ia; + public bool[] ba; + public double[] ra; + public alglib.complex[] ca; + + + /* rcomm interface */ + public int requesttype; + public double[] reportx; + public double reportf; + public int querysize; + public int queryfuncs; + public int queryvars; + public int querydim; + public int queryformulasize; + public double[] querydata; + public double[] replyfi; + public double[] replydj; + public alglib.sparse.sparsematrix replysj; + public double[] tmpx1; + public double[] tmpc1; + public double[] tmpf1; + public double[] tmpg1; + public double[,] tmpj1; + public alglib.sparse.sparsematrix tmps1; + + /* rcomm handler */ + public rcomm2handler rcomm2_handler; + public object handler_p0; + public object handler_p1; + public object handler_p2; + public object handler_p3; + + /* handler methods */ + public bool has_handler() + { + return rcomm2_handler!=null; + } + public void assign_handler(rcomm2handler handler, object p0, object p1, object p2, object p3) + { + rcomm2_handler = handler; + handler_p0 = p0; + handler_p1 = p1; + handler_p2 = p2; + handler_p3 = p3; + } + public void clear_handler() + { + rcomm2_handler = null; + handler_p0 = null; + handler_p1 = null; + handler_p2 = null; + handler_p3 = null; + } + public void forward_handler(rcommstate dst) + { + dst.rcomm2_handler = rcomm2_handler; + dst.handler_p0 = handler_p0; + dst.handler_p1 = handler_p1; + dst.handler_p2 = handler_p2; + dst.handler_p3 = handler_p3; + } + public bool apply_handler_to(rcommstate dst, xparams _params) + { + if( rcomm2_handler==null || dst.requesttype==0 || dst.requesttype>_ALGLIB_MAX_RCOMMV2_REQUEST ) + return false; + int worker_idx = 0; + if( alglib.smp.tsk_current_worker!=null ) + worker_idx = alglib.smp.tsk_current_worker.worker_idx-1; + int prev_worker_idx = alglib._cbck_worker_idx; + alglib._cbck_worker_idx = worker_idx; + try + { + rcomm2_handler(dst, handler_p0, handler_p1, handler_p2, handler_p3, _params); + } + catch + { + alglib._cbck_worker_idx = prev_worker_idx; + throw; + } + alglib._cbck_worker_idx = prev_worker_idx; + return true; + } + }; + + /******************************************************************** + Handlers used to debug optimizers + ********************************************************************/ + public static void _rcomm_debugslantedsumhandlerdense(ap.rcommstate state, object p0, object p1, object p2, object p3, alglib.xparams _params) + { + if( state.requesttype==2 ) + { + for(int qidx=0; qidx1 && !smp.isparallelcontext() && smp.ae_can_parallelize_callbacks(_params) ) + if( try_parallelize_v2request1(this, callbacks) ) + { + AE_CRITICAL_ASSERT(!lock_acquired); + return; + } + #endif + + /* perform serial execution */ + alglib.sparsematrix swrapper = new alglib.sparsematrix(state.replysj); + alglib.sparse.sparsecreatecrsemptybuf(state.queryvars, state.replysj, _params); + for(int qidx=0; qidx1 && !smp.isparallelcontext() && smp.ae_can_parallelize_callbacks(_params) ) + if( try_parallelize_v2request2(this, callbacks) ) + { + AE_CRITICAL_ASSERT(!lock_acquired); + return; + } + #endif + + /* perform serial execution */ + for(int qidx=0; qidx1 && !smp.isparallelcontext() && smp.ae_can_parallelize_callbacks(_params) ) + if( try_parallelize_v2request3(this, callbacks) ) + { + AE_CRITICAL_ASSERT(!lock_acquired); + return; + } + #endif + + /* perform serial execution */ + int njobs = state.querysize*state.queryvars+state.querysize; + for(int job_idx=0; job_idx1 && !smp.isparallelcontext() && smp.ae_can_parallelize_callbacks(_params) ) + if( try_parallelize_v2request4(this, callbacks) ) + { + AE_CRITICAL_ASSERT(!lock_acquired); + return; + } + #endif + + /* perform serial execution */ + for(int qidx=0; qidx1 && !smp.isparallelcontext() && smp.ae_can_parallelize_callbacks(_params) ) + if( try_parallelize_v2request5(this, callbacks) ) + { + AE_CRITICAL_ASSERT(!lock_acquired); + return; + } + #endif + + /* perform serial execution */ + int njobs = state.querysize*state.queryvars+state.querysize; + for(int job_idx=0; job_idx0 ) - tmpC = new double[rq.dim]; - tmpF = new double[rq.funcs]; - tmpG = new double[rq.vars]; - tmpJ = new double[rq.funcs, rq.vars]; + tmpX = new double[rq.rcommv2.queryvars]; + if( rq.rcommv2.querydim>0 ) + tmpC = new double[rq.rcommv2.querydim]; + tmpF = new double[rq.rcommv2.queryfuncs]; + tmpG = new double[rq.rcommv2.queryvars]; + tmpJ = new double[rq.rcommv2.queryfuncs, rq.rcommv2.queryvars]; if( is_sparse ) - alglib.sparsecreatecrsempty(rq.vars, out tmpS); + alglib.sparsecreatecrsempty(rq.rcommv2.queryvars, out tmpS); } // @@ -674,18 +1064,18 @@ public rcommv2_buffers() // resize buffers according to the current request size. // Does not change size, if requested size is too small. // - public void resize(rcommv2_request rq) + public void resize(rcommstate state) { - if( tmpX==null || tmpX.Length0 && (tmpC==null || tmpC.Length0 && (tmpC==null || tmpC.Length>2; + int head = n4<<2; + for(i=0; i>2; + int head = n4<<2; + Intrinsics.Vector256 avx_one = Intrinsics.Vector256.Create(1.0); + for(i=0; i=_ABLASF_KERNEL_SIZE1 ) + unsafe + { + fixed(double* px=x, py=y, pz=z) + { + if( try_rmuladdv(n, py+offsy, pz+offsz, px+offsx) ) + return; + } + } + #endif + for(i=0; i<=n-1; i++) + x[offsx+i] += y[offsy+i]*z[offsz+i]; + } /************************************************************************* Performs inplace subtraction of Y[]*Z[] from X[] @@ -4270,6 +4797,49 @@ public static void rnegmuladdv(int n, for(i=0; i<=n-1; i++) x[i] -= y[i]*z[i]; } + + /************************************************************************* + Performs inplace subtraction of Y[]*Z[] from X[] using offsets + + INPUT PARAMETERS: + N - vector length + Y - array[N+OffsY], vector to process + OffsY - source offset + Z - array[N+OffsZ], vector to process + OffsZ - source offset + X - array[N+OffsX], vector to process + OffsX - destination offset + + RESULT: + X := X - Y*Z (using within-array offsets) + + -- ALGLIB -- + Copyright 29.10.2021 by Bochkanov Sergey + *************************************************************************/ + public static void rnegmuladdvx(int n, + double[] y, + int offsy, + double[] z, + int offsz, + double[] x, + int offsx, + alglib.xparams _params) + { + int i; + #if ALGLIB_USE_SIMD + if( n>=_ABLASF_KERNEL_SIZE1 ) + unsafe + { + fixed(double* px=x, py=y, pz=z) + { + if( try_rnegmuladdv(n, py+offsy, pz+offsz, px+offsx) ) + return; + } + } + #endif + for(i=0; i<=n-1; i++) + x[offsx+i] -= y[offsy+i]*z[offsz+i]; + } /************************************************************************* Performs addition of Y[]*Z[] to X[] @@ -4309,6 +4879,53 @@ public static void rcopymuladdv(int n, for(i=0; i<=n-1; i++) r[i] = x[i]+y[i]*z[i]; } + + /************************************************************************* + Performs addition of Y[]*Z[] to X[], with result being stored to R[] using offsets + + INPUT PARAMETERS: + N - vector length + Y - array[N+OffsY], vector to process + OffsY - source offset + Z - array[N+OffsZ], vector to process + OffsZ - source offset + X - array[N+OffsX], vector to process + OffsX - source offset + R - array[N+OffsR], vector to process + OffsR - destination offset + + RESULT: + R := X + Y*Z (using within-array offsets) + + -- ALGLIB -- + Copyright 29.10.2021 by Bochkanov Sergey + *************************************************************************/ + public static void rcopymuladdvx(int n, + double[] y, + int offsy, + double[] z, + int offsz, + double[] x, + int offsx, + double[] r, + int offsr, + alglib.xparams _params) + { + int i = 0; + #if ALGLIB_USE_SIMD + if( n>=_ABLASF_KERNEL_SIZE1 ) + unsafe + { + fixed(double* px=x, py=y, pz=z, pr=r) + { + if( try_rcopymuladdv(n, py+offsy, pz+offsz, px+offsx, pr+offsr) ) + return; + } + } + #endif + for(i=0; i<=n-1; i++) + r[offsr+i] = x[offsx+i]+y[offsy+i]*z[offsz+i]; + } /************************************************************************* Performs subtraction of Y[]*Z[] from X[] @@ -4348,6 +4965,53 @@ public static void rcopynegmuladdv(int n, for(i=0; i<=n-1; i++) r[i] = x[i]-y[i]*z[i]; } + + /************************************************************************* + Performs subtraction of Y[]*Z[] from X[], with result being stored to R[] using offsets + + INPUT PARAMETERS: + N - vector length + Y - array[N+OffsY], vector to process + OffsY - source offset + Z - array[N+OffsZ], vector to process + OffsZ - source offset + X - array[N+OffsX], vector to process + OffsX - source offset + R - array[N+OffsR], vector to process + OffsR - destination offset + + RESULT: + R := X - Y*Z (using within-array offsets) + + -- ALGLIB -- + Copyright 29.10.2021 by Bochkanov Sergey + *************************************************************************/ + public static void rcopynegmuladdvx(int n, + double[] y, + int offsy, + double[] z, + int offsz, + double[] x, + int offsx, + double[] r, + int offsr, + alglib.xparams _params) + { + int i = 0; + #if ALGLIB_USE_SIMD + if( n>=_ABLASF_KERNEL_SIZE1 ) + unsafe + { + fixed(double* px=x, py=y, pz=z, pr=r) + { + if( try_rcopynegmuladdv(n, py+offsy, pz+offsz, px+offsx, pr+offsr) ) + return; + } + } + #endif + for(i=0; i<=n-1; i++) + r[offsr+i] = x[offsx+i]-y[offsy+i]*z[offsz+i]; + } /************************************************************************* Performs componentwise multiplication of vector X[] by vector Y[] @@ -4388,6 +5052,49 @@ public static void rmergemulv(int n, } } + /************************************************************************* + Performs componentwise multiplication of vector X[] by vector Y[] using offsets + + INPUT PARAMETERS: + N - vector length + Y - vector to multiply by + OffsY - source offset + X - target vector + OffsX - destination offset + + RESULT: + X := componentwise(X*Y) using offsets + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rmergemulvx(int n, + double[] y, + int offsy, + double[] x, + int offsx, + alglib.xparams _params) + { + int i = 0; + + #if ALGLIB_USE_SIMD + if( n>=_ABLASF_KERNEL_SIZE1 ) + unsafe + { + fixed(double* px=x, py=y) + { + if( try_rmergemul(n, py+offsy, px+offsx) ) + return; + } + } + #endif + + for(i=0; i<=n-1; i++) + { + x[offsx+i] = x[offsx+i]*y[offsy+i]; + } + } + /************************************************************************* Performs componentwise multiplication of row X[] by vector Y[] @@ -5637,6 +6344,88 @@ public static void rcopymulv(int n, y[i] = v*x[i]; } } + + /************************************************************************* + Performs copying with reciprocation: Y[]:=1/X[] + + INPUT PARAMETERS: + N - vector length + X - array[N], source, all elements must be non-zero + Y - preallocated array[N] + + OUTPUT PARAMETERS: + Y - array[N], Y = 1/X + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rcopyrcpv(int n, + double[] x, + double[] y, + alglib.xparams _params) + { + int i = 0; + + #if ALGLIB_USE_SIMD + if( n>=_ABLASF_KERNEL_SIZE1 ) + unsafe + { + fixed(double* px=x, py=y) + { + if( try_rcopyrcp(n, px, py) ) + return; + } + } + #endif + + for(i=0; i<=n-1; i++) + { + y[i] = 1.0/x[i]; + } + } + + /************************************************************************* + Performs copying with reciprocation, using offsets within arrays: Y[]:=1/X[] + + INPUT PARAMETERS: + N - vector length + X - array[OffsX+N], source, all elements must be non-zero + OffsX - start offset within X + Y - preallocated array[OffsY+N] + OffsY - start offset within Y + + OUTPUT PARAMETERS: + Y - N elements starting from OffsY are replaced by 1/X[OffsX:OffsX+N-1] + + -- ALGLIB -- + Copyright 20.01.2020 by Bochkanov Sergey + *************************************************************************/ + public static void rcopyrcpvx(int n, + double[] x, + int offsx, + double[] y, + int offsy, + alglib.xparams _params) + { + int i = 0; + + #if ALGLIB_USE_SIMD + if( n>=_ABLASF_KERNEL_SIZE1 ) + unsafe + { + fixed(double* px=x, py=y) + { + if( try_rcopyrcp(n, px+offsx, py+offsy) ) + return; + } + } + #endif + + for(i=0; i<=n-1; i++) + { + y[offsy+i] = 1.0/x[offsx+i]; + } + } /************************************************************************* Performs copying with multiplication of V*X[] to Y[I,*] @@ -8270,16 +9059,28 @@ private static bool bhpanelevalfastkernel(double d0, } #endif +#if NET5_0_OR_GREATER +#pragma warning disable CS8981 +#endif public partial class alglib { public partial class smp { public static int cores_count = 1; public static volatile int cores_to_use = 1; + + [System.ThreadStatic] + public static ae_worker_thread tsk_current_worker; /* never change it, must be always null */ + public static bool isparallelcontext() { return false; } + + public class ae_worker_thread + { + public volatile int worker_idx; /* never change it, must be always 0 */ + } } public class smpselftests { @@ -8302,6 +9103,9 @@ public static int getcorestouse() } } +#if NET5_0_OR_GREATER +#pragma warning disable CS8981 +#endif public partial class alglib { /* @@ -8336,6 +9140,9 @@ static internal ulong ae_get_effective_threading_cbk(xparams p) } +#if NET5_0_OR_GREATER +#pragma warning disable CS8981 +#endif public partial class alglib { /* @@ -8485,18 +9292,18 @@ internal static void process_v2request_1(rcommv2_request request, int query_idx, // // Query and reply offsets // - int query_data_offs = query_idx*(request.vars+request.dim); - int reply_fi_offs = query_idx*request.funcs; + int query_data_offs = query_idx*(request.rcommv2.queryvars+request.rcommv2.querydim); + int reply_fi_offs = query_idx*request.rcommv2.queryfuncs; // // Copy inputs to buffers // - for(int i=0; i0 ) - for(int i=0; i0 ) + for(int i=0; i0 ) - for(int i=0; i0 ) + for(int i=0; i0 && request.funcs==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0 && request.rcommv2.queryfuncs==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.grad_p(buffers.tmpX, buffers.tmpC, ref f0, buffers.tmpG, request.obj); - request.reply_fi[reply_fi_offs] = f0; - for(int i=0; i0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.jac_p(buffers.tmpX, buffers.tmpC, buffers.tmpF, buffers.tmpJ, request.obj); - for(int ridx=0; ridx0 ) - for(int i=0; i0 ) + for(int i=0; i0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.func_p(buffers.tmpX, buffers.tmpC, ref f, request.obj); buffers.tmpF[0] = f; } else if( callbacks.fvec!=null ) { - //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec(buffers.tmpX, buffers.tmpF, request.obj); } else if( callbacks.fvec_p!=null ) { - //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec_p(buffers.tmpX, buffers.tmpC, buffers.tmpF, request.obj); } else alglib.ap.assert(false, "ALGLIB: integrity check in '"+request.subpackage+"' subpackage failed; no callback for optimizer request"); buffers.tmpX[var_idx] = xprev; for(int t=0; t0 ) - for(int i=0; i0 ) + for(int i=0; i0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.func_p(buffers.tmpX, buffers.tmpC, ref f, request.obj); buffers.tmpF[0] = f; } else if( callbacks.fvec!=null ) { - //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec(buffers.tmpX, buffers.tmpF, request.obj); } else if( callbacks.fvec_p!=null ) { - //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec_p(buffers.tmpX, buffers.tmpC, buffers.tmpF, request.obj); } else alglib.ap.assert(false, "ALGLIB: integrity check in '"+request.subpackage+"' subpackage failed; no callback for optimizer request"); for(int t=0; t0 ) - for(int i=0; i0 ) + for(int i=0; i0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.func_p(buffers.tmpX, buffers.tmpC, ref f, request.obj); buffers.tmpF[0] = f; } else if( callbacks.fvec!=null ) { - //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec(buffers.tmpX, buffers.tmpF, request.obj); } else if( callbacks.fvec_p!=null ) { - //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec_p(buffers.tmpX, buffers.tmpC, buffers.tmpF, request.obj); } else alglib.ap.assert(false, "ALGLIB: integrity check in '"+request.subpackage+"' subpackage failed; no callback for optimizer request"); buffers.tmpX[var_idx] = xprev; for(int t=0; t0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.func_p(buffers.tmpX, buffers.tmpC, ref f, request.obj); buffers.tmpF[0] = f; } else if( callbacks.fvec!=null ) { - //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec(buffers.tmpX, buffers.tmpF, request.obj); } else if( callbacks.fvec_p!=null ) { - //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec_p(buffers.tmpX, buffers.tmpC, buffers.tmpF, request.obj); } else alglib.ap.assert(false, "ALGLIB: integrity check in '"+request.subpackage+"' subpackage failed; no callback for optimizer request"); buffers.tmpX[var_idx] = xprev; for(int t=0; t0 ) - for(int i=0; i0 ) + for(int i=0; i0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0 && m==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.func_p(buffers.tmpX, buffers.tmpC, ref f, request.obj); buffers.tmpF[0] = f; } else if( callbacks.fvec!=null ) { - //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec(buffers.tmpX, buffers.tmpF, request.obj); } else if( callbacks.fvec_p!=null ) { - //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec_p(buffers.tmpX, buffers.tmpC, buffers.tmpF, request.obj); } else alglib.ap.assert(false, "ALGLIB: integrity check in '"+request.subpackage+"' subpackage failed; no callback for optimizer request"); for(int t=0; t0 ) - for(int i=0; i0 ) + for(int i=0; i0 && request.funcs==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0 && request.rcommv2.queryfuncs==1, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.func_p(buffers.tmpX, buffers.tmpC, ref f0, request.obj); - request.reply_fi[reply_fi_offs] = f0; + request.rcommv2.replyfi[reply_fi_offs] = f0; return; } if( callbacks.fvec!=null ) { - //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.dim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim==0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec(buffers.tmpX, buffers.tmpF, request.obj); - for(int ridx=0; ridx0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); + //!!!!!_ALGLIB_ASSERT_THROW_OR_BREAK(request.rcommv2.querydim>0, std::string("ALGLIB: integrity check in '")+request.subpackage+"' subpackage failed; incompatible callback for optimizer request"); callbacks.fvec_p(buffers.tmpX, buffers.tmpC, buffers.tmpF, request.obj); - for(int ridx=0; ridx>> SOURCE LICENSE >>> @@ -29,142 +29,125 @@ public partial class alglib /************************************************************************* - Principal components analysis - - This function builds orthogonal basis where first axis corresponds to - direction with maximum variance, second axis maximizes variance in the - subspace orthogonal to first axis and so on. - - This function builds FULL basis, i.e. returns N vectors corresponding to - ALL directions, no matter how informative. If you need just a few (say, - 10 or 50) of the most important directions, you may find it faster to use - one of the reduced versions: - * pcatruncatedsubspace() - for subspace iteration based method + Optimal binary classification - It should be noted that, unlike LDA, PCA does not use class labels. + Algorithms finds optimal (=with minimal cross-entropy) binary partition. + Internal subroutine. INPUT PARAMETERS: - X - dataset, array[NPoints,NVars]. - matrix contains ONLY INDEPENDENT VARIABLES. - NPoints - dataset size, NPoints>=0 - NVars - number of independent variables, NVars>=1 + A - array[0..N-1], variable + C - array[0..N-1], class numbers (0 or 1). + N - array size OUTPUT PARAMETERS: - S2 - array[NVars]. variance values corresponding - to basis vectors. - V - array[NVars,NVars] - matrix, whose columns store basis vectors. - - ! FREE EDITION OF ALGLIB: - ! - ! Free Edition of ALGLIB supports following important features for this - ! function: - ! * C++ version: x64 SIMD support using C++ intrinsics - ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics - ! - ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB - ! Reference Manual in order to find out how to activate SIMD support - ! in ALGLIB. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! * hardware vendor (Intel, ARM) implementations of linear algebra and - ! other primitives (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Info - completetion code: + * -3, all values of A[] are same (partition is impossible) + * -2, one of C[] is incorrect (<0, >1) + * -1, incorrect pararemets were passed (N<=0). + * 1, OK + Threshold- partiton boundary. Left part contains values which are + strictly less than Threshold. Right part contains values + which are greater than or equal to Threshold. + PAL, PBL- probabilities P(0|v=Threshold) and P(1|v>=Threshold) + CVE - cross-validation estimate of cross-entropy -- ALGLIB -- - Copyright 25.08.2008 by Bochkanov Sergey + Copyright 22.05.2008 by Bochkanov Sergey *************************************************************************/ - public static void pcabuildbasis(double[,] x, int npoints, int nvars, out double[] s2, out double[,] v) + public static void dsoptimalsplit2(double[] a, int[] c, int n, out int info, out double threshold, out double pal, out double pbl, out double par, out double pbr, out double cve) { - s2 = new double[0]; - v = new double[0,0]; - pca.pcabuildbasis(x, npoints, nvars, ref s2, ref v, null); + info = 0; + threshold = 0; + pal = 0; + pbl = 0; + par = 0; + pbr = 0; + cve = 0; + bdss.dsoptimalsplit2(a, c, n, ref info, ref threshold, ref pal, ref pbl, ref par, ref pbr, ref cve, null); } - public static void pcabuildbasis(double[,] x, int npoints, int nvars, out double[] s2, out double[,] v, alglib.xparams _params) + public static void dsoptimalsplit2(double[] a, int[] c, int n, out int info, out double threshold, out double pal, out double pbl, out double par, out double pbr, out double cve, alglib.xparams _params) { - s2 = new double[0]; - v = new double[0,0]; - pca.pcabuildbasis(x, npoints, nvars, ref s2, ref v, _params); + info = 0; + threshold = 0; + pal = 0; + pbl = 0; + par = 0; + pbr = 0; + cve = 0; + bdss.dsoptimalsplit2(a, c, n, ref info, ref threshold, ref pal, ref pbl, ref par, ref pbr, ref cve, _params); } - - public static void pcabuildbasis(double[,] x, out double[] s2, out double[,] v) - { - int npoints; - int nvars; - - s2 = new double[0]; - v = new double[0,0]; - npoints = ap.rows(x); - nvars = ap.cols(x); - pca.pcabuildbasis(x, npoints, nvars, ref s2, ref v, null); - return; - } - - public static void pcabuildbasis(double[,] x, out double[] s2, out double[,] v, alglib.xparams _params) + /************************************************************************* + Optimal partition, internal subroutine. Fast version. + + Accepts: + A array[0..N-1] array of attributes array[0..N-1] + C array[0..N-1] array of class labels + TiesBuf array[0..N] temporaries (ties) + CntBuf array[0..2*NC-1] temporaries (counts) + Alpha centering factor (0<=alpha<=1, recommended value - 0.05) + BufR array[0..N-1] temporaries + BufI array[0..N-1] temporaries + + Output: + Info error code (">0"=OK, "<0"=bad) + RMS training set RMS error + CVRMS leave-one-out RMS error + + Note: + content of all arrays is changed by subroutine; + it doesn't allocate temporaries. + + -- ALGLIB -- + Copyright 11.12.2008 by Bochkanov Sergey + *************************************************************************/ + public static void dsoptimalsplit2fast(ref double[] a, ref int[] c, ref int[] tiesbuf, ref int[] cntbuf, ref double[] bufr, ref int[] bufi, int n, int nc, double alpha, out int info, out double threshold, out double rms, out double cvrms) { - int npoints; - int nvars; - - s2 = new double[0]; - v = new double[0,0]; - npoints = ap.rows(x); - nvars = ap.cols(x); - pca.pcabuildbasis(x, npoints, nvars, ref s2, ref v, _params); - - return; + info = 0; + threshold = 0; + rms = 0; + cvrms = 0; + bdss.dsoptimalsplit2fast(ref a, ref c, ref tiesbuf, ref cntbuf, ref bufr, ref bufi, n, nc, alpha, ref info, ref threshold, ref rms, ref cvrms, null); } - /************************************************************************* - Principal components analysis + public static void dsoptimalsplit2fast(ref double[] a, ref int[] c, ref int[] tiesbuf, ref int[] cntbuf, ref double[] bufr, ref int[] bufi, int n, int nc, double alpha, out int info, out double threshold, out double rms, out double cvrms, alglib.xparams _params) + { + info = 0; + threshold = 0; + rms = 0; + cvrms = 0; + bdss.dsoptimalsplit2fast(ref a, ref c, ref tiesbuf, ref cntbuf, ref bufr, ref bufi, n, nc, alpha, ref info, ref threshold, ref rms, ref cvrms, _params); + } - This function performs truncated PCA, i.e. returns just a few most important - directions. +} +public partial class alglib +{ - Internally it uses iterative eigensolver which is very efficient when only - a minor fraction of full basis is required. Thus, if you need full basis, - it is better to use pcabuildbasis() function. + + /************************************************************************* + Multiclass Fisher LDA - It should be noted that, unlike LDA, PCA does not use class labels. + The function finds coefficients of a linear combination which optimally + separates training set. Most suited for 2-class problems, see fisherldan() + for an variant that returns N-dimensional basis. INPUT PARAMETERS: - X - dataset, array[0..NPoints-1,0..NVars-1]. - matrix contains ONLY INDEPENDENT VARIABLES. - NPoints - dataset size, NPoints>=0 + XY - training set, array[NPoints,NVars+1]. + First NVars columns store values of independent + variables, the next column stores class index (from 0 + to NClasses-1) which dataset element belongs to. + Fractional values are rounded to the nearest integer. + The class index must be in the [0,NClasses-1] range, + an exception is generated otherwise. + NPoints - training set size, NPoints>=0 NVars - number of independent variables, NVars>=1 - NNeeded - number of requested components, in [1,NVars] range; - this function is efficient only for NNeeded<=2 OUTPUT PARAMETERS: - S2 - array[NNeeded]. Variance values corresponding - to basis vectors. - V - array[NVars,NNeeded] - matrix, whose columns store basis vectors. - - NOTE: passing eps=0 and maxits=0 results in small eps being selected as - stopping condition. Exact value of automatically selected eps is version- - -dependent. + W - linear combination coefficients, array[NVars] ! FREE EDITION OF ALGLIB: ! @@ -191,98 +174,70 @@ stopping condition. Exact value of automatically selected eps is version- ! related features provided by commercial edition of ALGLIB. -- ALGLIB -- - Copyright 10.01.2017 by Bochkanov Sergey + Copyright 31.05.2008 by Bochkanov Sergey *************************************************************************/ - public static void pcatruncatedsubspace(double[,] x, int npoints, int nvars, int nneeded, double eps, int maxits, out double[] s2, out double[,] v) + public static void fisherlda(double[,] xy, int npoints, int nvars, int nclasses, out double[] w) { - s2 = new double[0]; - v = new double[0,0]; - pca.pcatruncatedsubspace(x, npoints, nvars, nneeded, eps, maxits, ref s2, ref v, null); + w = new double[0]; + lda.fisherlda(xy, npoints, nvars, nclasses, ref w, null); } - public static void pcatruncatedsubspace(double[,] x, int npoints, int nvars, int nneeded, double eps, int maxits, out double[] s2, out double[,] v, alglib.xparams _params) + public static void fisherlda(double[,] xy, int npoints, int nvars, int nclasses, out double[] w, alglib.xparams _params) { - s2 = new double[0]; - v = new double[0,0]; - pca.pcatruncatedsubspace(x, npoints, nvars, nneeded, eps, maxits, ref s2, ref v, _params); + w = new double[0]; + lda.fisherlda(xy, npoints, nvars, nclasses, ref w, _params); } - public static void pcatruncatedsubspace(double[,] x, int nneeded, double eps, int maxits, out double[] s2, out double[,] v) + public static void fisherlda(double[,] xy, int nclasses, out double[] w) { int npoints; int nvars; - s2 = new double[0]; - v = new double[0,0]; - npoints = ap.rows(x); - nvars = ap.cols(x); - pca.pcatruncatedsubspace(x, npoints, nvars, nneeded, eps, maxits, ref s2, ref v, null); + w = new double[0]; + npoints = ap.rows(xy); + nvars = ap.cols(xy)-1; + lda.fisherlda(xy, npoints, nvars, nclasses, ref w, null); return; } - public static void pcatruncatedsubspace(double[,] x, int nneeded, double eps, int maxits, out double[] s2, out double[,] v, alglib.xparams _params) + public static void fisherlda(double[,] xy, int nclasses, out double[] w, alglib.xparams _params) { int npoints; int nvars; - s2 = new double[0]; - v = new double[0,0]; - npoints = ap.rows(x); - nvars = ap.cols(x); - pca.pcatruncatedsubspace(x, npoints, nvars, nneeded, eps, maxits, ref s2, ref v, _params); + w = new double[0]; + npoints = ap.rows(xy); + nvars = ap.cols(xy)-1; + lda.fisherlda(xy, npoints, nvars, nclasses, ref w, _params); return; } /************************************************************************* - Sparse truncated principal components analysis - - This function performs sparse truncated PCA, i.e. returns just a few most - important principal components for a sparse input X. - - Internally it uses iterative eigensolver which is very efficient when only - a minor fraction of full basis is required. + N-dimensional multiclass Fisher LDA - It should be noted that, unlike LDA, PCA does not use class labels. + Subroutine finds coefficients of linear combinations which optimally separates + training set on classes. It returns N-dimensional basis whose vector are sorted + by quality of training set separation (in descending order). INPUT PARAMETERS: - X - sparse dataset, sparse npoints*nvars matrix. It is - recommended to use CRS sparse storage format; non-CRS - input will be internally converted to CRS. - Matrix contains ONLY INDEPENDENT VARIABLES, and must - be EXACTLY npoints*nvars. - NPoints - dataset size, NPoints>=0 + XY - training set, array[NPoints,NVars+1]. + First NVars columns store values of independent + variables, the next column stores class index (from 0 + to NClasses-1) which dataset element belongs to. + Fractional values are rounded to the nearest integer. + The class index must be in the [0,NClasses-1] range, + an exception is generated otherwise. + NPoints - training set size, NPoints>=0 NVars - number of independent variables, NVars>=1 - NNeeded - number of requested components, in [1,NVars] range; - this function is efficient only for NNeeded<=2 OUTPUT PARAMETERS: - S2 - array[NNeeded]. Variance values corresponding - to basis vectors. - V - array[NVars,NNeeded] - matrix, whose columns store basis vectors. - - NOTE: passing eps=0 and maxits=0 results in small eps being selected as - a stopping condition. Exact value of automatically selected eps is - version-dependent. - - NOTE: zero MaxIts is silently replaced by some reasonable value which - prevents eternal loops (possible when inputs are degenerate and too - stringent stopping criteria are specified). In current version it - is 50+2*NVars. + W - basis, array[NVars,NVars] + columns of matrix stores basis vectors, sorted by + quality of training set separation (in descending order) ! FREE EDITION OF ALGLIB: ! @@ -309,118 +264,44 @@ is 50+2*NVars. ! related features provided by commercial edition of ALGLIB. -- ALGLIB -- - Copyright 10.01.2017 by Bochkanov Sergey + Copyright 31.05.2008 by Bochkanov Sergey *************************************************************************/ - public static void pcatruncatedsubspacesparse(sparsematrix x, int npoints, int nvars, int nneeded, double eps, int maxits, out double[] s2, out double[,] v) + public static void fisherldan(double[,] xy, int npoints, int nvars, int nclasses, out double[,] w) { - s2 = new double[0]; - v = new double[0,0]; - pca.pcatruncatedsubspacesparse(x.innerobj, npoints, nvars, nneeded, eps, maxits, ref s2, ref v, null); + w = new double[0,0]; + lda.fisherldan(xy, npoints, nvars, nclasses, ref w, null); } - public static void pcatruncatedsubspacesparse(sparsematrix x, int npoints, int nvars, int nneeded, double eps, int maxits, out double[] s2, out double[,] v, alglib.xparams _params) + public static void fisherldan(double[,] xy, int npoints, int nvars, int nclasses, out double[,] w, alglib.xparams _params) { - s2 = new double[0]; - v = new double[0,0]; - pca.pcatruncatedsubspacesparse(x.innerobj, npoints, nvars, nneeded, eps, maxits, ref s2, ref v, _params); + w = new double[0,0]; + lda.fisherldan(xy, npoints, nvars, nclasses, ref w, _params); } - -} -public partial class alglib -{ - - - /************************************************************************* - Optimal binary classification - - Algorithms finds optimal (=with minimal cross-entropy) binary partition. - Internal subroutine. - - INPUT PARAMETERS: - A - array[0..N-1], variable - C - array[0..N-1], class numbers (0 or 1). - N - array size - - OUTPUT PARAMETERS: - Info - completetion code: - * -3, all values of A[] are same (partition is impossible) - * -2, one of C[] is incorrect (<0, >1) - * -1, incorrect pararemets were passed (N<=0). - * 1, OK - Threshold- partiton boundary. Left part contains values which are - strictly less than Threshold. Right part contains values - which are greater than or equal to Threshold. - PAL, PBL- probabilities P(0|v=Threshold) and P(1|v>=Threshold) - CVE - cross-validation estimate of cross-entropy - - -- ALGLIB -- - Copyright 22.05.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dsoptimalsplit2(double[] a, int[] c, int n, out int info, out double threshold, out double pal, out double pbl, out double par, out double pbr, out double cve) + + public static void fisherldan(double[,] xy, int nclasses, out double[,] w) { - info = 0; - threshold = 0; - pal = 0; - pbl = 0; - par = 0; - pbr = 0; - cve = 0; - bdss.dsoptimalsplit2(a, c, n, ref info, ref threshold, ref pal, ref pbl, ref par, ref pbr, ref cve, null); - } + int npoints; + int nvars; - public static void dsoptimalsplit2(double[] a, int[] c, int n, out int info, out double threshold, out double pal, out double pbl, out double par, out double pbr, out double cve, alglib.xparams _params) - { - info = 0; - threshold = 0; - pal = 0; - pbl = 0; - par = 0; - pbr = 0; - cve = 0; - bdss.dsoptimalsplit2(a, c, n, ref info, ref threshold, ref pal, ref pbl, ref par, ref pbr, ref cve, _params); - } + w = new double[0,0]; + npoints = ap.rows(xy); + nvars = ap.cols(xy)-1; + lda.fisherldan(xy, npoints, nvars, nclasses, ref w, null); - /************************************************************************* - Optimal partition, internal subroutine. Fast version. - - Accepts: - A array[0..N-1] array of attributes array[0..N-1] - C array[0..N-1] array of class labels - TiesBuf array[0..N] temporaries (ties) - CntBuf array[0..2*NC-1] temporaries (counts) - Alpha centering factor (0<=alpha<=1, recommended value - 0.05) - BufR array[0..N-1] temporaries - BufI array[0..N-1] temporaries - - Output: - Info error code (">0"=OK, "<0"=bad) - RMS training set RMS error - CVRMS leave-one-out RMS error - - Note: - content of all arrays is changed by subroutine; - it doesn't allocate temporaries. - - -- ALGLIB -- - Copyright 11.12.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dsoptimalsplit2fast(ref double[] a, ref int[] c, ref int[] tiesbuf, ref int[] cntbuf, ref double[] bufr, ref int[] bufi, int n, int nc, double alpha, out int info, out double threshold, out double rms, out double cvrms) - { - info = 0; - threshold = 0; - rms = 0; - cvrms = 0; - bdss.dsoptimalsplit2fast(ref a, ref c, ref tiesbuf, ref cntbuf, ref bufr, ref bufi, n, nc, alpha, ref info, ref threshold, ref rms, ref cvrms, null); + return; } - - public static void dsoptimalsplit2fast(ref double[] a, ref int[] c, ref int[] tiesbuf, ref int[] cntbuf, ref double[] bufr, ref int[] bufi, int n, int nc, double alpha, out int info, out double threshold, out double rms, out double cvrms, alglib.xparams _params) + + public static void fisherldan(double[,] xy, int nclasses, out double[,] w, alglib.xparams _params) { - info = 0; - threshold = 0; - rms = 0; - cvrms = 0; - bdss.dsoptimalsplit2fast(ref a, ref c, ref tiesbuf, ref cntbuf, ref bufr, ref bufi, n, nc, alpha, ref info, ref threshold, ref rms, ref cvrms, _params); + int npoints; + int nvars; + + w = new double[0,0]; + npoints = ap.rows(xy); + nvars = ap.cols(xy)-1; + lda.fisherldan(xy, npoints, nvars, nclasses, ref w, _params); + + return; } } @@ -429,46 +310,35 @@ public partial class alglib /************************************************************************* - Model's errors: - * RelCLSError - fraction of misclassified cases. - * AvgCE - acerage cross-entropy - * RMSError - root-mean-square error - * AvgError - average error - * AvgRelError - average relative error - - NOTE 1: RelCLSError/AvgCE are zero on regression problems. + Buffer object which is used to perform various requests (usually model + inference) in the multithreaded mode (multiple threads working with same + KNN object). - NOTE 2: on classification problems RMSError/AvgError/AvgRelError contain - errors in prediction of posterior probabilities + This object should be created with KNNCreateBuffer(). *************************************************************************/ - public class modelerrors : alglibobject + public class knnbuffer : alglibobject { // // Public declarations // - public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } - public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } - public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } - public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } - public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } - public modelerrors() + public knnbuffer() { - _innerobj = new mlpbase.modelerrors(); + _innerobj = new knn.knnbuffer(); } public override alglib.alglibobject make_copy() { - return new modelerrors((mlpbase.modelerrors)_innerobj.make_copy()); + return new knnbuffer((knn.knnbuffer)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // - private mlpbase.modelerrors _innerobj; - public mlpbase.modelerrors innerobj { get { return _innerobj; } } - public modelerrors(mlpbase.modelerrors obj) + private knn.knnbuffer _innerobj; + public knn.knnbuffer innerobj { get { return _innerobj; } } + public knnbuffer(knn.knnbuffer obj) { _innerobj = obj; } @@ -476,31 +346,32 @@ public modelerrors(mlpbase.modelerrors obj) /************************************************************************* - + A KNN builder object; this object encapsulates dataset and all related + settings, it is used to create an actual instance of KNN model. *************************************************************************/ - public class multilayerperceptron : alglibobject + public class knnbuilder : alglibobject { // // Public declarations // - public multilayerperceptron() + public knnbuilder() { - _innerobj = new mlpbase.multilayerperceptron(); + _innerobj = new knn.knnbuilder(); } public override alglib.alglibobject make_copy() { - return new multilayerperceptron((mlpbase.multilayerperceptron)_innerobj.make_copy()); + return new knnbuilder((knn.knnbuilder)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // - private mlpbase.multilayerperceptron _innerobj; - public mlpbase.multilayerperceptron innerobj { get { return _innerobj; } } - public multilayerperceptron(mlpbase.multilayerperceptron obj) + private knn.knnbuilder _innerobj; + public knn.knnbuilder innerobj { get { return _innerobj; } } + public knnbuilder(knn.knnbuilder obj) { _innerobj = obj; } @@ -508,41 +379,91 @@ public multilayerperceptron(mlpbase.multilayerperceptron obj) /************************************************************************* - This function serializes data structure to string/stream. - - Important properties of s_out: - * it contains alphanumeric characters, dots, underscores, minus signs - * these symbols are grouped into words, which are separated by spaces - and Windows-style (CR+LF) newlines - * although serializer uses spaces and CR+LF as separators, you can - replace any separator character by arbitrary combination of spaces, - tabs, Windows or Unix newlines. It allows flexible reformatting of - the string in case you want to include it into a text or XML file. - But you should not insert separators into the middle of the "words" - nor should you change the case of letters. - * s_out can be freely moved between 32-bit and 64-bit systems, little - and big endian machines, and so on. You can serialize structure on - 32-bit machine and unserialize it on 64-bit one (or vice versa), or - serialize it on SPARC and unserialize on x86. You can also - serialize it in C++ version of ALGLIB and unserialize it in C# one, - and vice versa. + KNN model, can be used for classification or regression *************************************************************************/ - public static void mlpserialize(multilayerperceptron obj, out string s_out) + public class knnmodel : alglibobject { - alglib.serializer s = new alglib.serializer(); - s.alloc_start(); - mlpbase.mlpalloc(s, obj.innerobj, null); - s.sstart_str(); - mlpbase.mlpserialize(s, obj.innerobj, null); - s.stop(); - s_out = s.get_string(); - } - - - /************************************************************************* - This function serializes data structure to string/stream. + // + // Public declarations + // - Important properties of s_out: + public knnmodel() + { + _innerobj = new knn.knnmodel(); + } + + public override alglib.alglibobject make_copy() + { + return new knnmodel((knn.knnmodel)_innerobj.make_copy()); + } + + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private knn.knnmodel _innerobj; + public knn.knnmodel innerobj { get { return _innerobj; } } + public knnmodel(knn.knnmodel obj) + { + _innerobj = obj; + } + } + + + /************************************************************************* + KNN training report. + + Following fields store training set errors: + * relclserror - fraction of misclassified cases, [0,1] + * avgce - average cross-entropy in bits per symbol + * rmserror - root-mean-square error + * avgerror - average error + * avgrelerror - average relative error + + For classification problems: + * RMS, AVG and AVGREL errors are calculated for posterior probabilities + + For regression problems: + * RELCLS and AVGCE errors are zero + *************************************************************************/ + public class knnreport : alglibobject + { + // + // Public declarations + // + public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } + public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } + public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } + public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } + public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } + + public knnreport() + { + _innerobj = new knn.knnreport(); + } + + public override alglib.alglibobject make_copy() + { + return new knnreport((knn.knnreport)_innerobj.make_copy()); + } + + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private knn.knnreport _innerobj; + public knn.knnreport innerobj { get { return _innerobj; } } + public knnreport(knn.knnreport obj) + { + _innerobj = obj; + } + } + + + /************************************************************************* + This function serializes data structure to string/stream. + + Important properties of s_out: * it contains alphanumeric characters, dots, underscores, minus signs * these symbols are grouped into words, which are separated by spaces and Windows-style (CR+LF) newlines @@ -559,13 +480,45 @@ serialize it on SPARC and unserialize on x86. You can also serialize it in C++ version of ALGLIB and unserialize it in C# one, and vice versa. *************************************************************************/ - public static void mlpserialize(multilayerperceptron obj, System.IO.Stream stream_out) + public static void knnserialize(knnmodel obj, out string s_out) { alglib.serializer s = new alglib.serializer(); s.alloc_start(); - mlpbase.mlpalloc(s, obj.innerobj, null); + knn.knnalloc(s, obj.innerobj, null); + s.sstart_str(); + knn.knnserialize(s, obj.innerobj, null); + s.stop(); + s_out = s.get_string(); + } + + + /************************************************************************* + This function serializes data structure to string/stream. + + Important properties of s_out: + * it contains alphanumeric characters, dots, underscores, minus signs + * these symbols are grouped into words, which are separated by spaces + and Windows-style (CR+LF) newlines + * although serializer uses spaces and CR+LF as separators, you can + replace any separator character by arbitrary combination of spaces, + tabs, Windows or Unix newlines. It allows flexible reformatting of + the string in case you want to include it into a text or XML file. + But you should not insert separators into the middle of the "words" + nor should you change the case of letters. + * s_out can be freely moved between 32-bit and 64-bit systems, little + and big endian machines, and so on. You can serialize structure on + 32-bit machine and unserialize it on 64-bit one (or vice versa), or + serialize it on SPARC and unserialize on x86. You can also + serialize it in C++ version of ALGLIB and unserialize it in C# one, + and vice versa. + *************************************************************************/ + public static void knnserialize(knnmodel obj, System.IO.Stream stream_out) + { + alglib.serializer s = new alglib.serializer(); + s.alloc_start(); + knn.knnalloc(s, obj.innerobj, null); s.sstart_stream(stream_out); - mlpbase.mlpserialize(s, obj.innerobj, null); + knn.knnserialize(s, obj.innerobj, null); s.stop(); } @@ -573,12 +526,12 @@ public static void mlpserialize(multilayerperceptron obj, System.IO.Stream strea /************************************************************************* This function unserializes data structure from string/stream. *************************************************************************/ - public static void mlpunserialize(string s_in, out multilayerperceptron obj) + public static void knnunserialize(string s_in, out knnmodel obj) { alglib.serializer s = new alglib.serializer(); - obj = new multilayerperceptron(); + obj = new knnmodel(); s.ustart_str(s_in); - mlpbase.mlpunserialize(s, obj.innerobj, null); + knn.knnunserialize(s, obj.innerobj, null); s.stop(); } @@ -586,1336 +539,1635 @@ public static void mlpunserialize(string s_in, out multilayerperceptron obj) /************************************************************************* This function unserializes data structure from string/stream. *************************************************************************/ - public static void mlpunserialize(System.IO.Stream stream_in, out multilayerperceptron obj) + public static void knnunserialize(System.IO.Stream stream_in, out knnmodel obj) { alglib.serializer s = new alglib.serializer(); - obj = new multilayerperceptron(); + obj = new knnmodel(); s.ustart_stream(stream_in); - mlpbase.mlpunserialize(s, obj.innerobj, null); + knn.knnunserialize(s, obj.innerobj, null); s.stop(); } /************************************************************************* - Creates neural network with NIn inputs, NOut outputs, without hidden - layers, with linear output layer. Network weights are filled with small - random values. + This function creates buffer structure which can be used to perform + parallel KNN requests. + + KNN subpackage provides two sets of computing functions - ones which use + internal buffer of KNN model (these functions are single-threaded because + they use same buffer, which can not shared between threads), and ones + which use external buffer. + + This function is used to initialize external buffer. + + INPUT PARAMETERS + Model - KNN model which is associated with newly created buffer + + OUTPUT PARAMETERS + Buf - external buffer. + + + IMPORTANT: buffer object should be used only with model which was used to + initialize buffer. Any attempt to use buffer with different + object is dangerous - you may get integrity check failure + (exception) because sizes of internal arrays do not fit to + dimensions of the model structure. -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreate0(int nin, int nout, out multilayerperceptron network) + public static void knncreatebuffer(knnmodel model, out knnbuffer buf) { - network = new multilayerperceptron(); - mlpbase.mlpcreate0(nin, nout, network.innerobj, null); + buf = new knnbuffer(); + knn.knncreatebuffer(model.innerobj, buf.innerobj, null); } - public static void mlpcreate0(int nin, int nout, out multilayerperceptron network, alglib.xparams _params) + public static void knncreatebuffer(knnmodel model, out knnbuffer buf, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreate0(nin, nout, network.innerobj, _params); + buf = new knnbuffer(); + knn.knncreatebuffer(model.innerobj, buf.innerobj, _params); } /************************************************************************* - Same as MLPCreate0, but with one hidden layer (NHid neurons) with - non-linear activation function. Output layer is linear. + This subroutine creates KNNBuilder object which is used to train KNN models. + + By default, new builder stores empty dataset and some reasonable default + settings. At the very least, you should specify dataset prior to building + KNN model. You can also tweak settings of the model construction algorithm + (recommended, although default settings should work well). + + Following actions are mandatory: + * calling knnbuildersetdataset() to specify dataset + * calling knnbuilderbuildknnmodel() to build KNN model using current + dataset and default settings + + Additionally, you may call: + * knnbuildersetnorm() to change norm being used + + INPUT PARAMETERS: + none + + OUTPUT PARAMETERS: + S - KNN builder -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreate1(int nin, int nhid, int nout, out multilayerperceptron network) + public static void knnbuildercreate(out knnbuilder s) { - network = new multilayerperceptron(); - mlpbase.mlpcreate1(nin, nhid, nout, network.innerobj, null); + s = new knnbuilder(); + knn.knnbuildercreate(s.innerobj, null); } - public static void mlpcreate1(int nin, int nhid, int nout, out multilayerperceptron network, alglib.xparams _params) + public static void knnbuildercreate(out knnbuilder s, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreate1(nin, nhid, nout, network.innerobj, _params); + s = new knnbuilder(); + knn.knnbuildercreate(s.innerobj, _params); } /************************************************************************* - Same as MLPCreate0, but with two hidden layers (NHid1 and NHid2 neurons) - with non-linear activation function. Output layer is linear. - $ALL + Specifies regression problem (one or more continuous output variables are + predicted). There also exists "classification" version of this function. + + This subroutine adds dense dataset to the internal storage of the builder + object. Specifying your dataset in the dense format means that the dense + version of the KNN construction algorithm will be invoked. + + INPUT PARAMETERS: + S - KNN builder object + XY - array[NPoints,NVars+NOut] (note: actual size can be + larger, only leading part is used anyway), dataset: + * first NVars elements of each row store values of the + independent variables + * next NOut elements store values of the dependent + variables + NPoints - number of rows in the dataset, NPoints>=1 + NVars - number of independent variables, NVars>=1 + NOut - number of dependent variables, NOut>=1 + + OUTPUT PARAMETERS: + S - KNN builder -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreate2(int nin, int nhid1, int nhid2, int nout, out multilayerperceptron network) + public static void knnbuildersetdatasetreg(knnbuilder s, double[,] xy, int npoints, int nvars, int nout) { - network = new multilayerperceptron(); - mlpbase.mlpcreate2(nin, nhid1, nhid2, nout, network.innerobj, null); + + knn.knnbuildersetdatasetreg(s.innerobj, xy, npoints, nvars, nout, null); } - public static void mlpcreate2(int nin, int nhid1, int nhid2, int nout, out multilayerperceptron network, alglib.xparams _params) + public static void knnbuildersetdatasetreg(knnbuilder s, double[,] xy, int npoints, int nvars, int nout, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreate2(nin, nhid1, nhid2, nout, network.innerobj, _params); + + knn.knnbuildersetdatasetreg(s.innerobj, xy, npoints, nvars, nout, _params); } /************************************************************************* - Creates neural network with NIn inputs, NOut outputs, without hidden - layers with non-linear output layer. Network weights are filled with small - random values. - - Activation function of the output layer takes values: - - (B, +INF), if D>=0 + Specifies classification problem (two or more classes are predicted). + There also exists "regression" version of this function. - or + This subroutine adds dense dataset to the internal storage of the builder + object. Specifying your dataset in the dense format means that the dense + version of the KNN construction algorithm will be invoked. - (-INF, B), if D<0. + INPUT PARAMETERS: + S - KNN builder object + XY - array[NPoints,NVars+1] (note: actual size can be + larger, only leading part is used anyway), dataset: + * first NVars elements of each row store values of the + independent variables + * next element stores class index, in [0,NClasses) + NPoints - number of rows in the dataset, NPoints>=1 + NVars - number of independent variables, NVars>=1 + NClasses - number of classes, NClasses>=2 + OUTPUT PARAMETERS: + S - KNN builder -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreateb0(int nin, int nout, double b, double d, out multilayerperceptron network) + public static void knnbuildersetdatasetcls(knnbuilder s, double[,] xy, int npoints, int nvars, int nclasses) { - network = new multilayerperceptron(); - mlpbase.mlpcreateb0(nin, nout, b, d, network.innerobj, null); + + knn.knnbuildersetdatasetcls(s.innerobj, xy, npoints, nvars, nclasses, null); } - public static void mlpcreateb0(int nin, int nout, double b, double d, out multilayerperceptron network, alglib.xparams _params) + public static void knnbuildersetdatasetcls(knnbuilder s, double[,] xy, int npoints, int nvars, int nclasses, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreateb0(nin, nout, b, d, network.innerobj, _params); + + knn.knnbuildersetdatasetcls(s.innerobj, xy, npoints, nvars, nclasses, _params); } /************************************************************************* - Same as MLPCreateB0 but with non-linear hidden layer. + This function sets norm type used for neighbor search. + + INPUT PARAMETERS: + S - decision forest builder object + NormType - norm type: + * 0 inf-norm + * 1 1-norm + * 2 Euclidean norm (default) + + OUTPUT PARAMETERS: + S - decision forest builder -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreateb1(int nin, int nhid, int nout, double b, double d, out multilayerperceptron network) + public static void knnbuildersetnorm(knnbuilder s, int nrmtype) { - network = new multilayerperceptron(); - mlpbase.mlpcreateb1(nin, nhid, nout, b, d, network.innerobj, null); + + knn.knnbuildersetnorm(s.innerobj, nrmtype, null); } - public static void mlpcreateb1(int nin, int nhid, int nout, double b, double d, out multilayerperceptron network, alglib.xparams _params) + public static void knnbuildersetnorm(knnbuilder s, int nrmtype, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreateb1(nin, nhid, nout, b, d, network.innerobj, _params); + + knn.knnbuildersetnorm(s.innerobj, nrmtype, _params); } /************************************************************************* - Same as MLPCreateB0 but with two non-linear hidden layers. + This subroutine builds KNN model according to current settings, using + dataset internally stored in the builder object. + + The model being built performs inference using Eps-approximate K nearest + neighbors search algorithm, with: + * K=1, Eps=0 corresponding to the "nearest neighbor algorithm" + * K>1, Eps=0 corresponding to the "K nearest neighbors algorithm" + * K>=1, Eps>0 corresponding to "approximate nearest neighbors algorithm" + + An approximate KNN is a good option for high-dimensional datasets (exact + KNN works slowly when dimensions count grows). + + An ALGLIB implementation of kd-trees is used to perform k-nn searches. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. + + INPUT PARAMETERS: + S - KNN builder object + K - number of neighbors to search for, K>=1 + Eps - approximation factor: + * Eps=0 means that exact kNN search is performed + * Eps>0 means that (1+Eps)-approximate search is performed + + OUTPUT PARAMETERS: + Model - KNN model + Rep - report -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreateb2(int nin, int nhid1, int nhid2, int nout, double b, double d, out multilayerperceptron network) + public static void knnbuilderbuildknnmodel(knnbuilder s, int k, double eps, out knnmodel model, out knnreport rep) { - network = new multilayerperceptron(); - mlpbase.mlpcreateb2(nin, nhid1, nhid2, nout, b, d, network.innerobj, null); + model = new knnmodel(); + rep = new knnreport(); + knn.knnbuilderbuildknnmodel(s.innerobj, k, eps, model.innerobj, rep.innerobj, null); } - public static void mlpcreateb2(int nin, int nhid1, int nhid2, int nout, double b, double d, out multilayerperceptron network, alglib.xparams _params) + public static void knnbuilderbuildknnmodel(knnbuilder s, int k, double eps, out knnmodel model, out knnreport rep, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreateb2(nin, nhid1, nhid2, nout, b, d, network.innerobj, _params); + model = new knnmodel(); + rep = new knnreport(); + knn.knnbuilderbuildknnmodel(s.innerobj, k, eps, model.innerobj, rep.innerobj, _params); } /************************************************************************* - Creates neural network with NIn inputs, NOut outputs, without hidden - layers with non-linear output layer. Network weights are filled with small - random values. Activation function of the output layer takes values [A,B]. + Changing search settings of KNN model. + + K and EPS parameters of KNN (AKNN) search are specified during model + construction. However, plain KNN algorithm with Euclidean distance allows + you to change them at any moment. + + NOTE: future versions of KNN model may support advanced versions of KNN, + such as NCA or LMNN. It is possible that such algorithms won't allow + you to change search settings on the fly. If you call this function + for an algorithm which does not support on-the-fly changes, it will + throw an exception. + + INPUT PARAMETERS: + Model - KNN model + K - K>=1, neighbors count + EPS - accuracy of the EPS-approximate NN search. Set to 0.0, if + you want to perform "classic" KNN search. Specify larger + values if you need to speed-up high-dimensional KNN + queries. + + OUTPUT PARAMETERS: + nothing on success, exception on failure -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreater0(int nin, int nout, double a, double b, out multilayerperceptron network) + public static void knnrewritekeps(knnmodel model, int k, double eps) { - network = new multilayerperceptron(); - mlpbase.mlpcreater0(nin, nout, a, b, network.innerobj, null); + + knn.knnrewritekeps(model.innerobj, k, eps, null); } - public static void mlpcreater0(int nin, int nout, double a, double b, out multilayerperceptron network, alglib.xparams _params) + public static void knnrewritekeps(knnmodel model, int k, double eps, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreater0(nin, nout, a, b, network.innerobj, _params); + + knn.knnrewritekeps(model.innerobj, k, eps, _params); } /************************************************************************* - Same as MLPCreateR0, but with non-linear hidden layer. + Inference using KNN model. + + See also knnprocess0(), knnprocessi() and knnclassify() for options with a + bit more convenient interface. + + IMPORTANT: this function is thread-unsafe and modifies internal structures + of the model! You can not use same model object for parallel + evaluation from several threads. + + Use knntsprocess() with independent thread-local buffers, if + you need thread-safe evaluation. + + INPUT PARAMETERS: + Model - KNN model + X - input vector, array[0..NVars-1]. + Y - possible preallocated buffer. Reused if long enough. + + OUTPUT PARAMETERS: + Y - result. Regression estimate when solving regression task, + vector of posterior probabilities for classification task. -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreater1(int nin, int nhid, int nout, double a, double b, out multilayerperceptron network) + public static void knnprocess(knnmodel model, double[] x, ref double[] y) { - network = new multilayerperceptron(); - mlpbase.mlpcreater1(nin, nhid, nout, a, b, network.innerobj, null); + + knn.knnprocess(model.innerobj, x, ref y, null); } - public static void mlpcreater1(int nin, int nhid, int nout, double a, double b, out multilayerperceptron network, alglib.xparams _params) + public static void knnprocess(knnmodel model, double[] x, ref double[] y, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreater1(nin, nhid, nout, a, b, network.innerobj, _params); + + knn.knnprocess(model.innerobj, x, ref y, _params); } /************************************************************************* - Same as MLPCreateR0, but with two non-linear hidden layers. + This function returns first component of the inferred vector (i.e. one + with index #0). + + It is a convenience wrapper for knnprocess() intended for either: + * 1-dimensional regression problems + * 2-class classification problems + + In the former case this function returns inference result as scalar, which + is definitely more convenient that wrapping it as vector. In the latter + case it returns probability of object belonging to class #0. + + If you call it for anything different from two cases above, it will work + as defined, i.e. return y[0], although it is of less use in such cases. + + IMPORTANT: this function is thread-unsafe and modifies internal structures + of the model! You can not use same model object for parallel + evaluation from several threads. + + Use knntsprocess() with independent thread-local buffers, if + you need thread-safe evaluation. + + INPUT PARAMETERS: + Model - KNN model + X - input vector, array[0..NVars-1]. + + RESULT: + Y[0] -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreater2(int nin, int nhid1, int nhid2, int nout, double a, double b, out multilayerperceptron network) + public static double knnprocess0(knnmodel model, double[] x) { - network = new multilayerperceptron(); - mlpbase.mlpcreater2(nin, nhid1, nhid2, nout, a, b, network.innerobj, null); + + return knn.knnprocess0(model.innerobj, x, null); } - public static void mlpcreater2(int nin, int nhid1, int nhid2, int nout, double a, double b, out multilayerperceptron network, alglib.xparams _params) + public static double knnprocess0(knnmodel model, double[] x, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreater2(nin, nhid1, nhid2, nout, a, b, network.innerobj, _params); + + return knn.knnprocess0(model.innerobj, x, _params); } /************************************************************************* - Creates classifier network with NIn inputs and NOut possible classes. - Network contains no hidden layers and linear output layer with SOFTMAX- - normalization (so outputs sums up to 1.0 and converge to posterior - probabilities). + This function returns most probable class number for an input X. It is + same as calling knnprocess(model,x,y), then determining i=argmax(y[i]) and + returning i. + + A class number in [0,NOut) range in returned for classification problems, + -1 is returned when this function is called for regression problems. + + IMPORTANT: this function is thread-unsafe and modifies internal structures + of the model! You can not use same model object for parallel + evaluation from several threads. + + Use knntsprocess() with independent thread-local buffers, if + you need thread-safe evaluation. + + INPUT PARAMETERS: + Model - KNN model + X - input vector, array[0..NVars-1]. + + RESULT: + class number, -1 for regression tasks -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcreatec0(int nin, int nout, out multilayerperceptron network) + public static int knnclassify(knnmodel model, double[] x) { - network = new multilayerperceptron(); - mlpbase.mlpcreatec0(nin, nout, network.innerobj, null); + + return knn.knnclassify(model.innerobj, x, null); } - public static void mlpcreatec0(int nin, int nout, out multilayerperceptron network, alglib.xparams _params) + public static int knnclassify(knnmodel model, double[] x, alglib.xparams _params) { - network = new multilayerperceptron(); - mlpbase.mlpcreatec0(nin, nout, network.innerobj, _params); + + return knn.knnclassify(model.innerobj, x, _params); } /************************************************************************* - Same as MLPCreateC0, but with one non-linear hidden layer. - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreatec1(int nin, int nhid, int nout, out multilayerperceptron network) - { - network = new multilayerperceptron(); - mlpbase.mlpcreatec1(nin, nhid, nout, network.innerobj, null); - } - - public static void mlpcreatec1(int nin, int nhid, int nout, out multilayerperceptron network, alglib.xparams _params) - { - network = new multilayerperceptron(); - mlpbase.mlpcreatec1(nin, nhid, nout, network.innerobj, _params); - } - - /************************************************************************* - Same as MLPCreateC0, but with two non-linear hidden layers. + 'interactive' variant of knnprocess() for languages like Python which + support constructs like "y = knnprocessi(model,x)" and interactive mode of + the interpreter. - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreatec2(int nin, int nhid1, int nhid2, int nout, out multilayerperceptron network) - { - network = new multilayerperceptron(); - mlpbase.mlpcreatec2(nin, nhid1, nhid2, nout, network.innerobj, null); - } - - public static void mlpcreatec2(int nin, int nhid1, int nhid2, int nout, out multilayerperceptron network, alglib.xparams _params) - { - network = new multilayerperceptron(); - mlpbase.mlpcreatec2(nin, nhid1, nhid2, nout, network.innerobj, _params); - } - - /************************************************************************* - Copying of neural network + This function allocates new array on each call, so it is significantly + slower than its 'non-interactive' counterpart, but it is more convenient + when you call it from command line. - INPUT PARAMETERS: - Network1 - original + IMPORTANT: this function is thread-unsafe and may modify internal + structures of the model! You can not use same model object for + parallel evaluation from several threads. - OUTPUT PARAMETERS: - Network2 - copy + Use knntsprocess() with independent thread-local buffers if + you need thread-safe evaluation. -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcopy(multilayerperceptron network1, out multilayerperceptron network2) + public static void knnprocessi(knnmodel model, double[] x, out double[] y) { - network2 = new multilayerperceptron(); - mlpbase.mlpcopy(network1.innerobj, network2.innerobj, null); + y = new double[0]; + knn.knnprocessi(model.innerobj, x, ref y, null); } - public static void mlpcopy(multilayerperceptron network1, out multilayerperceptron network2, alglib.xparams _params) + public static void knnprocessi(knnmodel model, double[] x, out double[] y, alglib.xparams _params) { - network2 = new multilayerperceptron(); - mlpbase.mlpcopy(network1.innerobj, network2.innerobj, _params); + y = new double[0]; + knn.knnprocessi(model.innerobj, x, ref y, _params); } /************************************************************************* - This function copies tunable parameters (weights/means/sigmas) from one - network to another with same architecture. It performs some rudimentary - checks that architectures are same, and throws exception if check fails. + Thread-safe procesing using external buffer for temporaries. - It is intended for fast copying of states between two network which are - known to have same geometry. + This function is thread-safe (i.e . you can use same KNN model from + multiple threads) as long as you use different buffer objects for different + threads. INPUT PARAMETERS: - Network1 - source, must be correctly initialized - Network2 - target, must have same architecture + Model - KNN model + Buf - buffer object, must be allocated specifically for this + model with knncreatebuffer(). + X - input vector, array[NVars] OUTPUT PARAMETERS: - Network2 - network state is copied from source to target + Y - result, array[NOut]. Regression estimate when solving + regression task, vector of posterior probabilities for + a classification task. -- ALGLIB -- - Copyright 20.06.2013 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpcopytunableparameters(multilayerperceptron network1, multilayerperceptron network2) + public static void knntsprocess(knnmodel model, knnbuffer buf, double[] x, ref double[] y) { - mlpbase.mlpcopytunableparameters(network1.innerobj, network2.innerobj, null); + knn.knntsprocess(model.innerobj, buf.innerobj, x, ref y, null); } - public static void mlpcopytunableparameters(multilayerperceptron network1, multilayerperceptron network2, alglib.xparams _params) + public static void knntsprocess(knnmodel model, knnbuffer buf, double[] x, ref double[] y, alglib.xparams _params) { - mlpbase.mlpcopytunableparameters(network1.innerobj, network2.innerobj, _params); + knn.knntsprocess(model.innerobj, buf.innerobj, x, ref y, _params); } /************************************************************************* - Randomization of neural network weights + Relative classification error on the test set - -- ALGLIB -- - Copyright 06.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlprandomize(multilayerperceptron network) - { - - mlpbase.mlprandomize(network.innerobj, null); - } - - public static void mlprandomize(multilayerperceptron network, alglib.xparams _params) - { - - mlpbase.mlprandomize(network.innerobj, _params); - } - - /************************************************************************* - Randomization of neural network weights and standartisator + INPUT PARAMETERS: + Model - KNN model + XY - test set + NPoints - test set size - -- ALGLIB -- - Copyright 10.03.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mlprandomizefull(multilayerperceptron network) - { - - mlpbase.mlprandomizefull(network.innerobj, null); - } - - public static void mlprandomizefull(multilayerperceptron network, alglib.xparams _params) - { - - mlpbase.mlprandomizefull(network.innerobj, _params); - } - - /************************************************************************* - Internal subroutine. + RESULT: + percent of incorrectly classified cases. + Zero if model solves regression task. + + NOTE: if you need several different kinds of error metrics, it is better + to use knnallerrors() which computes all error metric with just one + pass over dataset. -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpinitpreprocessor(multilayerperceptron network, double[,] xy, int ssize) + public static double knnrelclserror(knnmodel model, double[,] xy, int npoints) { - mlpbase.mlpinitpreprocessor(network.innerobj, xy, ssize, null); + return knn.knnrelclserror(model.innerobj, xy, npoints, null); } - public static void mlpinitpreprocessor(multilayerperceptron network, double[,] xy, int ssize, alglib.xparams _params) + public static double knnrelclserror(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) { - mlpbase.mlpinitpreprocessor(network.innerobj, xy, ssize, _params); + return knn.knnrelclserror(model.innerobj, xy, npoints, _params); } /************************************************************************* - Returns information about initialized network: number of inputs, outputs, - weights. + Average cross-entropy (in bits per element) on the test set - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpproperties(multilayerperceptron network, out int nin, out int nout, out int wcount) - { - nin = 0; - nout = 0; - wcount = 0; - mlpbase.mlpproperties(network.innerobj, ref nin, ref nout, ref wcount, null); - } - - public static void mlpproperties(multilayerperceptron network, out int nin, out int nout, out int wcount, alglib.xparams _params) - { - nin = 0; - nout = 0; - wcount = 0; - mlpbase.mlpproperties(network.innerobj, ref nin, ref nout, ref wcount, _params); - } - - /************************************************************************* - Returns number of inputs. + INPUT PARAMETERS: + Model - KNN model + XY - test set + NPoints - test set size - -- ALGLIB -- - Copyright 19.10.2011 by Bochkanov Sergey - *************************************************************************/ - public static int mlpgetinputscount(multilayerperceptron network) - { - - return mlpbase.mlpgetinputscount(network.innerobj, null); - } - - public static int mlpgetinputscount(multilayerperceptron network, alglib.xparams _params) - { - - return mlpbase.mlpgetinputscount(network.innerobj, _params); - } - - /************************************************************************* - Returns number of outputs. + RESULT: + CrossEntropy/NPoints. + Zero if model solves regression task. - -- ALGLIB -- - Copyright 19.10.2011 by Bochkanov Sergey - *************************************************************************/ - public static int mlpgetoutputscount(multilayerperceptron network) - { - - return mlpbase.mlpgetoutputscount(network.innerobj, null); - } - - public static int mlpgetoutputscount(multilayerperceptron network, alglib.xparams _params) - { - - return mlpbase.mlpgetoutputscount(network.innerobj, _params); - } - - /************************************************************************* - Returns number of weights. + NOTE: the cross-entropy metric is too unstable when used to evaluate KNN + models (such models can report exactly zero probabilities), so we + do not recommend using it. - -- ALGLIB -- - Copyright 19.10.2011 by Bochkanov Sergey - *************************************************************************/ - public static int mlpgetweightscount(multilayerperceptron network) - { - - return mlpbase.mlpgetweightscount(network.innerobj, null); - } - - public static int mlpgetweightscount(multilayerperceptron network, alglib.xparams _params) - { - - return mlpbase.mlpgetweightscount(network.innerobj, _params); - } - - /************************************************************************* - Tells whether network is SOFTMAX-normalized (i.e. classifier) or not. + NOTE: if you need several different kinds of error metrics, it is better + to use knnallerrors() which computes all error metric with just one + pass over dataset. -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static bool mlpissoftmax(multilayerperceptron network) + public static double knnavgce(knnmodel model, double[,] xy, int npoints) { - return mlpbase.mlpissoftmax(network.innerobj, null); + return knn.knnavgce(model.innerobj, xy, npoints, null); } - public static bool mlpissoftmax(multilayerperceptron network, alglib.xparams _params) + public static double knnavgce(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) { - return mlpbase.mlpissoftmax(network.innerobj, _params); + return knn.knnavgce(model.innerobj, xy, npoints, _params); } /************************************************************************* - This function returns total number of layers (including input, hidden and - output layers). + RMS error on the test set. - -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey - *************************************************************************/ - public static int mlpgetlayerscount(multilayerperceptron network) - { - - return mlpbase.mlpgetlayerscount(network.innerobj, null); - } - - public static int mlpgetlayerscount(multilayerperceptron network, alglib.xparams _params) - { - - return mlpbase.mlpgetlayerscount(network.innerobj, _params); - } - - /************************************************************************* - This function returns size of K-th layer. + Its meaning for regression task is obvious. As for classification problems, + RMS error means error when estimating posterior probabilities. - K=0 corresponds to input layer, K=CNT-1 corresponds to output layer. + INPUT PARAMETERS: + Model - KNN model + XY - test set + NPoints - test set size - Size of the output layer is always equal to the number of outputs, although - when we have softmax-normalized network, last neuron doesn't have any - connections - it is just zero. + RESULT: + root mean square error. + + NOTE: if you need several different kinds of error metrics, it is better + to use knnallerrors() which computes all error metric with just one + pass over dataset. -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static int mlpgetlayersize(multilayerperceptron network, int k) + public static double knnrmserror(knnmodel model, double[,] xy, int npoints) { - return mlpbase.mlpgetlayersize(network.innerobj, k, null); + return knn.knnrmserror(model.innerobj, xy, npoints, null); } - public static int mlpgetlayersize(multilayerperceptron network, int k, alglib.xparams _params) + public static double knnrmserror(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) { - return mlpbase.mlpgetlayersize(network.innerobj, k, _params); + return knn.knnrmserror(model.innerobj, xy, npoints, _params); } /************************************************************************* - This function returns offset/scaling coefficients for I-th input of the - network. + Average error on the test set + + Its meaning for regression task is obvious. As for classification problems, + average error means error when estimating posterior probabilities. INPUT PARAMETERS: - Network - network - I - input index + Model - KNN model + XY - test set + NPoints - test set size - OUTPUT PARAMETERS: - Mean - mean term - Sigma - sigma term, guaranteed to be nonzero. + RESULT: + average error - I-th input is passed through linear transformation - IN[i] = (IN[i]-Mean)/Sigma - before feeding to the network + NOTE: if you need several different kinds of error metrics, it is better + to use knnallerrors() which computes all error metric with just one + pass over dataset. -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpgetinputscaling(multilayerperceptron network, int i, out double mean, out double sigma) + public static double knnavgerror(knnmodel model, double[,] xy, int npoints) { - mean = 0; - sigma = 0; - mlpbase.mlpgetinputscaling(network.innerobj, i, ref mean, ref sigma, null); + + return knn.knnavgerror(model.innerobj, xy, npoints, null); } - public static void mlpgetinputscaling(multilayerperceptron network, int i, out double mean, out double sigma, alglib.xparams _params) + public static double knnavgerror(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) { - mean = 0; - sigma = 0; - mlpbase.mlpgetinputscaling(network.innerobj, i, ref mean, ref sigma, _params); + + return knn.knnavgerror(model.innerobj, xy, npoints, _params); } /************************************************************************* - This function returns offset/scaling coefficients for I-th output of the - network. + Average relative error on the test set + + Its meaning for regression task is obvious. As for classification problems, + average relative error means error when estimating posterior probabilities. INPUT PARAMETERS: - Network - network - I - input index + Model - KNN model + XY - test set + NPoints - test set size - OUTPUT PARAMETERS: - Mean - mean term - Sigma - sigma term, guaranteed to be nonzero. + RESULT: + average relative error - I-th output is passed through linear transformation - OUT[i] = OUT[i]*Sigma+Mean - before returning it to user. In case we have SOFTMAX-normalized network, - we return (Mean,Sigma)=(0.0,1.0). + NOTE: if you need several different kinds of error metrics, it is better + to use knnallerrors() which computes all error metric with just one + pass over dataset. -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpgetoutputscaling(multilayerperceptron network, int i, out double mean, out double sigma) + public static double knnavgrelerror(knnmodel model, double[,] xy, int npoints) { - mean = 0; - sigma = 0; - mlpbase.mlpgetoutputscaling(network.innerobj, i, ref mean, ref sigma, null); + + return knn.knnavgrelerror(model.innerobj, xy, npoints, null); } - public static void mlpgetoutputscaling(multilayerperceptron network, int i, out double mean, out double sigma, alglib.xparams _params) + public static double knnavgrelerror(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) { - mean = 0; - sigma = 0; - mlpbase.mlpgetoutputscaling(network.innerobj, i, ref mean, ref sigma, _params); + + return knn.knnavgrelerror(model.innerobj, xy, npoints, _params); } /************************************************************************* - This function returns information about Ith neuron of Kth layer + Calculates all kinds of errors for the model in one call. INPUT PARAMETERS: - Network - network - K - layer index - I - neuron index (within layer) + Model - KNN model + XY - test set: + * one row per point + * first NVars columns store independent variables + * depending on problem type: + * next column stores class number in [0,NClasses) - for + classification problems + * next NOut columns store dependent variables - for + regression problems + NPoints - test set size, NPoints>=0 OUTPUT PARAMETERS: - FKind - activation function type (used by MLPActivationFunction()) - this value is zero for input or linear neurons - Threshold - also called offset, bias - zero for input neurons + Rep - following fields are loaded with errors for both regression + and classification models: + * rep.rmserror - RMS error for the output + * rep.avgerror - average error + * rep.avgrelerror - average relative error + following fields are set only for classification models, + zero for regression ones: + * relclserror - relative classification error, in [0,1] + * avgce - average cross-entropy in bits per dataset entry - NOTE: this function throws exception if layer or neuron with given index - do not exists. + NOTE: the cross-entropy metric is too unstable when used to evaluate KNN + models (such models can report exactly zero probabilities), so we + do not recommend using it. -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpgetneuroninfo(multilayerperceptron network, int k, int i, out int fkind, out double threshold) + public static void knnallerrors(knnmodel model, double[,] xy, int npoints, out knnreport rep) { - fkind = 0; - threshold = 0; - mlpbase.mlpgetneuroninfo(network.innerobj, k, i, ref fkind, ref threshold, null); + rep = new knnreport(); + knn.knnallerrors(model.innerobj, xy, npoints, rep.innerobj, null); } - public static void mlpgetneuroninfo(multilayerperceptron network, int k, int i, out int fkind, out double threshold, alglib.xparams _params) + public static void knnallerrors(knnmodel model, double[,] xy, int npoints, out knnreport rep, alglib.xparams _params) { - fkind = 0; - threshold = 0; - mlpbase.mlpgetneuroninfo(network.innerobj, k, i, ref fkind, ref threshold, _params); + rep = new knnreport(); + knn.knnallerrors(model.innerobj, xy, npoints, rep.innerobj, _params); } - - /************************************************************************* - This function returns information about connection from I0-th neuron of - K0-th layer to I1-th neuron of K1-th layer. - INPUT PARAMETERS: - Network - network - K0 - layer index - I0 - neuron index (within layer) - K1 - layer index - I1 - neuron index (within layer) +} +public partial class alglib +{ - RESULT: - connection weight (zero for non-existent connections) - This function: - 1. throws exception if layer or neuron with given index do not exists. - 2. returns zero if neurons exist, but there is no connection between them + /************************************************************************* + A random forest (decision forest) builder object. - -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Used to store dataset and specify decision forest training algorithm settings. *************************************************************************/ - public static double mlpgetweight(multilayerperceptron network, int k0, int i0, int k1, int i1) + public class decisionforestbuilder : alglibobject { + // + // Public declarations + // - return mlpbase.mlpgetweight(network.innerobj, k0, i0, k1, i1, null); - } - - public static double mlpgetweight(multilayerperceptron network, int k0, int i0, int k1, int i1, alglib.xparams _params) - { + public decisionforestbuilder() + { + _innerobj = new dforest.decisionforestbuilder(); + } + + public override alglib.alglibobject make_copy() + { + return new decisionforestbuilder((dforest.decisionforestbuilder)_innerobj.make_copy()); + } - return mlpbase.mlpgetweight(network.innerobj, k0, i0, k1, i1, _params); + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private dforest.decisionforestbuilder _innerobj; + public dforest.decisionforestbuilder innerobj { get { return _innerobj; } } + public decisionforestbuilder(dforest.decisionforestbuilder obj) + { + _innerobj = obj; + } } - - /************************************************************************* - This function sets offset/scaling coefficients for I-th input of the - network. - INPUT PARAMETERS: - Network - network - I - input index - Mean - mean term - Sigma - sigma term (if zero, will be replaced by 1.0) - NTE: I-th input is passed through linear transformation - IN[i] = (IN[i]-Mean)/Sigma - before feeding to the network. This function sets Mean and Sigma. + /************************************************************************* + Buffer object which is used to perform various requests (usually model + inference) in the multithreaded mode (multiple threads working with same + DF object). - -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + This object should be created with DFCreateBuffer(). *************************************************************************/ - public static void mlpsetinputscaling(multilayerperceptron network, int i, double mean, double sigma) + public class decisionforestbuffer : alglibobject { + // + // Public declarations + // - mlpbase.mlpsetinputscaling(network.innerobj, i, mean, sigma, null); - } - - public static void mlpsetinputscaling(multilayerperceptron network, int i, double mean, double sigma, alglib.xparams _params) - { + public decisionforestbuffer() + { + _innerobj = new dforest.decisionforestbuffer(); + } + + public override alglib.alglibobject make_copy() + { + return new decisionforestbuffer((dforest.decisionforestbuffer)_innerobj.make_copy()); + } - mlpbase.mlpsetinputscaling(network.innerobj, i, mean, sigma, _params); + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private dforest.decisionforestbuffer _innerobj; + public dforest.decisionforestbuffer innerobj { get { return _innerobj; } } + public decisionforestbuffer(dforest.decisionforestbuffer obj) + { + _innerobj = obj; + } } + + + /************************************************************************* + Decision forest (random forest) model. + *************************************************************************/ + public class decisionforest : alglibobject + { + // + // Public declarations + // + + public decisionforest() + { + _innerobj = new dforest.decisionforest(); + } + + public override alglib.alglibobject make_copy() + { + return new decisionforest((dforest.decisionforest)_innerobj.make_copy()); + } + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private dforest.decisionforest _innerobj; + public dforest.decisionforest innerobj { get { return _innerobj; } } + public decisionforest(dforest.decisionforest obj) + { + _innerobj = obj; + } + } + + /************************************************************************* - This function sets offset/scaling coefficients for I-th output of the - network. + Decision forest training report. - INPUT PARAMETERS: - Network - network - I - input index - Mean - mean term - Sigma - sigma term (if zero, will be replaced by 1.0) + === training/oob errors ================================================== - OUTPUT PARAMETERS: + Following fields store training set errors: + * relclserror - fraction of misclassified cases, [0,1] + * avgce - average cross-entropy in bits per symbol + * rmserror - root-mean-square error + * avgerror - average error + * avgrelerror - average relative error - NOTE: I-th output is passed through linear transformation - OUT[i] = OUT[i]*Sigma+Mean - before returning it to user. This function sets Sigma/Mean. In case we - have SOFTMAX-normalized network, you can not set (Sigma,Mean) to anything - other than(0.0,1.0) - this function will throw exception. + Out-of-bag estimates are stored in fields with same names, but "oob" prefix. - -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + For classification problems: + * RMS, AVG and AVGREL errors are calculated for posterior probabilities + + For regression problems: + * RELCLS and AVGCE errors are zero + + === variable importance ================================================== + + Following fields are used to store variable importance information: + + * topvars - variables ordered from the most important to + less important ones (according to current + choice of importance raiting). + For example, topvars[0] contains index of the + most important variable, and topvars[0:2] are + indexes of 3 most important ones and so on. + + * varimportances - array[nvars], ratings (the larger, the more + important the variable is, always in [0,1] + range). + By default, filled by zeros (no importance + ratings are provided unless you explicitly + request them). + Zero rating means that variable is not important, + however you will rarely encounter such a thing, + in many cases unimportant variables produce + nearly-zero (but nonzero) ratings. + + Variable importance report must be EXPLICITLY requested by calling: + * dfbuildersetimportancegini() function, if you need out-of-bag Gini-based + importance rating also known as MDI (fast to calculate, resistant to + overfitting issues, but has some bias towards continuous and + high-cardinality categorical variables) + * dfbuildersetimportancetrngini() function, if you need training set Gini- + -based importance rating (what other packages typically report). + * dfbuildersetimportancepermutation() function, if you need permutation- + based importance rating also known as MDA (slower to calculate, but less + biased) + * dfbuildersetimportancenone() function, if you do not need importance + ratings - ratings will be zero, topvars[] will be [0,1,2,...] + + Different importance ratings (Gini or permutation) produce non-comparable + values. Although in all cases rating values lie in [0,1] range, there are + exist differences: + * informally speaking, Gini importance rating tends to divide "unit amount + of importance" between several important variables, i.e. it produces + estimates which roughly sum to 1.0 (or less than 1.0, if your task can + not be solved exactly). If all variables are equally important, they + will have same rating, roughly 1/NVars, even if every variable is + critically important. + * from the other side, permutation importance tells us what percentage of + the model predictive power will be ruined by permuting this specific + variable. It does not produce estimates which sum to one. Critically + important variable will have rating close to 1.0, and you may have + multiple variables with such a rating. + + More information on variable importance ratings can be found in comments + on the dfbuildersetimportancegini() and dfbuildersetimportancepermutation() + functions. *************************************************************************/ - public static void mlpsetoutputscaling(multilayerperceptron network, int i, double mean, double sigma) + public class dfreport : alglibobject { + // + // Public declarations + // + public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } + public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } + public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } + public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } + public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } + public double oobrelclserror { get { return _innerobj.oobrelclserror; } set { _innerobj.oobrelclserror = value; } } + public double oobavgce { get { return _innerobj.oobavgce; } set { _innerobj.oobavgce = value; } } + public double oobrmserror { get { return _innerobj.oobrmserror; } set { _innerobj.oobrmserror = value; } } + public double oobavgerror { get { return _innerobj.oobavgerror; } set { _innerobj.oobavgerror = value; } } + public double oobavgrelerror { get { return _innerobj.oobavgrelerror; } set { _innerobj.oobavgrelerror = value; } } + public int[] topvars { get { return _innerobj.topvars; } set { _innerobj.topvars = value; } } + public double[] varimportances { get { return _innerobj.varimportances; } set { _innerobj.varimportances = value; } } - mlpbase.mlpsetoutputscaling(network.innerobj, i, mean, sigma, null); + public dfreport() + { + _innerobj = new dforest.dfreport(); + } + + public override alglib.alglibobject make_copy() + { + return new dfreport((dforest.dfreport)_innerobj.make_copy()); + } + + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private dforest.dfreport _innerobj; + public dforest.dfreport innerobj { get { return _innerobj; } } + public dfreport(dforest.dfreport obj) + { + _innerobj = obj; + } } + + + /************************************************************************* + This function serializes data structure to string/stream. - public static void mlpsetoutputscaling(multilayerperceptron network, int i, double mean, double sigma, alglib.xparams _params) + Important properties of s_out: + * it contains alphanumeric characters, dots, underscores, minus signs + * these symbols are grouped into words, which are separated by spaces + and Windows-style (CR+LF) newlines + * although serializer uses spaces and CR+LF as separators, you can + replace any separator character by arbitrary combination of spaces, + tabs, Windows or Unix newlines. It allows flexible reformatting of + the string in case you want to include it into a text or XML file. + But you should not insert separators into the middle of the "words" + nor should you change the case of letters. + * s_out can be freely moved between 32-bit and 64-bit systems, little + and big endian machines, and so on. You can serialize structure on + 32-bit machine and unserialize it on 64-bit one (or vice versa), or + serialize it on SPARC and unserialize on x86. You can also + serialize it in C++ version of ALGLIB and unserialize it in C# one, + and vice versa. + *************************************************************************/ + public static void dfserialize(decisionforest obj, out string s_out) + { + alglib.serializer s = new alglib.serializer(); + s.alloc_start(); + dforest.dfalloc(s, obj.innerobj, null); + s.sstart_str(); + dforest.dfserialize(s, obj.innerobj, null); + s.stop(); + s_out = s.get_string(); + } + + + /************************************************************************* + This function serializes data structure to string/stream. + + Important properties of s_out: + * it contains alphanumeric characters, dots, underscores, minus signs + * these symbols are grouped into words, which are separated by spaces + and Windows-style (CR+LF) newlines + * although serializer uses spaces and CR+LF as separators, you can + replace any separator character by arbitrary combination of spaces, + tabs, Windows or Unix newlines. It allows flexible reformatting of + the string in case you want to include it into a text or XML file. + But you should not insert separators into the middle of the "words" + nor should you change the case of letters. + * s_out can be freely moved between 32-bit and 64-bit systems, little + and big endian machines, and so on. You can serialize structure on + 32-bit machine and unserialize it on 64-bit one (or vice versa), or + serialize it on SPARC and unserialize on x86. You can also + serialize it in C++ version of ALGLIB and unserialize it in C# one, + and vice versa. + *************************************************************************/ + public static void dfserialize(decisionforest obj, System.IO.Stream stream_out) + { + alglib.serializer s = new alglib.serializer(); + s.alloc_start(); + dforest.dfalloc(s, obj.innerobj, null); + s.sstart_stream(stream_out); + dforest.dfserialize(s, obj.innerobj, null); + s.stop(); + } + + + /************************************************************************* + This function unserializes data structure from string/stream. + *************************************************************************/ + public static void dfunserialize(string s_in, out decisionforest obj) { + alglib.serializer s = new alglib.serializer(); + obj = new decisionforest(); + s.ustart_str(s_in); + dforest.dfunserialize(s, obj.innerobj, null); + s.stop(); + } + + + /************************************************************************* + This function unserializes data structure from string/stream. + *************************************************************************/ + public static void dfunserialize(System.IO.Stream stream_in, out decisionforest obj) - mlpbase.mlpsetoutputscaling(network.innerobj, i, mean, sigma, _params); + { + alglib.serializer s = new alglib.serializer(); + obj = new decisionforest(); + s.ustart_stream(stream_in); + dforest.dfunserialize(s, obj.innerobj, null); + s.stop(); } /************************************************************************* - This function modifies information about Ith neuron of Kth layer + This function creates buffer structure which can be used to perform + parallel inference requests. - INPUT PARAMETERS: - Network - network - K - layer index - I - neuron index (within layer) - FKind - activation function type (used by MLPActivationFunction()) - this value must be zero for input neurons - (you can not set activation function for input neurons) - Threshold - also called offset, bias - this value must be zero for input neurons - (you can not set threshold for input neurons) + DF subpackage provides two sets of computing functions - ones which use + internal buffer of DF model (these functions are single-threaded because + they use same buffer, which can not shared between threads), and ones + which use external buffer. - NOTES: - 1. this function throws exception if layer or neuron with given index do - not exists. - 2. this function also throws exception when you try to set non-linear - activation function for input neurons (any kind of network) or for output - neurons of classifier network. - 3. this function throws exception when you try to set non-zero threshold for - input neurons (any kind of network). + This function is used to initialize external buffer. + + INPUT PARAMETERS + Model - DF model which is associated with newly created buffer + + OUTPUT PARAMETERS + Buf - external buffer. + + + IMPORTANT: buffer object should be used only with model which was used to + initialize buffer. Any attempt to use buffer with different + object is dangerous - you may get integrity check failure + (exception) because sizes of internal arrays do not fit to + dimensions of the model structure. -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpsetneuroninfo(multilayerperceptron network, int k, int i, int fkind, double threshold) + public static void dfcreatebuffer(decisionforest model, out decisionforestbuffer buf) { - - mlpbase.mlpsetneuroninfo(network.innerobj, k, i, fkind, threshold, null); + buf = new decisionforestbuffer(); + dforest.dfcreatebuffer(model.innerobj, buf.innerobj, null); } - public static void mlpsetneuroninfo(multilayerperceptron network, int k, int i, int fkind, double threshold, alglib.xparams _params) + public static void dfcreatebuffer(decisionforest model, out decisionforestbuffer buf, alglib.xparams _params) { - - mlpbase.mlpsetneuroninfo(network.innerobj, k, i, fkind, threshold, _params); + buf = new decisionforestbuffer(); + dforest.dfcreatebuffer(model.innerobj, buf.innerobj, _params); } /************************************************************************* - This function modifies information about connection from I0-th neuron of - K0-th layer to I1-th neuron of K1-th layer. + This subroutine creates DecisionForestBuilder object which is used to + train decision forests. + + By default, new builder stores empty dataset and some reasonable default + settings. At the very least, you should specify dataset prior to building + decision forest. You can also tweak settings of the forest construction + algorithm (recommended, although default setting should work well). + + Following actions are mandatory: + * calling dfbuildersetdataset() to specify dataset + * calling dfbuilderbuildrandomforest() to build decision forest using + current dataset and default settings + + Additionally, you may call: + * dfbuildersetrndvars() or dfbuildersetrndvarsratio() to specify number of + variables randomly chosen for each split + * dfbuildersetsubsampleratio() to specify fraction of the dataset randomly + subsampled to build each tree + * dfbuildersetseed() to control random seed chosen for tree construction INPUT PARAMETERS: - Network - network - K0 - layer index - I0 - neuron index (within layer) - K1 - layer index - I1 - neuron index (within layer) - W - connection weight (must be zero for non-existent - connections) + none - This function: - 1. throws exception if layer or neuron with given index do not exists. - 2. throws exception if you try to set non-zero weight for non-existent - connection + OUTPUT PARAMETERS: + S - decision forest builder -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpsetweight(multilayerperceptron network, int k0, int i0, int k1, int i1, double w) + public static void dfbuildercreate(out decisionforestbuilder s) { - - mlpbase.mlpsetweight(network.innerobj, k0, i0, k1, i1, w, null); + s = new decisionforestbuilder(); + dforest.dfbuildercreate(s.innerobj, null); } - public static void mlpsetweight(multilayerperceptron network, int k0, int i0, int k1, int i1, double w, alglib.xparams _params) + public static void dfbuildercreate(out decisionforestbuilder s, alglib.xparams _params) { - - mlpbase.mlpsetweight(network.innerobj, k0, i0, k1, i1, w, _params); + s = new decisionforestbuilder(); + dforest.dfbuildercreate(s.innerobj, _params); } /************************************************************************* - Neural network activation function + This subroutine adds dense dataset to the internal storage of the builder + object. Specifying your dataset in the dense format means that the dense + version of the forest construction algorithm will be invoked. INPUT PARAMETERS: - NET - neuron input - K - function index (zero for linear function) + S - decision forest builder object + XY - array[NPoints,NVars+1] (minimum size; actual size can + be larger, only leading part is used anyway), dataset: + * first NVars elements of each row store values of the + independent variables + * last column store class number (in 0...NClasses-1) + or real value of the dependent variable + NPoints - number of rows in the dataset, NPoints>=1 + NVars - number of independent variables, NVars>=1 + NClasses - indicates type of the problem being solved: + * NClasses>=2 means that classification problem is + solved (last column of the dataset stores class + number) + * NClasses=1 means that regression problem is solved + (last column of the dataset stores variable value) OUTPUT PARAMETERS: - F - function - DF - its derivative - D2F - its second derivative + S - decision forest builder -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpactivationfunction(double net, int k, out double f, out double df, out double d2f) + public static void dfbuildersetdataset(decisionforestbuilder s, double[,] xy, int npoints, int nvars, int nclasses) { - f = 0; - df = 0; - d2f = 0; - mlpbase.mlpactivationfunction(net, k, ref f, ref df, ref d2f, null); + + dforest.dfbuildersetdataset(s.innerobj, xy, npoints, nvars, nclasses, null); } - public static void mlpactivationfunction(double net, int k, out double f, out double df, out double d2f, alglib.xparams _params) + public static void dfbuildersetdataset(decisionforestbuilder s, double[,] xy, int npoints, int nvars, int nclasses, alglib.xparams _params) { - f = 0; - df = 0; - d2f = 0; - mlpbase.mlpactivationfunction(net, k, ref f, ref df, ref d2f, _params); + + dforest.dfbuildersetdataset(s.innerobj, xy, npoints, nvars, nclasses, _params); } /************************************************************************* - Procesing + This function sets number of variables (in [1,NVars] range) used by + decision forest construction algorithm. + + The default option is to use roughly sqrt(NVars) variables. INPUT PARAMETERS: - Network - neural network - X - input vector, array[0..NIn-1]. + S - decision forest builder object + RndVars - number of randomly selected variables; values outside + of [1,NVars] range are silently clipped. OUTPUT PARAMETERS: - Y - result. Regression estimate when solving regression task, - vector of posterior probabilities for classification task. - - See also MLPProcessI + S - decision forest builder -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpprocess(multilayerperceptron network, double[] x, ref double[] y) + public static void dfbuildersetrndvars(decisionforestbuilder s, int rndvars) { - mlpbase.mlpprocess(network.innerobj, x, ref y, null); + dforest.dfbuildersetrndvars(s.innerobj, rndvars, null); } - public static void mlpprocess(multilayerperceptron network, double[] x, ref double[] y, alglib.xparams _params) + public static void dfbuildersetrndvars(decisionforestbuilder s, int rndvars, alglib.xparams _params) { - mlpbase.mlpprocess(network.innerobj, x, ref y, _params); + dforest.dfbuildersetrndvars(s.innerobj, rndvars, _params); } /************************************************************************* - 'interactive' variant of MLPProcess for languages like Python which - support constructs like "Y = MLPProcess(NN,X)" and interactive mode of the - interpreter + This function sets number of variables used by decision forest construction + algorithm as a fraction of total variable count (0,1) range. - This function allocates new array on each call, so it is significantly - slower than its 'non-interactive' counterpart, but it is more convenient - when you call it from command line. + The default option is to use roughly sqrt(NVars) variables. + + INPUT PARAMETERS: + S - decision forest builder object + F - round(NVars*F) variables are selected + + OUTPUT PARAMETERS: + S - decision forest builder -- ALGLIB -- - Copyright 21.09.2010 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpprocessi(multilayerperceptron network, double[] x, out double[] y) + public static void dfbuildersetrndvarsratio(decisionforestbuilder s, double f) { - y = new double[0]; - mlpbase.mlpprocessi(network.innerobj, x, ref y, null); + + dforest.dfbuildersetrndvarsratio(s.innerobj, f, null); } - public static void mlpprocessi(multilayerperceptron network, double[] x, out double[] y, alglib.xparams _params) + public static void dfbuildersetrndvarsratio(decisionforestbuilder s, double f, alglib.xparams _params) { - y = new double[0]; - mlpbase.mlpprocessi(network.innerobj, x, ref y, _params); + + dforest.dfbuildersetrndvarsratio(s.innerobj, f, _params); } /************************************************************************* - Error of the neural network on dataset. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + This function tells decision forest builder to automatically choose number + of variables used by decision forest construction algorithm. Roughly + sqrt(NVars) variables will be used. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. - - RESULT: - sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + S - decision forest builder object - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + OUTPUT PARAMETERS: + S - decision forest builder -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static double mlperror(multilayerperceptron network, double[,] xy, int npoints) + public static void dfbuildersetrndvarsauto(decisionforestbuilder s) { - return mlpbase.mlperror(network.innerobj, xy, npoints, null); + dforest.dfbuildersetrndvarsauto(s.innerobj, null); } - public static double mlperror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) + public static void dfbuildersetrndvarsauto(decisionforestbuilder s, alglib.xparams _params) { - return mlpbase.mlperror(network.innerobj, xy, npoints, _params); + dforest.dfbuildersetrndvarsauto(s.innerobj, _params); } /************************************************************************* - Error of the neural network on dataset given by sparse matrix. + This function sets size of dataset subsample generated the decision forest + construction algorithm. Size is specified as a fraction of total dataset + size. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + The default option is to use 50% of the dataset for training, 50% for the + OOB estimates. You can decrease fraction F down to 10%, 1% or even below + in order to reduce overfitting. INPUT PARAMETERS: - Network - neural network - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0 - - RESULT: - sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + S - decision forest builder object + F - fraction of the dataset to use, in (0,1] range. Values + outside of this range will be silently clipped. At + least one element is always selected for the training + set. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + OUTPUT PARAMETERS: + S - decision forest builder -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static double mlperrorsparse(multilayerperceptron network, sparsematrix xy, int npoints) + public static void dfbuildersetsubsampleratio(decisionforestbuilder s, double f) { - return mlpbase.mlperrorsparse(network.innerobj, xy.innerobj, npoints, null); + dforest.dfbuildersetsubsampleratio(s.innerobj, f, null); } - public static double mlperrorsparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) + public static void dfbuildersetsubsampleratio(decisionforestbuilder s, double f, alglib.xparams _params) { - return mlpbase.mlperrorsparse(network.innerobj, xy.innerobj, npoints, _params); + dforest.dfbuildersetsubsampleratio(s.innerobj, f, _params); } /************************************************************************* - Natural error function for neural network, internal subroutine. + This function sets seed used by internal RNG for random subsampling and + random selection of variable subsets. - NOTE: this function is single-threaded. Unlike other error function, it - receives no speed-up from being executed in SMP mode. + By default random seed is used, i.e. every time you build decision forest, + we seed generator with new value obtained from system-wide RNG. Thus, + decision forest builder returns non-deterministic results. You can change + such behavior by specyfing fixed positive seed value. + + INPUT PARAMETERS: + S - decision forest builder object + SeedVal - seed value: + * positive values are used for seeding RNG with fixed + seed, i.e. subsequent runs on same data will return + same decision forests + * non-positive seed means that random seed is used + for every run of builder, i.e. subsequent runs on + same datasets will return slightly different + decision forests + + OUTPUT PARAMETERS: + S - decision forest builder, see -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static double mlperrorn(multilayerperceptron network, double[,] xy, int ssize) + public static void dfbuildersetseed(decisionforestbuilder s, int seedval) { - return mlpbase.mlperrorn(network.innerobj, xy, ssize, null); + dforest.dfbuildersetseed(s.innerobj, seedval, null); } - public static double mlperrorn(multilayerperceptron network, double[,] xy, int ssize, alglib.xparams _params) + public static void dfbuildersetseed(decisionforestbuilder s, int seedval, alglib.xparams _params) { - return mlpbase.mlperrorn(network.innerobj, xy, ssize, _params); + dforest.dfbuildersetseed(s.innerobj, seedval, _params); } /************************************************************************* - Classification error of the neural network on dataset. + This function sets random decision forest construction algorithm. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + As for now, only one decision forest construction algorithm is supported - + a dense "baseline" RDF algorithm. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. - - RESULT: - classification error (number of misclassified cases) + S - decision forest builder object + AlgoType - algorithm type: + * 0 = baseline dense RDF - DATASET FORMAT: + OUTPUT PARAMETERS: + S - decision forest builder, see - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + public static void dfbuildersetrdfalgo(decisionforestbuilder s, int algotype) + { + + dforest.dfbuildersetrdfalgo(s.innerobj, algotype, null); + } + + public static void dfbuildersetrdfalgo(decisionforestbuilder s, int algotype, alglib.xparams _params) + { + + dforest.dfbuildersetrdfalgo(s.innerobj, algotype, _params); + } + + /************************************************************************* + This function sets split selection algorithm used by decision forest + classifier. You may choose several algorithms, with different speed and + quality of the results. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + INPUT PARAMETERS: + S - decision forest builder object + SplitStrength- split type: + * 0 = split at the random position, fastest one + * 1 = split at the middle of the range + * 2 = strong split at the best point of the range (default) - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + OUTPUT PARAMETERS: + S - decision forest builder, see -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static int mlpclserror(multilayerperceptron network, double[,] xy, int npoints) + public static void dfbuildersetrdfsplitstrength(decisionforestbuilder s, int splitstrength) { - return mlpbase.mlpclserror(network.innerobj, xy, npoints, null); + dforest.dfbuildersetrdfsplitstrength(s.innerobj, splitstrength, null); } - public static int mlpclserror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) + public static void dfbuildersetrdfsplitstrength(decisionforestbuilder s, int splitstrength, alglib.xparams _params) { - return mlpbase.mlpclserror(network.innerobj, xy, npoints, _params); + dforest.dfbuildersetrdfsplitstrength(s.innerobj, splitstrength, _params); } /************************************************************************* - Relative classification error on the test set. + This function tells decision forest construction algorithm to use + Gini impurity based variable importance estimation (also known as MDI). - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + This version of importance estimation algorithm analyzes mean decrease in + impurity (MDI) on training sample during splits. The result is divided + by impurity at the root node in order to produce estimate in [0,1] range. - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. + Such estimates are fast to calculate and beautifully normalized (sum to + one) but have following downsides: + * They ALWAYS sum to 1.0, even if output is completely unpredictable. I.e. + MDI allows to order variables by importance, but does not tell us about + "absolute" importances of variables + * there exist some bias towards continuous and high-cardinality categorical + variables - RESULT: - Percent of incorrectly classified cases. Works both for classifier - networks and general purpose networks used as classifiers. + NOTE: informally speaking, MDA (permutation importance) rating answers the + question "what part of the model predictive power is ruined by + permuting k-th variable?" while MDI tells us "what part of the model + predictive power was achieved due to usage of k-th variable". - DATASET FORMAT: + Thus, MDA rates each variable independently at "0 to 1" scale while + MDI (and OOB-MDI too) tends to divide "unit amount of importance" + between several important variables. - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + If all variables are equally important, they will have same + MDI/OOB-MDI rating, equal (for OOB-MDI: roughly equal) to 1/NVars. + However, roughly same picture will be produced for the "all + variables provide information no one is critical" situation and for + the "all variables are critical, drop any one, everything is ruined" + situation. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + Contrary to that, MDA will rate critical variable as ~1.0 important, + and important but non-critical variable will have less than unit + rating. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + NOTE: quite an often MDA and MDI return same results. It generally happens + on problems with low test set error (a few percents at most) and + large enough training set to avoid overfitting. + + The difference between MDA, MDI and OOB-MDI becomes important only + on "hard" tasks with high test set error and/or small training set. + + INPUT PARAMETERS: + S - decision forest builder object + + OUTPUT PARAMETERS: + S - decision forest builder object. Next call to the forest + construction function will produce: + * importance estimates in rep.varimportances field + * variable ranks in rep.topvars field -- ALGLIB -- - Copyright 25.12.2008 by Bochkanov Sergey + Copyright 29.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlprelclserror(multilayerperceptron network, double[,] xy, int npoints) + public static void dfbuildersetimportancetrngini(decisionforestbuilder s) { - return mlpbase.mlprelclserror(network.innerobj, xy, npoints, null); + dforest.dfbuildersetimportancetrngini(s.innerobj, null); } - public static double mlprelclserror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) + public static void dfbuildersetimportancetrngini(decisionforestbuilder s, alglib.xparams _params) { - return mlpbase.mlprelclserror(network.innerobj, xy, npoints, _params); + dforest.dfbuildersetimportancetrngini(s.innerobj, _params); } /************************************************************************* - Relative classification error on the test set given by sparse matrix. + This function tells decision forest construction algorithm to use + out-of-bag version of Gini variable importance estimation (also known as + OOB-MDI). - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + This version of importance estimation algorithm analyzes mean decrease in + impurity (MDI) on out-of-bag sample during splits. The result is divided + by impurity at the root node in order to produce estimate in [0,1] range. - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. Sparse matrix must use CRS format - for storage. - NPoints - points count, >=0. + Such estimates are fast to calculate and resistant to overfitting issues + (thanks to the out-of-bag estimates used). However, OOB Gini rating has + following downsides: + * there exist some bias towards continuous and high-cardinality categorical + variables + * Gini rating allows us to order variables by importance, but it is hard + to define importance of the variable by itself. - RESULT: - Percent of incorrectly classified cases. Works both for classifier - networks and general purpose networks used as classifiers. + NOTE: informally speaking, MDA (permutation importance) rating answers the + question "what part of the model predictive power is ruined by + permuting k-th variable?" while MDI tells us "what part of the model + predictive power was achieved due to usage of k-th variable". - DATASET FORMAT: + Thus, MDA rates each variable independently at "0 to 1" scale while + MDI (and OOB-MDI too) tends to divide "unit amount of importance" + between several important variables. - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + If all variables are equally important, they will have same + MDI/OOB-MDI rating, equal (for OOB-MDI: roughly equal) to 1/NVars. + However, roughly same picture will be produced for the "all + variables provide information no one is critical" situation and for + the "all variables are critical, drop any one, everything is ruined" + situation. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + Contrary to that, MDA will rate critical variable as ~1.0 important, + and important but non-critical variable will have less than unit + rating. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + NOTE: quite an often MDA and MDI return same results. It generally happens + on problems with low test set error (a few percents at most) and + large enough training set to avoid overfitting. + + The difference between MDA, MDI and OOB-MDI becomes important only + on "hard" tasks with high test set error and/or small training set. + + INPUT PARAMETERS: + S - decision forest builder object + + OUTPUT PARAMETERS: + S - decision forest builder object. Next call to the forest + construction function will produce: + * importance estimates in rep.varimportances field + * variable ranks in rep.topvars field -- ALGLIB -- - Copyright 09.08.2012 by Bochkanov Sergey + Copyright 29.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlprelclserrorsparse(multilayerperceptron network, sparsematrix xy, int npoints) + public static void dfbuildersetimportanceoobgini(decisionforestbuilder s) { - return mlpbase.mlprelclserrorsparse(network.innerobj, xy.innerobj, npoints, null); + dforest.dfbuildersetimportanceoobgini(s.innerobj, null); } - public static double mlprelclserrorsparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) + public static void dfbuildersetimportanceoobgini(decisionforestbuilder s, alglib.xparams _params) { - return mlpbase.mlprelclserrorsparse(network.innerobj, xy.innerobj, npoints, _params); + dforest.dfbuildersetimportanceoobgini(s.innerobj, _params); } /************************************************************************* - Average cross-entropy (in bits per element) on the test set. + This function tells decision forest construction algorithm to use + permutation variable importance estimator (also known as MDA). - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + This version of importance estimation algorithm analyzes mean increase in + out-of-bag sum of squared residuals after random permutation of J-th + variable. The result is divided by error computed with all variables being + perturbed in order to produce R-squared-like estimate in [0,1] range. - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. + Such estimate is slower to calculate than Gini-based rating because it + needs multiple inference runs for each of variables being studied. - RESULT: - CrossEntropy/(NPoints*LN(2)). - Zero if network solves regression task. + ALGLIB uses parallelized and highly optimized algorithm which analyzes + path through the decision tree and allows to handle most perturbations + in O(1) time; nevertheless, requesting MDA importances may increase forest + construction time from 10% to 200% (or more, if you have thousands of + variables). - DATASET FORMAT: + However, MDA rating has following benefits over Gini-based ones: + * no bias towards specific variable types + * ability to directly evaluate "absolute" importance of some variable at + "0 to 1" scale (contrary to Gini-based rating, which returns comparative + importances). - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + NOTE: informally speaking, MDA (permutation importance) rating answers the + question "what part of the model predictive power is ruined by + permuting k-th variable?" while MDI tells us "what part of the model + predictive power was achieved due to usage of k-th variable". - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + Thus, MDA rates each variable independently at "0 to 1" scale while + MDI (and OOB-MDI too) tends to divide "unit amount of importance" + between several important variables. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + If all variables are equally important, they will have same + MDI/OOB-MDI rating, equal (for OOB-MDI: roughly equal) to 1/NVars. + However, roughly same picture will be produced for the "all + variables provide information no one is critical" situation and for + the "all variables are critical, drop any one, everything is ruined" + situation. + + Contrary to that, MDA will rate critical variable as ~1.0 important, + and important but non-critical variable will have less than unit + rating. + + NOTE: quite an often MDA and MDI return same results. It generally happens + on problems with low test set error (a few percents at most) and + large enough training set to avoid overfitting. + + The difference between MDA, MDI and OOB-MDI becomes important only + on "hard" tasks with high test set error and/or small training set. + + INPUT PARAMETERS: + S - decision forest builder object + + OUTPUT PARAMETERS: + S - decision forest builder object. Next call to the forest + construction function will produce: + * importance estimates in rep.varimportances field + * variable ranks in rep.topvars field -- ALGLIB -- - Copyright 08.01.2009 by Bochkanov Sergey + Copyright 29.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgce(multilayerperceptron network, double[,] xy, int npoints) + public static void dfbuildersetimportancepermutation(decisionforestbuilder s) { - return mlpbase.mlpavgce(network.innerobj, xy, npoints, null); + dforest.dfbuildersetimportancepermutation(s.innerobj, null); } - public static double mlpavgce(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) + public static void dfbuildersetimportancepermutation(decisionforestbuilder s, alglib.xparams _params) { - return mlpbase.mlpavgce(network.innerobj, xy, npoints, _params); + dforest.dfbuildersetimportancepermutation(s.innerobj, _params); } /************************************************************************* - Average cross-entropy (in bits per element) on the test set given by - sparse matrix. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + This function tells decision forest construction algorithm to skip + variable importance estimation. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0. - - RESULT: - CrossEntropy/(NPoints*LN(2)). - Zero if network solves regression task. - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + S - decision forest builder object - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + OUTPUT PARAMETERS: + S - decision forest builder object. Next call to the forest + construction function will result in forest being built + without variable importance estimation. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + -- ALGLIB -- + Copyright 29.07.2019 by Bochkanov Sergey + *************************************************************************/ + public static void dfbuildersetimportancenone(decisionforestbuilder s) + { + + dforest.dfbuildersetimportancenone(s.innerobj, null); + } + + public static void dfbuildersetimportancenone(decisionforestbuilder s, alglib.xparams _params) + { + + dforest.dfbuildersetimportancenone(s.innerobj, _params); + } + + /************************************************************************* + This function is an alias for dfbuilderpeekprogress(), left in ALGLIB for + backward compatibility reasons. -- ALGLIB -- - Copyright 9.08.2012 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgcesparse(multilayerperceptron network, sparsematrix xy, int npoints) + public static double dfbuildergetprogress(decisionforestbuilder s) { - return mlpbase.mlpavgcesparse(network.innerobj, xy.innerobj, npoints, null); + return dforest.dfbuildergetprogress(s.innerobj, null); } - public static double mlpavgcesparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) + public static double dfbuildergetprogress(decisionforestbuilder s, alglib.xparams _params) { - return mlpbase.mlpavgcesparse(network.innerobj, xy.innerobj, npoints, _params); + return dforest.dfbuildergetprogress(s.innerobj, _params); } /************************************************************************* - RMS error on the test set given. + This function is used to peek into decision forest construction process + from some other thread and get current progress indicator. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + It returns value in [0,1]. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. + S - decision forest builder object used to build forest + in some other thread RESULT: - Root mean square error. Its meaning for regression task is obvious. As for - classification task, RMS error means error when estimating posterior - probabilities. - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs - - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + progress value, in [0,1] -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static double mlprmserror(multilayerperceptron network, double[,] xy, int npoints) + public static double dfbuilderpeekprogress(decisionforestbuilder s) { - return mlpbase.mlprmserror(network.innerobj, xy, npoints, null); + return dforest.dfbuilderpeekprogress(s.innerobj, null); } - public static double mlprmserror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) + public static double dfbuilderpeekprogress(decisionforestbuilder s, alglib.xparams _params) { - return mlpbase.mlprmserror(network.innerobj, xy, npoints, _params); + return dforest.dfbuilderpeekprogress(s.innerobj, _params); } /************************************************************************* - RMS error on the test set given by sparse matrix. + This subroutine builds decision forest according to current settings using + dataset internally stored in the builder object. Dense algorithm is used. + + NOTE: this function uses dense algorithm for forest construction + independently from the dataset format (dense or sparse). + + NOTE: forest built with this function is stored in-memory using 64-bit + data structures for offsets/indexes/split values. It is possible to + convert forest into more memory-efficient compressed binary + representation. Depending on the problem properties, 3.7x-5.7x + compression factors are possible. + + The downsides of compression are (a) slight reduction in the model + accuracy and (b) ~1.5x reduction in the inference speed (due to + increased complexity of the storage format). + + See comments on dfbinarycompression() for more info. + + Default settings are used by the algorithm; you can tweak them with the + help of the following functions: + * dfbuildersetrfactor() - to control a fraction of the dataset used for + subsampling + * dfbuildersetrandomvars() - to control number of variables randomly chosen + for decision rule creation ! COMMERCIAL EDITION OF ALGLIB: ! @@ -1929,2088 +2181,1855 @@ RMS error on the test set given by sparse matrix. ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0. + S - decision forest builder object + NTrees - NTrees>=1, number of trees to train - RESULT: - Root mean square error. Its meaning for regression task is obvious. As for - classification task, RMS error means error when estimating posterior - probabilities. + OUTPUT PARAMETERS: + DF - decision forest. You can compress this forest to more + compact 16-bit representation with dfbinarycompression() + Rep - report, see below for information on its fields. - DATASET FORMAT: + === report information produced by forest construction function ========== - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + Decision forest training report includes following information: + * training set errors + * out-of-bag estimates of errors + * variable importance ratings - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + Following fields are used to store information: + * training set errors are stored in rep.relclserror, rep.avgce, rep.rmserror, + rep.avgerror and rep.avgrelerror + * out-of-bag estimates of errors are stored in rep.oobrelclserror, rep.oobavgce, + rep.oobrmserror, rep.oobavgerror and rep.oobavgrelerror - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + Variable importance reports, if requested by dfbuildersetimportancegini(), + dfbuildersetimportancetrngini() or dfbuildersetimportancepermutation() + call, are stored in: + * rep.varimportances field stores importance ratings + * rep.topvars stores variable indexes ordered from the most important to + less important ones + + You can find more information about report fields in: + * comments on dfreport structure + * comments on dfbuildersetimportancegini function + * comments on dfbuildersetimportancetrngini function + * comments on dfbuildersetimportancepermutation function -- ALGLIB -- - Copyright 09.08.2012 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static double mlprmserrorsparse(multilayerperceptron network, sparsematrix xy, int npoints) + public static void dfbuilderbuildrandomforest(decisionforestbuilder s, int ntrees, out decisionforest df, out dfreport rep) { - - return mlpbase.mlprmserrorsparse(network.innerobj, xy.innerobj, npoints, null); + df = new decisionforest(); + rep = new dfreport(); + dforest.dfbuilderbuildrandomforest(s.innerobj, ntrees, df.innerobj, rep.innerobj, null); } - public static double mlprmserrorsparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) + public static void dfbuilderbuildrandomforest(decisionforestbuilder s, int ntrees, out decisionforest df, out dfreport rep, alglib.xparams _params) { - - return mlpbase.mlprmserrorsparse(network.innerobj, xy.innerobj, npoints, _params); + df = new decisionforest(); + rep = new dfreport(); + dforest.dfbuilderbuildrandomforest(s.innerobj, ntrees, df.innerobj, rep.innerobj, _params); } /************************************************************************* - Average absolute error on the test set. + This function performs binary compression of the decision forest. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Original decision forest produced by the forest builder is stored using + 64-bit representation for all numbers - offsets, variable indexes, split + points. - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. + It is possible to significantly reduce model size by means of: + * using compressed dynamic encoding for integers (offsets and variable + indexes), which uses just 1 byte to store small ints (less than 128), + just 2 bytes for larger values (less than 128^2) and so on + * storing floating point numbers using 8-bit exponent and 16-bit mantissa - RESULT: - Its meaning for regression task is obvious. As for classification task, it - means average error when estimating posterior probabilities. + As result, model needs significantly less memory (compression factor + depends on variable and class counts). In particular: + * NVars<128 and NClasses<128 result in 4.4x-5.7x model size reduction + * NVars<16384 and NClasses<128 result in 3.7x-4.5x model size reduction - DATASET FORMAT: + Such storage format performs lossless compression of all integers, but + compression of floating point values (split values) is lossy, with roughly + 0.01% relative error introduced during rounding. Thus, we recommend you to + re-evaluate model accuracy after compression. - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + Another downside of compression is ~1.5x reduction in the inference + speed due to necessity of dynamic decompression of the compressed model. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + INPUT PARAMETERS: + DF - decision forest built by forest builder - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + OUTPUT PARAMETERS: + DF - replaced by compressed forest + + RESULT: + compression factor (in-RAM size of the compressed model vs than of the + uncompressed one), positive number larger than 1.0 -- ALGLIB -- - Copyright 11.03.2008 by Bochkanov Sergey + Copyright 22.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgerror(multilayerperceptron network, double[,] xy, int npoints) + public static double dfbinarycompression(decisionforest df) { - return mlpbase.mlpavgerror(network.innerobj, xy, npoints, null); + return dforest.dfbinarycompression(df.innerobj, null); } - public static double mlpavgerror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) + public static double dfbinarycompression(decisionforest df, alglib.xparams _params) { - return mlpbase.mlpavgerror(network.innerobj, xy, npoints, _params); + return dforest.dfbinarycompression(df.innerobj, _params); } /************************************************************************* - Average absolute error on the test set given by sparse matrix. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Inference using decision forest - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0. + IMPORTANT: this function is thread-unsafe and may modify internal + structures of the model! You can not use same model object for + parallel evaluation from several threads. - RESULT: - Its meaning for regression task is obvious. As for classification task, it - means average error when estimating posterior probabilities. + Use dftsprocess() with independent thread-local buffers if + you need thread-safe evaluation. - DATASET FORMAT: + INPUT PARAMETERS: + DF - decision forest model + X - input vector, array[NVars] + Y - possibly preallocated buffer, reallocated if too small - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + OUTPUT PARAMETERS: + Y - result. Regression estimate when solving regression task, + vector of posterior probabilities for classification task. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + See also DFProcessI. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). -- ALGLIB -- - Copyright 09.08.2012 by Bochkanov Sergey + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgerrorsparse(multilayerperceptron network, sparsematrix xy, int npoints) + public static void dfprocess(decisionforest df, double[] x, ref double[] y) { - return mlpbase.mlpavgerrorsparse(network.innerobj, xy.innerobj, npoints, null); + dforest.dfprocess(df.innerobj, x, ref y, null); } - public static double mlpavgerrorsparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) + public static void dfprocess(decisionforest df, double[] x, ref double[] y, alglib.xparams _params) { - return mlpbase.mlpavgerrorsparse(network.innerobj, xy.innerobj, npoints, _params); + dforest.dfprocess(df.innerobj, x, ref y, _params); } /************************************************************************* - Average relative error on the test set. + 'interactive' variant of DFProcess for languages like Python which support + constructs like "Y = DFProcessI(DF,X)" and interactive mode of interpreter - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + This function allocates new array on each call, so it is significantly + slower than its 'non-interactive' counterpart, but it is more convenient + when you call it from command line. - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. + IMPORTANT: this function is thread-unsafe and may modify internal + structures of the model! You can not use same model object for + parallel evaluation from several threads. - RESULT: - Its meaning for regression task is obvious. As for classification task, it - means average relative error when estimating posterior probability of - belonging to the correct class. + Use dftsprocess() with independent thread-local buffers if + you need thread-safe evaluation. - DATASET FORMAT: + -- ALGLIB -- + Copyright 28.02.2010 by Bochkanov Sergey + *************************************************************************/ + public static void dfprocessi(decisionforest df, double[] x, out double[] y) + { + y = new double[0]; + dforest.dfprocessi(df.innerobj, x, ref y, null); + } + + public static void dfprocessi(decisionforest df, double[] x, out double[] y, alglib.xparams _params) + { + y = new double[0]; + dforest.dfprocessi(df.innerobj, x, ref y, _params); + } + + /************************************************************************* + This function returns first component of the inferred vector (i.e. one + with index #0). - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + It is a convenience wrapper for dfprocess() intended for either: + * 1-dimensional regression problems + * 2-class classification problems - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + In the former case this function returns inference result as scalar, which + is definitely more convenient that wrapping it as vector. In the latter + case it returns probability of object belonging to class #0. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + If you call it for anything different from two cases above, it will work + as defined, i.e. return y[0], although it is of less use in such cases. + + IMPORTANT: this function is thread-unsafe and modifies internal structures + of the model! You can not use same model object for parallel + evaluation from several threads. + + Use dftsprocess() with independent thread-local buffers, if + you need thread-safe evaluation. + + INPUT PARAMETERS: + Model - DF model + X - input vector, array[0..NVars-1]. + + RESULT: + Y[0] -- ALGLIB -- - Copyright 11.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgrelerror(multilayerperceptron network, double[,] xy, int npoints) + public static double dfprocess0(decisionforest model, double[] x) { - return mlpbase.mlpavgrelerror(network.innerobj, xy, npoints, null); + return dforest.dfprocess0(model.innerobj, x, null); } - public static double mlpavgrelerror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) + public static double dfprocess0(decisionforest model, double[] x, alglib.xparams _params) { - return mlpbase.mlpavgrelerror(network.innerobj, xy, npoints, _params); + return dforest.dfprocess0(model.innerobj, x, _params); } /************************************************************************* - Average relative error on the test set given by sparse matrix. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0. + This function returns most probable class number for an input X. It is + same as calling dfprocess(model,x,y), then determining i=argmax(y[i]) and + returning i. - RESULT: - Its meaning for regression task is obvious. As for classification task, it - means average relative error when estimating posterior probability of - belonging to the correct class. + A class number in [0,NOut) range in returned for classification problems, + -1 is returned when this function is called for regression problems. - DATASET FORMAT: + IMPORTANT: this function is thread-unsafe and modifies internal structures + of the model! You can not use same model object for parallel + evaluation from several threads. - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + Use dftsprocess() with independent thread-local buffers, if + you need thread-safe evaluation. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + INPUT PARAMETERS: + Model - decision forest model + X - input vector, array[0..NVars-1]. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + RESULT: + class number, -1 for regression tasks -- ALGLIB -- - Copyright 09.08.2012 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgrelerrorsparse(multilayerperceptron network, sparsematrix xy, int npoints) + public static int dfclassify(decisionforest model, double[] x) { - return mlpbase.mlpavgrelerrorsparse(network.innerobj, xy.innerobj, npoints, null); + return dforest.dfclassify(model.innerobj, x, null); } - public static double mlpavgrelerrorsparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) + public static int dfclassify(decisionforest model, double[] x, alglib.xparams _params) { - return mlpbase.mlpavgrelerrorsparse(network.innerobj, xy.innerobj, npoints, _params); + return dforest.dfclassify(model.innerobj, x, _params); } /************************************************************************* - Gradient calculation + Inference using decision forest + + Thread-safe procesing using external buffer for temporaries. + + This function is thread-safe (i.e . you can use same DF model from + multiple threads) as long as you use different buffer objects for different + threads. INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - X - input vector, length of array must be at least NIn - DesiredY- desired outputs, length of array must be at least NOut - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + DF - decision forest model + Buf - buffer object, must be allocated specifically for this + model with dfcreatebuffer(). + X - input vector, array[NVars] + Y - possibly preallocated buffer, reallocated if too small OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, array[WCount] + Y - result. Regression estimate when solving regression task, + vector of posterior probabilities for classification task. + + See also DFProcessI. + -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void mlpgrad(multilayerperceptron network, double[] x, double[] desiredy, out double e, ref double[] grad) + public static void dftsprocess(decisionforest df, decisionforestbuffer buf, double[] x, ref double[] y) { - e = 0; - mlpbase.mlpgrad(network.innerobj, x, desiredy, ref e, ref grad, null); + + dforest.dftsprocess(df.innerobj, buf.innerobj, x, ref y, null); } - public static void mlpgrad(multilayerperceptron network, double[] x, double[] desiredy, out double e, ref double[] grad, alglib.xparams _params) + public static void dftsprocess(decisionforest df, decisionforestbuffer buf, double[] x, ref double[] y, alglib.xparams _params) { - e = 0; - mlpbase.mlpgrad(network.innerobj, x, desiredy, ref e, ref grad, _params); + + dforest.dftsprocess(df.innerobj, buf.innerobj, x, ref y, _params); } /************************************************************************* - Gradient calculation (natural error function is used) + Relative classification error on the test set INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - X - input vector, length of array must be at least NIn - DesiredY- desired outputs, length of array must be at least NOut - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + DF - decision forest model + XY - test set + NPoints - test set size - OUTPUT PARAMETERS: - E - error function, sum-of-squares for regression networks, - cross-entropy for classification networks. - Grad - gradient of E with respect to weights of network, array[WCount] + RESULT: + percent of incorrectly classified cases. + Zero if model solves regression task. -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradn(multilayerperceptron network, double[] x, double[] desiredy, out double e, ref double[] grad) + public static double dfrelclserror(decisionforest df, double[,] xy, int npoints) { - e = 0; - mlpbase.mlpgradn(network.innerobj, x, desiredy, ref e, ref grad, null); + + return dforest.dfrelclserror(df.innerobj, xy, npoints, null); } - public static void mlpgradn(multilayerperceptron network, double[] x, double[] desiredy, out double e, ref double[] grad, alglib.xparams _params) + public static double dfrelclserror(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) { - e = 0; - mlpbase.mlpgradn(network.innerobj, x, desiredy, ref e, ref grad, _params); + + return dforest.dfrelclserror(df.innerobj, xy, npoints, _params); } /************************************************************************* - Batch gradient calculation for a set of inputs/outputs - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Average cross-entropy (in bits per element) on the test set INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset in dense format; one sample = one row: - * first NIn columns contain inputs, - * for regression problem, next NOut columns store - desired outputs. - * for classification problem, next column (just one!) - stores class number. - SSize - number of elements in XY - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + DF - decision forest model + XY - test set + NPoints - test set size - OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, array[WCount] + RESULT: + CrossEntropy/(NPoints*LN(2)). + Zero if model solves regression task. -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad) + public static double dfavgce(decisionforest df, double[,] xy, int npoints) { - e = 0; - mlpbase.mlpgradbatch(network.innerobj, xy, ssize, ref e, ref grad, null); + + return dforest.dfavgce(df.innerobj, xy, npoints, null); } - public static void mlpgradbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, alglib.xparams _params) + public static double dfavgce(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) { - e = 0; - mlpbase.mlpgradbatch(network.innerobj, xy, ssize, ref e, ref grad, _params); + + return dforest.dfavgce(df.innerobj, xy, npoints, _params); } /************************************************************************* - Batch gradient calculation for a set of inputs/outputs given by sparse - matrices - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + RMS error on the test set INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset in sparse format; one sample = one row: - * MATRIX MUST BE STORED IN CRS FORMAT - * first NIn columns contain inputs. - * for regression problem, next NOut columns store - desired outputs. - * for classification problem, next column (just one!) - stores class number. - SSize - number of elements in XY - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + DF - decision forest model + XY - test set + NPoints - test set size - OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, array[WCount] + RESULT: + root mean square error. + Its meaning for regression task is obvious. As for + classification task, RMS error means error when estimating posterior + probabilities. -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradbatchsparse(multilayerperceptron network, sparsematrix xy, int ssize, out double e, ref double[] grad) + public static double dfrmserror(decisionforest df, double[,] xy, int npoints) { - e = 0; - mlpbase.mlpgradbatchsparse(network.innerobj, xy.innerobj, ssize, ref e, ref grad, null); + + return dforest.dfrmserror(df.innerobj, xy, npoints, null); } - public static void mlpgradbatchsparse(multilayerperceptron network, sparsematrix xy, int ssize, out double e, ref double[] grad, alglib.xparams _params) + public static double dfrmserror(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) { - e = 0; - mlpbase.mlpgradbatchsparse(network.innerobj, xy.innerobj, ssize, ref e, ref grad, _params); + + return dforest.dfrmserror(df.innerobj, xy, npoints, _params); } /************************************************************************* - Batch gradient calculation for a subset of dataset - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Average error on the test set INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset in dense format; one sample = one row: - * first NIn columns contain inputs, - * for regression problem, next NOut columns store - desired outputs. - * for classification problem, next column (just one!) - stores class number. - SetSize - real size of XY, SetSize>=0; - Idx - subset of SubsetSize elements, array[SubsetSize]: - * Idx[I] stores row index in the original dataset which is - given by XY. Gradient is calculated with respect to rows - whose indexes are stored in Idx[]. - * Idx[] must store correct indexes; this function throws - an exception in case incorrect index (less than 0 or - larger than rows(XY)) is given - * Idx[] may store indexes in any order and even with - repetitions. - SubsetSize- number of elements in Idx[] array: - * positive value means that subset given by Idx[] is processed - * zero value results in zero gradient - * negative value means that full dataset is processed - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + DF - decision forest model + XY - test set + NPoints - test set size - OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, - array[WCount] + RESULT: + Its meaning for regression task is obvious. As for + classification task, it means average error when estimating posterior + probabilities. -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradbatchsubset(multilayerperceptron network, double[,] xy, int setsize, int[] idx, int subsetsize, out double e, ref double[] grad) + public static double dfavgerror(decisionforest df, double[,] xy, int npoints) { - e = 0; - mlpbase.mlpgradbatchsubset(network.innerobj, xy, setsize, idx, subsetsize, ref e, ref grad, null); + + return dforest.dfavgerror(df.innerobj, xy, npoints, null); } - public static void mlpgradbatchsubset(multilayerperceptron network, double[,] xy, int setsize, int[] idx, int subsetsize, out double e, ref double[] grad, alglib.xparams _params) + public static double dfavgerror(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) { - e = 0; - mlpbase.mlpgradbatchsubset(network.innerobj, xy, setsize, idx, subsetsize, ref e, ref grad, _params); + + return dforest.dfavgerror(df.innerobj, xy, npoints, _params); } /************************************************************************* - Batch gradient calculation for a set of inputs/outputs for a subset of - dataset given by set of indexes. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Average relative error on the test set INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset in sparse format; one sample = one row: - * MATRIX MUST BE STORED IN CRS FORMAT - * first NIn columns contain inputs, - * for regression problem, next NOut columns store - desired outputs. - * for classification problem, next column (just one!) - stores class number. - SetSize - real size of XY, SetSize>=0; - Idx - subset of SubsetSize elements, array[SubsetSize]: - * Idx[I] stores row index in the original dataset which is - given by XY. Gradient is calculated with respect to rows - whose indexes are stored in Idx[]. - * Idx[] must store correct indexes; this function throws - an exception in case incorrect index (less than 0 or - larger than rows(XY)) is given - * Idx[] may store indexes in any order and even with - repetitions. - SubsetSize- number of elements in Idx[] array: - * positive value means that subset given by Idx[] is processed - * zero value results in zero gradient - * negative value means that full dataset is processed - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. - - OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, - array[WCount] + DF - decision forest model + XY - test set + NPoints - test set size - NOTE: when SubsetSize<0 is used full dataset by call MLPGradBatchSparse - function. + RESULT: + Its meaning for regression task is obvious. As for + classification task, it means average relative error when estimating + posterior probability of belonging to the correct class. -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradbatchsparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] idx, int subsetsize, out double e, ref double[] grad) + public static double dfavgrelerror(decisionforest df, double[,] xy, int npoints) { - e = 0; - mlpbase.mlpgradbatchsparsesubset(network.innerobj, xy.innerobj, setsize, idx, subsetsize, ref e, ref grad, null); + + return dforest.dfavgrelerror(df.innerobj, xy, npoints, null); } - public static void mlpgradbatchsparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] idx, int subsetsize, out double e, ref double[] grad, alglib.xparams _params) + public static double dfavgrelerror(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) { - e = 0; - mlpbase.mlpgradbatchsparsesubset(network.innerobj, xy.innerobj, setsize, idx, subsetsize, ref e, ref grad, _params); + + return dforest.dfavgrelerror(df.innerobj, xy, npoints, _params); } /************************************************************************* - Batch gradient calculation for a set of inputs/outputs - (natural error function is used) - - INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - set of inputs/outputs; one sample = one row; - first NIn columns contain inputs, - next NOut columns - desired outputs. - SSize - number of elements in XY - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + This subroutine builds random decision forest. - OUTPUT PARAMETERS: - E - error function, sum-of-squares for regression networks, - cross-entropy for classification networks. - Grad - gradient of E with respect to weights of network, array[WCount] + --------- DEPRECATED VERSION! USE DECISION FOREST BUILDER OBJECT --------- -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 19.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradnbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad) + public static void dfbuildrandomdecisionforest(double[,] xy, int npoints, int nvars, int nclasses, int ntrees, double r, out int info, out decisionforest df, out dfreport rep) { - e = 0; - mlpbase.mlpgradnbatch(network.innerobj, xy, ssize, ref e, ref grad, null); + info = 0; + df = new decisionforest(); + rep = new dfreport(); + dforest.dfbuildrandomdecisionforest(xy, npoints, nvars, nclasses, ntrees, r, ref info, df.innerobj, rep.innerobj, null); } - public static void mlpgradnbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, alglib.xparams _params) + public static void dfbuildrandomdecisionforest(double[,] xy, int npoints, int nvars, int nclasses, int ntrees, double r, out int info, out decisionforest df, out dfreport rep, alglib.xparams _params) { - e = 0; - mlpbase.mlpgradnbatch(network.innerobj, xy, ssize, ref e, ref grad, _params); + info = 0; + df = new decisionforest(); + rep = new dfreport(); + dforest.dfbuildrandomdecisionforest(xy, npoints, nvars, nclasses, ntrees, r, ref info, df.innerobj, rep.innerobj, _params); } /************************************************************************* - Batch Hessian calculation (natural error function) using R-algorithm. - Internal subroutine. + This subroutine builds random decision forest. - -- ALGLIB -- - Copyright 26.01.2008 by Bochkanov Sergey. + --------- DEPRECATED VERSION! USE DECISION FOREST BUILDER OBJECT --------- - Hessian calculation based on R-algorithm described in - "Fast Exact Multiplication by the Hessian", - B. A. Pearlmutter, - Neural Computation, 1994. + -- ALGLIB -- + Copyright 19.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void mlphessiannbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, ref double[,] h) + public static void dfbuildrandomdecisionforestx1(double[,] xy, int npoints, int nvars, int nclasses, int ntrees, int nrndvars, double r, out int info, out decisionforest df, out dfreport rep) { - e = 0; - mlpbase.mlphessiannbatch(network.innerobj, xy, ssize, ref e, ref grad, ref h, null); + info = 0; + df = new decisionforest(); + rep = new dfreport(); + dforest.dfbuildrandomdecisionforestx1(xy, npoints, nvars, nclasses, ntrees, nrndvars, r, ref info, df.innerobj, rep.innerobj, null); } - public static void mlphessiannbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, ref double[,] h, alglib.xparams _params) + public static void dfbuildrandomdecisionforestx1(double[,] xy, int npoints, int nvars, int nclasses, int ntrees, int nrndvars, double r, out int info, out decisionforest df, out dfreport rep, alglib.xparams _params) { - e = 0; - mlpbase.mlphessiannbatch(network.innerobj, xy, ssize, ref e, ref grad, ref h, _params); + info = 0; + df = new decisionforest(); + rep = new dfreport(); + dforest.dfbuildrandomdecisionforestx1(xy, npoints, nvars, nclasses, ntrees, nrndvars, r, ref info, df.innerobj, rep.innerobj, _params); } - + +} +public partial class alglib +{ + + /************************************************************************* - Batch Hessian calculation using R-algorithm. - Internal subroutine. + Model's errors: + * RelCLSError - fraction of misclassified cases. + * AvgCE - acerage cross-entropy + * RMSError - root-mean-square error + * AvgError - average error + * AvgRelError - average relative error - -- ALGLIB -- - Copyright 26.01.2008 by Bochkanov Sergey. + NOTE 1: RelCLSError/AvgCE are zero on regression problems. - Hessian calculation based on R-algorithm described in - "Fast Exact Multiplication by the Hessian", - B. A. Pearlmutter, - Neural Computation, 1994. + NOTE 2: on classification problems RMSError/AvgError/AvgRelError contain + errors in prediction of posterior probabilities *************************************************************************/ - public static void mlphessianbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, ref double[,] h) + public class modelerrors : alglibobject { - e = 0; - mlpbase.mlphessianbatch(network.innerobj, xy, ssize, ref e, ref grad, ref h, null); - } + // + // Public declarations + // + public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } + public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } + public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } + public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } + public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } - public static void mlphessianbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, ref double[,] h, alglib.xparams _params) - { - e = 0; - mlpbase.mlphessianbatch(network.innerobj, xy, ssize, ref e, ref grad, ref h, _params); - } + public modelerrors() + { + _innerobj = new mlpbase.modelerrors(); + } + + public override alglib.alglibobject make_copy() + { + return new modelerrors((mlpbase.modelerrors)_innerobj.make_copy()); + } - /************************************************************************* - Calculation of all types of errors on subset of dataset. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private mlpbase.modelerrors _innerobj; + public mlpbase.modelerrors innerobj { get { return _innerobj; } } + public modelerrors(mlpbase.modelerrors obj) + { + _innerobj = obj; + } + } - INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset; one sample = one row; - first NIn columns contain inputs, - next NOut columns - desired outputs. - SetSize - real size of XY, SetSize>=0; - Subset - subset of SubsetSize elements, array[SubsetSize]; - SubsetSize- number of elements in Subset[] array: - * if SubsetSize>0, rows of XY with indices Subset[0]... - ...Subset[SubsetSize-1] are processed - * if SubsetSize=0, zeros are returned - * if SubsetSize<0, entire dataset is processed; Subset[] - array is ignored in this case. - OUTPUT PARAMETERS: - Rep - it contains all type of errors. + /************************************************************************* - -- ALGLIB -- - Copyright 04.09.2012 by Bochkanov Sergey *************************************************************************/ - public static void mlpallerrorssubset(multilayerperceptron network, double[,] xy, int setsize, int[] subset, int subsetsize, out modelerrors rep) + public class multilayerperceptron : alglibobject { - rep = new modelerrors(); - mlpbase.mlpallerrorssubset(network.innerobj, xy, setsize, subset, subsetsize, rep.innerobj, null); + // + // Public declarations + // + + public multilayerperceptron() + { + _innerobj = new mlpbase.multilayerperceptron(); + } + + public override alglib.alglibobject make_copy() + { + return new multilayerperceptron((mlpbase.multilayerperceptron)_innerobj.make_copy()); + } + + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private mlpbase.multilayerperceptron _innerobj; + public mlpbase.multilayerperceptron innerobj { get { return _innerobj; } } + public multilayerperceptron(mlpbase.multilayerperceptron obj) + { + _innerobj = obj; + } } + + + /************************************************************************* + This function serializes data structure to string/stream. - public static void mlpallerrorssubset(multilayerperceptron network, double[,] xy, int setsize, int[] subset, int subsetsize, out modelerrors rep, alglib.xparams _params) + Important properties of s_out: + * it contains alphanumeric characters, dots, underscores, minus signs + * these symbols are grouped into words, which are separated by spaces + and Windows-style (CR+LF) newlines + * although serializer uses spaces and CR+LF as separators, you can + replace any separator character by arbitrary combination of spaces, + tabs, Windows or Unix newlines. It allows flexible reformatting of + the string in case you want to include it into a text or XML file. + But you should not insert separators into the middle of the "words" + nor should you change the case of letters. + * s_out can be freely moved between 32-bit and 64-bit systems, little + and big endian machines, and so on. You can serialize structure on + 32-bit machine and unserialize it on 64-bit one (or vice versa), or + serialize it on SPARC and unserialize on x86. You can also + serialize it in C++ version of ALGLIB and unserialize it in C# one, + and vice versa. + *************************************************************************/ + public static void mlpserialize(multilayerperceptron obj, out string s_out) { - rep = new modelerrors(); - mlpbase.mlpallerrorssubset(network.innerobj, xy, setsize, subset, subsetsize, rep.innerobj, _params); + alglib.serializer s = new alglib.serializer(); + s.alloc_start(); + mlpbase.mlpalloc(s, obj.innerobj, null); + s.sstart_str(); + mlpbase.mlpserialize(s, obj.innerobj, null); + s.stop(); + s_out = s.get_string(); } - - /************************************************************************* - Calculation of all types of errors on subset of dataset. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset given by sparse matrix; - one sample = one row; - first NIn columns contain inputs, - next NOut columns - desired outputs. - SetSize - real size of XY, SetSize>=0; - Subset - subset of SubsetSize elements, array[SubsetSize]; - SubsetSize- number of elements in Subset[] array: - * if SubsetSize>0, rows of XY with indices Subset[0]... - ...Subset[SubsetSize-1] are processed - * if SubsetSize=0, zeros are returned - * if SubsetSize<0, entire dataset is processed; Subset[] - array is ignored in this case. - OUTPUT PARAMETERS: - Rep - it contains all type of errors. + /************************************************************************* + This function serializes data structure to string/stream. + + Important properties of s_out: + * it contains alphanumeric characters, dots, underscores, minus signs + * these symbols are grouped into words, which are separated by spaces + and Windows-style (CR+LF) newlines + * although serializer uses spaces and CR+LF as separators, you can + replace any separator character by arbitrary combination of spaces, + tabs, Windows or Unix newlines. It allows flexible reformatting of + the string in case you want to include it into a text or XML file. + But you should not insert separators into the middle of the "words" + nor should you change the case of letters. + * s_out can be freely moved between 32-bit and 64-bit systems, little + and big endian machines, and so on. You can serialize structure on + 32-bit machine and unserialize it on 64-bit one (or vice versa), or + serialize it on SPARC and unserialize on x86. You can also + serialize it in C++ version of ALGLIB and unserialize it in C# one, + and vice versa. + *************************************************************************/ + public static void mlpserialize(multilayerperceptron obj, System.IO.Stream stream_out) + { + alglib.serializer s = new alglib.serializer(); + s.alloc_start(); + mlpbase.mlpalloc(s, obj.innerobj, null); + s.sstart_stream(stream_out); + mlpbase.mlpserialize(s, obj.innerobj, null); + s.stop(); + } - -- ALGLIB -- - Copyright 04.09.2012 by Bochkanov Sergey + /************************************************************************* + This function unserializes data structure from string/stream. *************************************************************************/ - public static void mlpallerrorssparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] subset, int subsetsize, out modelerrors rep) + public static void mlpunserialize(string s_in, out multilayerperceptron obj) { - rep = new modelerrors(); - mlpbase.mlpallerrorssparsesubset(network.innerobj, xy.innerobj, setsize, subset, subsetsize, rep.innerobj, null); + alglib.serializer s = new alglib.serializer(); + obj = new multilayerperceptron(); + s.ustart_str(s_in); + mlpbase.mlpunserialize(s, obj.innerobj, null); + s.stop(); } + + + /************************************************************************* + This function unserializes data structure from string/stream. + *************************************************************************/ + public static void mlpunserialize(System.IO.Stream stream_in, out multilayerperceptron obj) - public static void mlpallerrorssparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] subset, int subsetsize, out modelerrors rep, alglib.xparams _params) { - rep = new modelerrors(); - mlpbase.mlpallerrorssparsesubset(network.innerobj, xy.innerobj, setsize, subset, subsetsize, rep.innerobj, _params); + alglib.serializer s = new alglib.serializer(); + obj = new multilayerperceptron(); + s.ustart_stream(stream_in); + mlpbase.mlpunserialize(s, obj.innerobj, null); + s.stop(); } /************************************************************************* - Error of the neural network on subset of dataset. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - SetSize - real size of XY, SetSize>=0; - Subset - subset of SubsetSize elements, array[SubsetSize]; - SubsetSize- number of elements in Subset[] array: - * if SubsetSize>0, rows of XY with indices Subset[0]... - ...Subset[SubsetSize-1] are processed - * if SubsetSize=0, zeros are returned - * if SubsetSize<0, entire dataset is processed; Subset[] - array is ignored in this case. - - RESULT: - sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs - - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + Creates neural network with NIn inputs, NOut outputs, without hidden + layers, with linear output layer. Network weights are filled with small + random values. -- ALGLIB -- - Copyright 04.09.2012 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static double mlperrorsubset(multilayerperceptron network, double[,] xy, int setsize, int[] subset, int subsetsize) + public static void mlpcreate0(int nin, int nout, out multilayerperceptron network) { - - return mlpbase.mlperrorsubset(network.innerobj, xy, setsize, subset, subsetsize, null); + network = new multilayerperceptron(); + mlpbase.mlpcreate0(nin, nout, network.innerobj, null); } - public static double mlperrorsubset(multilayerperceptron network, double[,] xy, int setsize, int[] subset, int subsetsize, alglib.xparams _params) + public static void mlpcreate0(int nin, int nout, out multilayerperceptron network, alglib.xparams _params) { - - return mlpbase.mlperrorsubset(network.innerobj, xy, setsize, subset, subsetsize, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreate0(nin, nout, network.innerobj, _params); } /************************************************************************* - Error of the neural network on subset of sparse dataset. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - SetSize - real size of XY, SetSize>=0; - it is used when SubsetSize<0; - Subset - subset of SubsetSize elements, array[SubsetSize]; - SubsetSize- number of elements in Subset[] array: - * if SubsetSize>0, rows of XY with indices Subset[0]... - ...Subset[SubsetSize-1] are processed - * if SubsetSize=0, zeros are returned - * if SubsetSize<0, entire dataset is processed; Subset[] - array is ignored in this case. - - RESULT: - sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs - - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + Same as MLPCreate0, but with one hidden layer (NHid neurons) with + non-linear activation function. Output layer is linear. -- ALGLIB -- - Copyright 04.09.2012 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static double mlperrorsparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] subset, int subsetsize) + public static void mlpcreate1(int nin, int nhid, int nout, out multilayerperceptron network) { - - return mlpbase.mlperrorsparsesubset(network.innerobj, xy.innerobj, setsize, subset, subsetsize, null); + network = new multilayerperceptron(); + mlpbase.mlpcreate1(nin, nhid, nout, network.innerobj, null); } - public static double mlperrorsparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] subset, int subsetsize, alglib.xparams _params) + public static void mlpcreate1(int nin, int nhid, int nout, out multilayerperceptron network, alglib.xparams _params) { - - return mlpbase.mlperrorsparsesubset(network.innerobj, xy.innerobj, setsize, subset, subsetsize, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreate1(nin, nhid, nout, network.innerobj, _params); } - -} -public partial class alglib -{ - - + /************************************************************************* - Neural networks ensemble + Same as MLPCreate0, but with two hidden layers (NHid1 and NHid2 neurons) + with non-linear activation function. Output layer is linear. + $ALL + + -- ALGLIB -- + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public class mlpensemble : alglibobject + public static void mlpcreate2(int nin, int nhid1, int nhid2, int nout, out multilayerperceptron network) { - // - // Public declarations - // - - public mlpensemble() - { - _innerobj = new mlpe.mlpensemble(); - } - - public override alglib.alglibobject make_copy() - { - return new mlpensemble((mlpe.mlpensemble)_innerobj.make_copy()); - } - - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private mlpe.mlpensemble _innerobj; - public mlpe.mlpensemble innerobj { get { return _innerobj; } } - public mlpensemble(mlpe.mlpensemble obj) - { - _innerobj = obj; - } + network = new multilayerperceptron(); + mlpbase.mlpcreate2(nin, nhid1, nhid2, nout, network.innerobj, null); } - - - /************************************************************************* - This function serializes data structure to string/stream. - Important properties of s_out: - * it contains alphanumeric characters, dots, underscores, minus signs - * these symbols are grouped into words, which are separated by spaces - and Windows-style (CR+LF) newlines - * although serializer uses spaces and CR+LF as separators, you can - replace any separator character by arbitrary combination of spaces, - tabs, Windows or Unix newlines. It allows flexible reformatting of - the string in case you want to include it into a text or XML file. - But you should not insert separators into the middle of the "words" - nor should you change the case of letters. - * s_out can be freely moved between 32-bit and 64-bit systems, little - and big endian machines, and so on. You can serialize structure on - 32-bit machine and unserialize it on 64-bit one (or vice versa), or - serialize it on SPARC and unserialize on x86. You can also - serialize it in C++ version of ALGLIB and unserialize it in C# one, - and vice versa. - *************************************************************************/ - public static void mlpeserialize(mlpensemble obj, out string s_out) + public static void mlpcreate2(int nin, int nhid1, int nhid2, int nout, out multilayerperceptron network, alglib.xparams _params) { - alglib.serializer s = new alglib.serializer(); - s.alloc_start(); - mlpe.mlpealloc(s, obj.innerobj, null); - s.sstart_str(); - mlpe.mlpeserialize(s, obj.innerobj, null); - s.stop(); - s_out = s.get_string(); + network = new multilayerperceptron(); + mlpbase.mlpcreate2(nin, nhid1, nhid2, nout, network.innerobj, _params); } + + /************************************************************************* + Creates neural network with NIn inputs, NOut outputs, without hidden + layers with non-linear output layer. Network weights are filled with small + random values. + Activation function of the output layer takes values: - /************************************************************************* - This function serializes data structure to string/stream. - - Important properties of s_out: - * it contains alphanumeric characters, dots, underscores, minus signs - * these symbols are grouped into words, which are separated by spaces - and Windows-style (CR+LF) newlines - * although serializer uses spaces and CR+LF as separators, you can - replace any separator character by arbitrary combination of spaces, - tabs, Windows or Unix newlines. It allows flexible reformatting of - the string in case you want to include it into a text or XML file. - But you should not insert separators into the middle of the "words" - nor should you change the case of letters. - * s_out can be freely moved between 32-bit and 64-bit systems, little - and big endian machines, and so on. You can serialize structure on - 32-bit machine and unserialize it on 64-bit one (or vice versa), or - serialize it on SPARC and unserialize on x86. You can also - serialize it in C++ version of ALGLIB and unserialize it in C# one, - and vice versa. - *************************************************************************/ - public static void mlpeserialize(mlpensemble obj, System.IO.Stream stream_out) - { - alglib.serializer s = new alglib.serializer(); - s.alloc_start(); - mlpe.mlpealloc(s, obj.innerobj, null); - s.sstart_stream(stream_out); - mlpe.mlpeserialize(s, obj.innerobj, null); - s.stop(); - } + (B, +INF), if D>=0 + or - /************************************************************************* - This function unserializes data structure from string/stream. - *************************************************************************/ - public static void mlpeunserialize(string s_in, out mlpensemble obj) - { - alglib.serializer s = new alglib.serializer(); - obj = new mlpensemble(); - s.ustart_str(s_in); - mlpe.mlpeunserialize(s, obj.innerobj, null); - s.stop(); - } + (-INF, B), if D<0. - /************************************************************************* - This function unserializes data structure from string/stream. + -- ALGLIB -- + Copyright 30.03.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpeunserialize(System.IO.Stream stream_in, out mlpensemble obj) + public static void mlpcreateb0(int nin, int nout, double b, double d, out multilayerperceptron network) + { + network = new multilayerperceptron(); + mlpbase.mlpcreateb0(nin, nout, b, d, network.innerobj, null); + } + public static void mlpcreateb0(int nin, int nout, double b, double d, out multilayerperceptron network, alglib.xparams _params) { - alglib.serializer s = new alglib.serializer(); - obj = new mlpensemble(); - s.ustart_stream(stream_in); - mlpe.mlpeunserialize(s, obj.innerobj, null); - s.stop(); + network = new multilayerperceptron(); + mlpbase.mlpcreateb0(nin, nout, b, d, network.innerobj, _params); } /************************************************************************* - Like MLPCreate0, but for ensembles. + Same as MLPCreateB0 but with non-linear hidden layer. -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 30.03.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreate0(int nin, int nout, int ensemblesize, out mlpensemble ensemble) + public static void mlpcreateb1(int nin, int nhid, int nout, double b, double d, out multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreate0(nin, nout, ensemblesize, ensemble.innerobj, null); + network = new multilayerperceptron(); + mlpbase.mlpcreateb1(nin, nhid, nout, b, d, network.innerobj, null); } - public static void mlpecreate0(int nin, int nout, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcreateb1(int nin, int nhid, int nout, double b, double d, out multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreate0(nin, nout, ensemblesize, ensemble.innerobj, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreateb1(nin, nhid, nout, b, d, network.innerobj, _params); } /************************************************************************* - Like MLPCreate1, but for ensembles. + Same as MLPCreateB0 but with two non-linear hidden layers. -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 30.03.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreate1(int nin, int nhid, int nout, int ensemblesize, out mlpensemble ensemble) + public static void mlpcreateb2(int nin, int nhid1, int nhid2, int nout, double b, double d, out multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreate1(nin, nhid, nout, ensemblesize, ensemble.innerobj, null); + network = new multilayerperceptron(); + mlpbase.mlpcreateb2(nin, nhid1, nhid2, nout, b, d, network.innerobj, null); } - public static void mlpecreate1(int nin, int nhid, int nout, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcreateb2(int nin, int nhid1, int nhid2, int nout, double b, double d, out multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreate1(nin, nhid, nout, ensemblesize, ensemble.innerobj, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreateb2(nin, nhid1, nhid2, nout, b, d, network.innerobj, _params); } /************************************************************************* - Like MLPCreate2, but for ensembles. + Creates neural network with NIn inputs, NOut outputs, without hidden + layers with non-linear output layer. Network weights are filled with small + random values. Activation function of the output layer takes values [A,B]. -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 30.03.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreate2(int nin, int nhid1, int nhid2, int nout, int ensemblesize, out mlpensemble ensemble) + public static void mlpcreater0(int nin, int nout, double a, double b, out multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreate2(nin, nhid1, nhid2, nout, ensemblesize, ensemble.innerobj, null); + network = new multilayerperceptron(); + mlpbase.mlpcreater0(nin, nout, a, b, network.innerobj, null); } - public static void mlpecreate2(int nin, int nhid1, int nhid2, int nout, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcreater0(int nin, int nout, double a, double b, out multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreate2(nin, nhid1, nhid2, nout, ensemblesize, ensemble.innerobj, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreater0(nin, nout, a, b, network.innerobj, _params); } /************************************************************************* - Like MLPCreateB0, but for ensembles. + Same as MLPCreateR0, but with non-linear hidden layer. -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 30.03.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreateb0(int nin, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble) + public static void mlpcreater1(int nin, int nhid, int nout, double a, double b, out multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreateb0(nin, nout, b, d, ensemblesize, ensemble.innerobj, null); + network = new multilayerperceptron(); + mlpbase.mlpcreater1(nin, nhid, nout, a, b, network.innerobj, null); } - public static void mlpecreateb0(int nin, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcreater1(int nin, int nhid, int nout, double a, double b, out multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreateb0(nin, nout, b, d, ensemblesize, ensemble.innerobj, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreater1(nin, nhid, nout, a, b, network.innerobj, _params); } /************************************************************************* - Like MLPCreateB1, but for ensembles. + Same as MLPCreateR0, but with two non-linear hidden layers. -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 30.03.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreateb1(int nin, int nhid, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble) + public static void mlpcreater2(int nin, int nhid1, int nhid2, int nout, double a, double b, out multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreateb1(nin, nhid, nout, b, d, ensemblesize, ensemble.innerobj, null); + network = new multilayerperceptron(); + mlpbase.mlpcreater2(nin, nhid1, nhid2, nout, a, b, network.innerobj, null); } - public static void mlpecreateb1(int nin, int nhid, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcreater2(int nin, int nhid1, int nhid2, int nout, double a, double b, out multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreateb1(nin, nhid, nout, b, d, ensemblesize, ensemble.innerobj, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreater2(nin, nhid1, nhid2, nout, a, b, network.innerobj, _params); } /************************************************************************* - Like MLPCreateB2, but for ensembles. + Creates classifier network with NIn inputs and NOut possible classes. + Network contains no hidden layers and linear output layer with SOFTMAX- + normalization (so outputs sums up to 1.0 and converge to posterior + probabilities). -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreateb2(int nin, int nhid1, int nhid2, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble) + public static void mlpcreatec0(int nin, int nout, out multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreateb2(nin, nhid1, nhid2, nout, b, d, ensemblesize, ensemble.innerobj, null); + network = new multilayerperceptron(); + mlpbase.mlpcreatec0(nin, nout, network.innerobj, null); } - public static void mlpecreateb2(int nin, int nhid1, int nhid2, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcreatec0(int nin, int nout, out multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreateb2(nin, nhid1, nhid2, nout, b, d, ensemblesize, ensemble.innerobj, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreatec0(nin, nout, network.innerobj, _params); } /************************************************************************* - Like MLPCreateR0, but for ensembles. + Same as MLPCreateC0, but with one non-linear hidden layer. -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreater0(int nin, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble) + public static void mlpcreatec1(int nin, int nhid, int nout, out multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreater0(nin, nout, a, b, ensemblesize, ensemble.innerobj, null); + network = new multilayerperceptron(); + mlpbase.mlpcreatec1(nin, nhid, nout, network.innerobj, null); } - public static void mlpecreater0(int nin, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcreatec1(int nin, int nhid, int nout, out multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreater0(nin, nout, a, b, ensemblesize, ensemble.innerobj, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreatec1(nin, nhid, nout, network.innerobj, _params); } /************************************************************************* - Like MLPCreateR1, but for ensembles. + Same as MLPCreateC0, but with two non-linear hidden layers. -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreater1(int nin, int nhid, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble) + public static void mlpcreatec2(int nin, int nhid1, int nhid2, int nout, out multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreater1(nin, nhid, nout, a, b, ensemblesize, ensemble.innerobj, null); + network = new multilayerperceptron(); + mlpbase.mlpcreatec2(nin, nhid1, nhid2, nout, network.innerobj, null); } - public static void mlpecreater1(int nin, int nhid, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcreatec2(int nin, int nhid1, int nhid2, int nout, out multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreater1(nin, nhid, nout, a, b, ensemblesize, ensemble.innerobj, _params); + network = new multilayerperceptron(); + mlpbase.mlpcreatec2(nin, nhid1, nhid2, nout, network.innerobj, _params); } /************************************************************************* - Like MLPCreateR2, but for ensembles. + Copying of neural network + + INPUT PARAMETERS: + Network1 - original + + OUTPUT PARAMETERS: + Network2 - copy -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreater2(int nin, int nhid1, int nhid2, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble) + public static void mlpcopy(multilayerperceptron network1, out multilayerperceptron network2) { - ensemble = new mlpensemble(); - mlpe.mlpecreater2(nin, nhid1, nhid2, nout, a, b, ensemblesize, ensemble.innerobj, null); + network2 = new multilayerperceptron(); + mlpbase.mlpcopy(network1.innerobj, network2.innerobj, null); } - public static void mlpecreater2(int nin, int nhid1, int nhid2, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcopy(multilayerperceptron network1, out multilayerperceptron network2, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreater2(nin, nhid1, nhid2, nout, a, b, ensemblesize, ensemble.innerobj, _params); + network2 = new multilayerperceptron(); + mlpbase.mlpcopy(network1.innerobj, network2.innerobj, _params); } /************************************************************************* - Like MLPCreateC0, but for ensembles. + This function copies tunable parameters (weights/means/sigmas) from one + network to another with same architecture. It performs some rudimentary + checks that architectures are same, and throws exception if check fails. + + It is intended for fast copying of states between two network which are + known to have same geometry. + + INPUT PARAMETERS: + Network1 - source, must be correctly initialized + Network2 - target, must have same architecture + + OUTPUT PARAMETERS: + Network2 - network state is copied from source to target -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 20.06.2013 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreatec0(int nin, int nout, int ensemblesize, out mlpensemble ensemble) + public static void mlpcopytunableparameters(multilayerperceptron network1, multilayerperceptron network2) { - ensemble = new mlpensemble(); - mlpe.mlpecreatec0(nin, nout, ensemblesize, ensemble.innerobj, null); + + mlpbase.mlpcopytunableparameters(network1.innerobj, network2.innerobj, null); } - public static void mlpecreatec0(int nin, int nout, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpcopytunableparameters(multilayerperceptron network1, multilayerperceptron network2, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreatec0(nin, nout, ensemblesize, ensemble.innerobj, _params); + + mlpbase.mlpcopytunableparameters(network1.innerobj, network2.innerobj, _params); } /************************************************************************* - Like MLPCreateC1, but for ensembles. + Randomization of neural network weights -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 06.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreatec1(int nin, int nhid, int nout, int ensemblesize, out mlpensemble ensemble) + public static void mlprandomize(multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreatec1(nin, nhid, nout, ensemblesize, ensemble.innerobj, null); + + mlpbase.mlprandomize(network.innerobj, null); } - public static void mlpecreatec1(int nin, int nhid, int nout, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlprandomize(multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreatec1(nin, nhid, nout, ensemblesize, ensemble.innerobj, _params); + + mlpbase.mlprandomize(network.innerobj, _params); } /************************************************************************* - Like MLPCreateC2, but for ensembles. + Randomization of neural network weights and standartisator -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Copyright 10.03.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreatec2(int nin, int nhid1, int nhid2, int nout, int ensemblesize, out mlpensemble ensemble) + public static void mlprandomizefull(multilayerperceptron network) { - ensemble = new mlpensemble(); - mlpe.mlpecreatec2(nin, nhid1, nhid2, nout, ensemblesize, ensemble.innerobj, null); + + mlpbase.mlprandomizefull(network.innerobj, null); } - public static void mlpecreatec2(int nin, int nhid1, int nhid2, int nout, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlprandomizefull(multilayerperceptron network, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreatec2(nin, nhid1, nhid2, nout, ensemblesize, ensemble.innerobj, _params); + + mlpbase.mlprandomizefull(network.innerobj, _params); } /************************************************************************* - Creates ensemble from network. Only network geometry is copied. + Internal subroutine. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 30.03.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpecreatefromnetwork(multilayerperceptron network, int ensemblesize, out mlpensemble ensemble) + public static void mlpinitpreprocessor(multilayerperceptron network, double[,] xy, int ssize) { - ensemble = new mlpensemble(); - mlpe.mlpecreatefromnetwork(network.innerobj, ensemblesize, ensemble.innerobj, null); + + mlpbase.mlpinitpreprocessor(network.innerobj, xy, ssize, null); } - public static void mlpecreatefromnetwork(multilayerperceptron network, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + public static void mlpinitpreprocessor(multilayerperceptron network, double[,] xy, int ssize, alglib.xparams _params) { - ensemble = new mlpensemble(); - mlpe.mlpecreatefromnetwork(network.innerobj, ensemblesize, ensemble.innerobj, _params); + + mlpbase.mlpinitpreprocessor(network.innerobj, xy, ssize, _params); } /************************************************************************* - Randomization of MLP ensemble + Returns information about initialized network: number of inputs, outputs, + weights. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void mlperandomize(mlpensemble ensemble) + public static void mlpproperties(multilayerperceptron network, out int nin, out int nout, out int wcount) { + nin = 0; + nout = 0; + wcount = 0; + mlpbase.mlpproperties(network.innerobj, ref nin, ref nout, ref wcount, null); + } - mlpe.mlperandomize(ensemble.innerobj, null); + public static void mlpproperties(multilayerperceptron network, out int nin, out int nout, out int wcount, alglib.xparams _params) + { + nin = 0; + nout = 0; + wcount = 0; + mlpbase.mlpproperties(network.innerobj, ref nin, ref nout, ref wcount, _params); } - public static void mlperandomize(mlpensemble ensemble, alglib.xparams _params) + /************************************************************************* + Returns number of inputs. + + -- ALGLIB -- + Copyright 19.10.2011 by Bochkanov Sergey + *************************************************************************/ + public static int mlpgetinputscount(multilayerperceptron network) { - mlpe.mlperandomize(ensemble.innerobj, _params); + return mlpbase.mlpgetinputscount(network.innerobj, null); + } + + public static int mlpgetinputscount(multilayerperceptron network, alglib.xparams _params) + { + + return mlpbase.mlpgetinputscount(network.innerobj, _params); } /************************************************************************* - Return ensemble properties (number of inputs and outputs). + Returns number of outputs. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 19.10.2011 by Bochkanov Sergey *************************************************************************/ - public static void mlpeproperties(mlpensemble ensemble, out int nin, out int nout) + public static int mlpgetoutputscount(multilayerperceptron network) { - nin = 0; - nout = 0; - mlpe.mlpeproperties(ensemble.innerobj, ref nin, ref nout, null); + + return mlpbase.mlpgetoutputscount(network.innerobj, null); } - public static void mlpeproperties(mlpensemble ensemble, out int nin, out int nout, alglib.xparams _params) + public static int mlpgetoutputscount(multilayerperceptron network, alglib.xparams _params) { - nin = 0; - nout = 0; - mlpe.mlpeproperties(ensemble.innerobj, ref nin, ref nout, _params); + + return mlpbase.mlpgetoutputscount(network.innerobj, _params); } /************************************************************************* - Return normalization type (whether ensemble is SOFTMAX-normalized or not). + Returns number of weights. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 19.10.2011 by Bochkanov Sergey *************************************************************************/ - public static bool mlpeissoftmax(mlpensemble ensemble) + public static int mlpgetweightscount(multilayerperceptron network) { - return mlpe.mlpeissoftmax(ensemble.innerobj, null); + return mlpbase.mlpgetweightscount(network.innerobj, null); } - public static bool mlpeissoftmax(mlpensemble ensemble, alglib.xparams _params) + public static int mlpgetweightscount(multilayerperceptron network, alglib.xparams _params) { - return mlpe.mlpeissoftmax(ensemble.innerobj, _params); + return mlpbase.mlpgetweightscount(network.innerobj, _params); } /************************************************************************* - Procesing + Tells whether network is SOFTMAX-normalized (i.e. classifier) or not. - INPUT PARAMETERS: - Ensemble- neural networks ensemble - X - input vector, array[0..NIn-1]. - Y - (possibly) preallocated buffer; if size of Y is less than - NOut, it will be reallocated. If it is large enough, it - is NOT reallocated, so we can save some time on reallocation. + -- ALGLIB -- + Copyright 04.11.2007 by Bochkanov Sergey + *************************************************************************/ + public static bool mlpissoftmax(multilayerperceptron network) + { + + return mlpbase.mlpissoftmax(network.innerobj, null); + } + + public static bool mlpissoftmax(multilayerperceptron network, alglib.xparams _params) + { + + return mlpbase.mlpissoftmax(network.innerobj, _params); + } + + /************************************************************************* + This function returns total number of layers (including input, hidden and + output layers). + + -- ALGLIB -- + Copyright 25.03.2011 by Bochkanov Sergey + *************************************************************************/ + public static int mlpgetlayerscount(multilayerperceptron network) + { + + return mlpbase.mlpgetlayerscount(network.innerobj, null); + } + + public static int mlpgetlayerscount(multilayerperceptron network, alglib.xparams _params) + { + + return mlpbase.mlpgetlayerscount(network.innerobj, _params); + } + + /************************************************************************* + This function returns size of K-th layer. + K=0 corresponds to input layer, K=CNT-1 corresponds to output layer. - OUTPUT PARAMETERS: - Y - result. Regression estimate when solving regression task, - vector of posterior probabilities for classification task. + Size of the output layer is always equal to the number of outputs, although + when we have softmax-normalized network, last neuron doesn't have any + connections - it is just zero. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 25.03.2011 by Bochkanov Sergey *************************************************************************/ - public static void mlpeprocess(mlpensemble ensemble, double[] x, ref double[] y) + public static int mlpgetlayersize(multilayerperceptron network, int k) { - mlpe.mlpeprocess(ensemble.innerobj, x, ref y, null); + return mlpbase.mlpgetlayersize(network.innerobj, k, null); } - public static void mlpeprocess(mlpensemble ensemble, double[] x, ref double[] y, alglib.xparams _params) + public static int mlpgetlayersize(multilayerperceptron network, int k, alglib.xparams _params) { - mlpe.mlpeprocess(ensemble.innerobj, x, ref y, _params); + return mlpbase.mlpgetlayersize(network.innerobj, k, _params); } /************************************************************************* - 'interactive' variant of MLPEProcess for languages like Python which - support constructs like "Y = MLPEProcess(LM,X)" and interactive mode of the - interpreter + This function returns offset/scaling coefficients for I-th input of the + network. - This function allocates new array on each call, so it is significantly - slower than its 'non-interactive' counterpart, but it is more convenient - when you call it from command line. + INPUT PARAMETERS: + Network - network + I - input index + + OUTPUT PARAMETERS: + Mean - mean term + Sigma - sigma term, guaranteed to be nonzero. + + I-th input is passed through linear transformation + IN[i] = (IN[i]-Mean)/Sigma + before feeding to the network -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 25.03.2011 by Bochkanov Sergey *************************************************************************/ - public static void mlpeprocessi(mlpensemble ensemble, double[] x, out double[] y) + public static void mlpgetinputscaling(multilayerperceptron network, int i, out double mean, out double sigma) { - y = new double[0]; - mlpe.mlpeprocessi(ensemble.innerobj, x, ref y, null); + mean = 0; + sigma = 0; + mlpbase.mlpgetinputscaling(network.innerobj, i, ref mean, ref sigma, null); } - public static void mlpeprocessi(mlpensemble ensemble, double[] x, out double[] y, alglib.xparams _params) + public static void mlpgetinputscaling(multilayerperceptron network, int i, out double mean, out double sigma, alglib.xparams _params) { - y = new double[0]; - mlpe.mlpeprocessi(ensemble.innerobj, x, ref y, _params); + mean = 0; + sigma = 0; + mlpbase.mlpgetinputscaling(network.innerobj, i, ref mean, ref sigma, _params); } /************************************************************************* - Relative classification error on the test set + This function returns offset/scaling coefficients for I-th output of the + network. INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size + Network - network + I - input index - RESULT: - percent of incorrectly classified cases. - Works both for classifier betwork and for regression networks which - are used as classifiers. + OUTPUT PARAMETERS: + Mean - mean term + Sigma - sigma term, guaranteed to be nonzero. + + I-th output is passed through linear transformation + OUT[i] = OUT[i]*Sigma+Mean + before returning it to user. In case we have SOFTMAX-normalized network, + we return (Mean,Sigma)=(0.0,1.0). -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 25.03.2011 by Bochkanov Sergey *************************************************************************/ - public static double mlperelclserror(mlpensemble ensemble, double[,] xy, int npoints) + public static void mlpgetoutputscaling(multilayerperceptron network, int i, out double mean, out double sigma) { + mean = 0; + sigma = 0; + mlpbase.mlpgetoutputscaling(network.innerobj, i, ref mean, ref sigma, null); + } - return mlpe.mlperelclserror(ensemble.innerobj, xy, npoints, null); + public static void mlpgetoutputscaling(multilayerperceptron network, int i, out double mean, out double sigma, alglib.xparams _params) + { + mean = 0; + sigma = 0; + mlpbase.mlpgetoutputscaling(network.innerobj, i, ref mean, ref sigma, _params); } - public static double mlperelclserror(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + /************************************************************************* + This function returns information about Ith neuron of Kth layer + + INPUT PARAMETERS: + Network - network + K - layer index + I - neuron index (within layer) + + OUTPUT PARAMETERS: + FKind - activation function type (used by MLPActivationFunction()) + this value is zero for input or linear neurons + Threshold - also called offset, bias + zero for input neurons + + NOTE: this function throws exception if layer or neuron with given index + do not exists. + + -- ALGLIB -- + Copyright 25.03.2011 by Bochkanov Sergey + *************************************************************************/ + public static void mlpgetneuroninfo(multilayerperceptron network, int k, int i, out int fkind, out double threshold) { + fkind = 0; + threshold = 0; + mlpbase.mlpgetneuroninfo(network.innerobj, k, i, ref fkind, ref threshold, null); + } - return mlpe.mlperelclserror(ensemble.innerobj, xy, npoints, _params); + public static void mlpgetneuroninfo(multilayerperceptron network, int k, int i, out int fkind, out double threshold, alglib.xparams _params) + { + fkind = 0; + threshold = 0; + mlpbase.mlpgetneuroninfo(network.innerobj, k, i, ref fkind, ref threshold, _params); } /************************************************************************* - Average cross-entropy (in bits per element) on the test set + This function returns information about connection from I0-th neuron of + K0-th layer to I1-th neuron of K1-th layer. INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size + Network - network + K0 - layer index + I0 - neuron index (within layer) + K1 - layer index + I1 - neuron index (within layer) RESULT: - CrossEntropy/(NPoints*LN(2)). - Zero if ensemble solves regression task. + connection weight (zero for non-existent connections) + + This function: + 1. throws exception if layer or neuron with given index do not exists. + 2. returns zero if neurons exist, but there is no connection between them -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 25.03.2011 by Bochkanov Sergey *************************************************************************/ - public static double mlpeavgce(mlpensemble ensemble, double[,] xy, int npoints) + public static double mlpgetweight(multilayerperceptron network, int k0, int i0, int k1, int i1) { - return mlpe.mlpeavgce(ensemble.innerobj, xy, npoints, null); + return mlpbase.mlpgetweight(network.innerobj, k0, i0, k1, i1, null); } - public static double mlpeavgce(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + public static double mlpgetweight(multilayerperceptron network, int k0, int i0, int k1, int i1, alglib.xparams _params) { - return mlpe.mlpeavgce(ensemble.innerobj, xy, npoints, _params); + return mlpbase.mlpgetweight(network.innerobj, k0, i0, k1, i1, _params); } /************************************************************************* - RMS error on the test set + This function sets offset/scaling coefficients for I-th input of the + network. INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size + Network - network + I - input index + Mean - mean term + Sigma - sigma term (if zero, will be replaced by 1.0) - RESULT: - root mean square error. - Its meaning for regression task is obvious. As for classification task - RMS error means error when estimating posterior probabilities. + NTE: I-th input is passed through linear transformation + IN[i] = (IN[i]-Mean)/Sigma + before feeding to the network. This function sets Mean and Sigma. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 25.03.2011 by Bochkanov Sergey *************************************************************************/ - public static double mlpermserror(mlpensemble ensemble, double[,] xy, int npoints) + public static void mlpsetinputscaling(multilayerperceptron network, int i, double mean, double sigma) { - return mlpe.mlpermserror(ensemble.innerobj, xy, npoints, null); + mlpbase.mlpsetinputscaling(network.innerobj, i, mean, sigma, null); } - public static double mlpermserror(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + public static void mlpsetinputscaling(multilayerperceptron network, int i, double mean, double sigma, alglib.xparams _params) { - return mlpe.mlpermserror(ensemble.innerobj, xy, npoints, _params); + mlpbase.mlpsetinputscaling(network.innerobj, i, mean, sigma, _params); } /************************************************************************* - Average error on the test set + This function sets offset/scaling coefficients for I-th output of the + network. INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size + Network - network + I - input index + Mean - mean term + Sigma - sigma term (if zero, will be replaced by 1.0) - RESULT: - Its meaning for regression task is obvious. As for classification task - it means average error when estimating posterior probabilities. + OUTPUT PARAMETERS: + + NOTE: I-th output is passed through linear transformation + OUT[i] = OUT[i]*Sigma+Mean + before returning it to user. This function sets Sigma/Mean. In case we + have SOFTMAX-normalized network, you can not set (Sigma,Mean) to anything + other than(0.0,1.0) - this function will throw exception. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 25.03.2011 by Bochkanov Sergey *************************************************************************/ - public static double mlpeavgerror(mlpensemble ensemble, double[,] xy, int npoints) + public static void mlpsetoutputscaling(multilayerperceptron network, int i, double mean, double sigma) { - return mlpe.mlpeavgerror(ensemble.innerobj, xy, npoints, null); + mlpbase.mlpsetoutputscaling(network.innerobj, i, mean, sigma, null); } - public static double mlpeavgerror(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + public static void mlpsetoutputscaling(multilayerperceptron network, int i, double mean, double sigma, alglib.xparams _params) { - return mlpe.mlpeavgerror(ensemble.innerobj, xy, npoints, _params); + mlpbase.mlpsetoutputscaling(network.innerobj, i, mean, sigma, _params); } /************************************************************************* - Average relative error on the test set + This function modifies information about Ith neuron of Kth layer INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size + Network - network + K - layer index + I - neuron index (within layer) + FKind - activation function type (used by MLPActivationFunction()) + this value must be zero for input neurons + (you can not set activation function for input neurons) + Threshold - also called offset, bias + this value must be zero for input neurons + (you can not set threshold for input neurons) - RESULT: - Its meaning for regression task is obvious. As for classification task - it means average relative error when estimating posterior probabilities. + NOTES: + 1. this function throws exception if layer or neuron with given index do + not exists. + 2. this function also throws exception when you try to set non-linear + activation function for input neurons (any kind of network) or for output + neurons of classifier network. + 3. this function throws exception when you try to set non-zero threshold for + input neurons (any kind of network). -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 25.03.2011 by Bochkanov Sergey *************************************************************************/ - public static double mlpeavgrelerror(mlpensemble ensemble, double[,] xy, int npoints) + public static void mlpsetneuroninfo(multilayerperceptron network, int k, int i, int fkind, double threshold) { - return mlpe.mlpeavgrelerror(ensemble.innerobj, xy, npoints, null); + mlpbase.mlpsetneuroninfo(network.innerobj, k, i, fkind, threshold, null); } - public static double mlpeavgrelerror(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + public static void mlpsetneuroninfo(multilayerperceptron network, int k, int i, int fkind, double threshold, alglib.xparams _params) { - return mlpe.mlpeavgrelerror(ensemble.innerobj, xy, npoints, _params); + mlpbase.mlpsetneuroninfo(network.innerobj, k, i, fkind, threshold, _params); } - -} -public partial class alglib -{ - - + /************************************************************************* - This structure is a clusterization engine. + This function modifies information about connection from I0-th neuron of + K0-th layer to I1-th neuron of K1-th layer. - You should not try to access its fields directly. - Use ALGLIB functions in order to work with this object. + INPUT PARAMETERS: + Network - network + K0 - layer index + I0 - neuron index (within layer) + K1 - layer index + I1 - neuron index (within layer) + W - connection weight (must be zero for non-existent + connections) + + This function: + 1. throws exception if layer or neuron with given index do not exists. + 2. throws exception if you try to set non-zero weight for non-existent + connection -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + Copyright 25.03.2011 by Bochkanov Sergey *************************************************************************/ - public class clusterizerstate : alglibobject + public static void mlpsetweight(multilayerperceptron network, int k0, int i0, int k1, int i1, double w) { - // - // Public declarations - // - public clusterizerstate() - { - _innerobj = new clustering.clusterizerstate(); - } - - public override alglib.alglibobject make_copy() - { - return new clusterizerstate((clustering.clusterizerstate)_innerobj.make_copy()); - } + mlpbase.mlpsetweight(network.innerobj, k0, i0, k1, i1, w, null); + } - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private clustering.clusterizerstate _innerobj; - public clustering.clusterizerstate innerobj { get { return _innerobj; } } - public clusterizerstate(clustering.clusterizerstate obj) - { - _innerobj = obj; - } + public static void mlpsetweight(multilayerperceptron network, int k0, int i0, int k1, int i1, double w, alglib.xparams _params) + { + + mlpbase.mlpsetweight(network.innerobj, k0, i0, k1, i1, w, _params); } - - + /************************************************************************* - This structure is used to store results of the agglomerative hierarchical - clustering (AHC). + Neural network activation function - Following information is returned: + INPUT PARAMETERS: + NET - neuron input + K - function index (zero for linear function) - * TerminationType - completion code: - * 1 for successful completion of algorithm - * -5 inappropriate combination of clustering algorithm and distance - function was used. As for now, it is possible only when Ward's - method is called for dataset with non-Euclidean distance function. - In case negative completion code is returned, other fields of report - structure are invalid and should not be used. + OUTPUT PARAMETERS: + F - function + DF - its derivative + D2F - its second derivative - * NPoints contains number of points in the original dataset + -- ALGLIB -- + Copyright 04.11.2007 by Bochkanov Sergey + *************************************************************************/ + public static void mlpactivationfunction(double net, int k, out double f, out double df, out double d2f) + { + f = 0; + df = 0; + d2f = 0; + mlpbase.mlpactivationfunction(net, k, ref f, ref df, ref d2f, null); + } + + public static void mlpactivationfunction(double net, int k, out double f, out double df, out double d2f, alglib.xparams _params) + { + f = 0; + df = 0; + d2f = 0; + mlpbase.mlpactivationfunction(net, k, ref f, ref df, ref d2f, _params); + } + + /************************************************************************* + Procesing - * Z contains information about merges performed (see below). Z contains - indexes from the original (unsorted) dataset and it can be used when you - need to know what points were merged. However, it is not convenient when - you want to build a dendrograd (see below). + INPUT PARAMETERS: + Network - neural network + X - input vector, array[0..NIn-1]. - * if you want to build dendrogram, you can use Z, but it is not good - option, because Z contains indexes from unsorted dataset. Dendrogram - built from such dataset is likely to have intersections. So, you have to - reorder you points before building dendrogram. - Permutation which reorders point is returned in P. Another representation - of merges, which is more convenient for dendorgram construction, is - returned in PM. + OUTPUT PARAMETERS: + Y - result. Regression estimate when solving regression task, + vector of posterior probabilities for classification task. - * more information on format of Z, P and PM can be found below and in the - examples from ALGLIB Reference Manual. + See also MLPProcessI - FORMAL DESCRIPTION OF FIELDS: - NPoints number of points - Z array[NPoints-1,2], contains indexes of clusters - linked in pairs to form clustering tree. I-th row - corresponds to I-th merge: - * Z[I,0] - index of the first cluster to merge - * Z[I,1] - index of the second cluster to merge - * Z[I,0]=0 - NFeatures number of variables, >=1 - TerminationType completion code: - * -5 if distance type is anything different from - Euclidean metric - * -3 for degenerate dataset: a) less than K distinct - points, b) K=0 for non-empty dataset. - * +1 for successful completion - K number of clusters - C array[K,NFeatures], rows of the array store centers - CIdx array[NPoints], which contains cluster indexes - IterationsCount actual number of iterations performed by clusterizer. - If algorithm performed more than one random restart, - total number of iterations is returned. - Energy merit function, "energy", sum of squared deviations - from cluster centers + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 27.11.2012 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public class kmeansreport : alglibobject + public static double mlperror(multilayerperceptron network, double[,] xy, int npoints) { - // - // Public declarations - // - public int npoints { get { return _innerobj.npoints; } set { _innerobj.npoints = value; } } - public int nfeatures { get { return _innerobj.nfeatures; } set { _innerobj.nfeatures = value; } } - public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } - public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } - public double energy { get { return _innerobj.energy; } set { _innerobj.energy = value; } } - public int k { get { return _innerobj.k; } set { _innerobj.k = value; } } - public double[,] c { get { return _innerobj.c; } set { _innerobj.c = value; } } - public int[] cidx { get { return _innerobj.cidx; } set { _innerobj.cidx = value; } } - - public kmeansreport() - { - _innerobj = new clustering.kmeansreport(); - } - - public override alglib.alglibobject make_copy() - { - return new kmeansreport((clustering.kmeansreport)_innerobj.make_copy()); - } - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private clustering.kmeansreport _innerobj; - public clustering.kmeansreport innerobj { get { return _innerobj; } } - public kmeansreport(clustering.kmeansreport obj) - { - _innerobj = obj; - } + return mlpbase.mlperror(network.innerobj, xy, npoints, null); } - /************************************************************************* - This function initializes clusterizer object. Newly initialized object is - empty, i.e. it does not contain dataset. You should use it as follows: - 1. creation - 2. dataset is added with ClusterizerSetPoints() - 3. additional parameters are set - 3. clusterization is performed with one of the clustering functions - - -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void clusterizercreate(out clusterizerstate s) + public static double mlperror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) { - s = new clusterizerstate(); - clustering.clusterizercreate(s.innerobj, null); - } - public static void clusterizercreate(out clusterizerstate s, alglib.xparams _params) - { - s = new clusterizerstate(); - clustering.clusterizercreate(s.innerobj, _params); + return mlpbase.mlperror(network.innerobj, xy, npoints, _params); } /************************************************************************* - This function adds dataset to the clusterizer structure. + Error of the neural network on dataset given by sparse matrix. - This function overrides all previous calls of ClusterizerSetPoints() or - ClusterizerSetDistances(). + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - S - clusterizer state, initialized by ClusterizerCreate() - XY - array[NPoints,NFeatures], dataset - NPoints - number of points, >=0 - NFeatures- number of features, >=1 - DistType- distance function: - * 0 Chebyshev distance (L-inf norm) - * 1 city block distance (L1 norm) - * 2 Euclidean distance (L2 norm), non-squared - * 10 Pearson correlation: - dist(a,b) = 1-corr(a,b) - * 11 Absolute Pearson correlation: - dist(a,b) = 1-|corr(a,b)| - * 12 Uncentered Pearson correlation (cosine of the angle): - dist(a,b) = a'*b/(|a|*|b|) - * 13 Absolute uncentered Pearson correlation - dist(a,b) = |a'*b|/(|a|*|b|) - * 20 Spearman rank correlation: - dist(a,b) = 1-rankcorr(a,b) - * 21 Absolute Spearman rank correlation - dist(a,b) = 1-|rankcorr(a,b)| + Network - neural network + XY - training set, see below for information on the + training set format. This function checks correctness + of the dataset (no NANs/INFs, class numbers are + correct) and throws exception when incorrect dataset + is passed. Sparse matrix must use CRS format for + storage. + NPoints - points count, >=0 - NOTE 1: different distance functions have different performance penalty: - * Euclidean or Pearson correlation distances are the fastest ones - * Spearman correlation distance function is a bit slower - * city block and Chebyshev distances are order of magnitude slower + RESULT: + sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) - The reason behing difference in performance is that correlation-based - distance functions are computed using optimized linear algebra kernels, - while Chebyshev and city block distance functions are computed using - simple nested loops with two branches at each iteration. + DATASET FORMAT: - NOTE 2: different clustering algorithms have different limitations: - * agglomerative hierarchical clustering algorithms may be used with - any kind of distance metric - * k-means++ clustering algorithm may be used only with Euclidean - distance function - Thus, list of specific clustering algorithms you may use depends - on distance function you specify when you set your dataset. + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void clusterizersetpoints(clusterizerstate s, double[,] xy, int npoints, int nfeatures, int disttype) - { - - clustering.clusterizersetpoints(s.innerobj, xy, npoints, nfeatures, disttype, null); - } - - public static void clusterizersetpoints(clusterizerstate s, double[,] xy, int npoints, int nfeatures, int disttype, alglib.xparams _params) + public static double mlperrorsparse(multilayerperceptron network, sparsematrix xy, int npoints) { - clustering.clusterizersetpoints(s.innerobj, xy, npoints, nfeatures, disttype, _params); + return mlpbase.mlperrorsparse(network.innerobj, xy.innerobj, npoints, null); } - - public static void clusterizersetpoints(clusterizerstate s, double[,] xy, int disttype) - { - int npoints; - int nfeatures; - - - npoints = ap.rows(xy); - nfeatures = ap.cols(xy); - clustering.clusterizersetpoints(s.innerobj, xy, npoints, nfeatures, disttype, null); - return; - } - - public static void clusterizersetpoints(clusterizerstate s, double[,] xy, int disttype, alglib.xparams _params) + public static double mlperrorsparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) { - int npoints; - int nfeatures; - - npoints = ap.rows(xy); - nfeatures = ap.cols(xy); - clustering.clusterizersetpoints(s.innerobj, xy, npoints, nfeatures, disttype, _params); - - return; + return mlpbase.mlperrorsparse(network.innerobj, xy.innerobj, npoints, _params); } /************************************************************************* - This function adds dataset given by distance matrix to the clusterizer - structure. It is important that dataset is not given explicitly - only - distance matrix is given. - - This function overrides all previous calls of ClusterizerSetPoints() or - ClusterizerSetDistances(). - - INPUT PARAMETERS: - S - clusterizer state, initialized by ClusterizerCreate() - D - array[NPoints,NPoints], distance matrix given by its upper - or lower triangle (main diagonal is ignored because its - entries are expected to be zero). - NPoints - number of points - IsUpper - whether upper or lower triangle of D is given. + Natural error function for neural network, internal subroutine. - NOTE 1: different clustering algorithms have different limitations: - * agglomerative hierarchical clustering algorithms may be used with - any kind of distance metric, including one which is given by - distance matrix - * k-means++ clustering algorithm may be used only with Euclidean - distance function and explicitly given points - it can not be - used with dataset given by distance matrix - Thus, if you call this function, you will be unable to use k-means - clustering algorithm to process your problem. + NOTE: this function is single-threaded. Unlike other error function, it + receives no speed-up from being executed in SMP mode. -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void clusterizersetdistances(clusterizerstate s, double[,] d, int npoints, bool isupper) - { - - clustering.clusterizersetdistances(s.innerobj, d, npoints, isupper, null); - } - - public static void clusterizersetdistances(clusterizerstate s, double[,] d, int npoints, bool isupper, alglib.xparams _params) + public static double mlperrorn(multilayerperceptron network, double[,] xy, int ssize) { - clustering.clusterizersetdistances(s.innerobj, d, npoints, isupper, _params); + return mlpbase.mlperrorn(network.innerobj, xy, ssize, null); } - - public static void clusterizersetdistances(clusterizerstate s, double[,] d, bool isupper) - { - int npoints; - if( (ap.rows(d)!=ap.cols(d))) - throw new alglibexception("Error while calling 'clusterizersetdistances': looks like one of arguments has wrong size"); - - npoints = ap.rows(d); - clustering.clusterizersetdistances(s.innerobj, d, npoints, isupper, null); - return; - } - - public static void clusterizersetdistances(clusterizerstate s, double[,] d, bool isupper, alglib.xparams _params) + public static double mlperrorn(multilayerperceptron network, double[,] xy, int ssize, alglib.xparams _params) { - int npoints; - if( (ap.rows(d)!=ap.cols(d))) - throw new alglibexception("Error while calling 'clusterizersetdistances': looks like one of arguments has wrong size"); - - npoints = ap.rows(d); - clustering.clusterizersetdistances(s.innerobj, d, npoints, isupper, _params); - return; + return mlpbase.mlperrorn(network.innerobj, xy, ssize, _params); } /************************************************************************* - This function sets agglomerative hierarchical clustering algorithm + Classification error of the neural network on dataset. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - S - clusterizer state, initialized by ClusterizerCreate() - Algo - algorithm type: - * 0 complete linkage (default algorithm) - * 1 single linkage - * 2 unweighted average linkage - * 3 weighted average linkage - * 4 Ward's method + Network - neural network; + XY - training set, see below for information on the + training set format; + NPoints - points count. - NOTE: Ward's method works correctly only with Euclidean distance, that's - why algorithm will return negative termination code (failure) for - any other distance type. + RESULT: + classification error (number of misclassified cases) - It is possible, however, to use this method with user-supplied - distance matrix. It is your responsibility to pass one which was - calculated with Euclidean distance function. + DATASET FORMAT: + + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void clusterizersetahcalgo(clusterizerstate s, int algo) + public static int mlpclserror(multilayerperceptron network, double[,] xy, int npoints) { - clustering.clusterizersetahcalgo(s.innerobj, algo, null); + return mlpbase.mlpclserror(network.innerobj, xy, npoints, null); } - public static void clusterizersetahcalgo(clusterizerstate s, int algo, alglib.xparams _params) + public static int mlpclserror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) { - clustering.clusterizersetahcalgo(s.innerobj, algo, _params); + return mlpbase.mlpclserror(network.innerobj, xy, npoints, _params); } /************************************************************************* - This function sets k-means properties: number of restarts and maximum - number of iterations per one run. + Relative classification error on the test set. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - S - clusterizer state, initialized by ClusterizerCreate() - Restarts- restarts count, >=1. - k-means++ algorithm performs several restarts and chooses - best set of centers (one with minimum squared distance). - MaxIts - maximum number of k-means iterations performed during one - run. >=0, zero value means that algorithm performs unlimited - number of iterations. + Network - neural network; + XY - training set, see below for information on the + training set format; + NPoints - points count. + + RESULT: + Percent of incorrectly classified cases. Works both for classifier + networks and general purpose networks used as classifiers. + + DATASET FORMAT: + + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + Copyright 25.12.2008 by Bochkanov Sergey *************************************************************************/ - public static void clusterizersetkmeanslimits(clusterizerstate s, int restarts, int maxits) + public static double mlprelclserror(multilayerperceptron network, double[,] xy, int npoints) { - clustering.clusterizersetkmeanslimits(s.innerobj, restarts, maxits, null); + return mlpbase.mlprelclserror(network.innerobj, xy, npoints, null); } - public static void clusterizersetkmeanslimits(clusterizerstate s, int restarts, int maxits, alglib.xparams _params) + public static double mlprelclserror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) { - clustering.clusterizersetkmeanslimits(s.innerobj, restarts, maxits, _params); + return mlpbase.mlprelclserror(network.innerobj, xy, npoints, _params); } /************************************************************************* - This function sets k-means initialization algorithm. Several different - algorithms can be chosen, including k-means++. + Relative classification error on the test set given by sparse matrix. - INPUT PARAMETERS: - S - clusterizer state, initialized by ClusterizerCreate() - InitAlgo- initialization algorithm: - * 0 automatic selection ( different versions of ALGLIB - may select different algorithms) - * 1 random initialization - * 2 k-means++ initialization (best quality of initial - centers, but long non-parallelizable initialization - phase with bad cache locality) - * 3 "fast-greedy" algorithm with efficient, easy to - parallelize initialization. Quality of initial centers - is somewhat worse than that of k-means++. This - algorithm is a default one in the current version of - ALGLIB. - *-1 "debug" algorithm which always selects first K rows - of dataset; this algorithm is used for debug purposes - only. Do not use it in the industrial code! + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. + + INPUT PARAMETERS: + Network - neural network; + XY - training set, see below for information on the + training set format. Sparse matrix must use CRS format + for storage. + NPoints - points count, >=0. + + RESULT: + Percent of incorrectly classified cases. Works both for classifier + networks and general purpose networks used as classifiers. + + DATASET FORMAT: + + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 21.01.2015 by Bochkanov Sergey + Copyright 09.08.2012 by Bochkanov Sergey *************************************************************************/ - public static void clusterizersetkmeansinit(clusterizerstate s, int initalgo) + public static double mlprelclserrorsparse(multilayerperceptron network, sparsematrix xy, int npoints) { - clustering.clusterizersetkmeansinit(s.innerobj, initalgo, null); + return mlpbase.mlprelclserrorsparse(network.innerobj, xy.innerobj, npoints, null); } - public static void clusterizersetkmeansinit(clusterizerstate s, int initalgo, alglib.xparams _params) + public static double mlprelclserrorsparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) { - clustering.clusterizersetkmeansinit(s.innerobj, initalgo, _params); + return mlpbase.mlprelclserrorsparse(network.innerobj, xy.innerobj, npoints, _params); } /************************************************************************* - This function sets seed which is used to initialize internal RNG. By - default, deterministic seed is used - same for each run of clusterizer. If - you specify non-deterministic seed value, then some algorithms which - depend on random initialization (in current version: k-means) may return - slightly different results after each run. + Average cross-entropy (in bits per element) on the test set. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - S - clusterizer state, initialized by ClusterizerCreate() - Seed - seed: - * positive values = use deterministic seed for each run of - algorithms which depend on random initialization - * zero or negative values = use non-deterministic seed + Network - neural network; + XY - training set, see below for information on the + training set format; + NPoints - points count. + + RESULT: + CrossEntropy/(NPoints*LN(2)). + Zero if network solves regression task. + + DATASET FORMAT: + + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 08.06.2017 by Bochkanov Sergey + Copyright 08.01.2009 by Bochkanov Sergey *************************************************************************/ - public static void clusterizersetseed(clusterizerstate s, int seed) + public static double mlpavgce(multilayerperceptron network, double[,] xy, int npoints) { - clustering.clusterizersetseed(s.innerobj, seed, null); + return mlpbase.mlpavgce(network.innerobj, xy, npoints, null); } - public static void clusterizersetseed(clusterizerstate s, int seed, alglib.xparams _params) + public static double mlpavgce(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) { - clustering.clusterizersetseed(s.innerobj, seed, _params); + return mlpbase.mlpavgce(network.innerobj, xy, npoints, _params); } /************************************************************************* - This function performs agglomerative hierarchical clustering - - NOTE: Agglomerative hierarchical clustering algorithm has two phases: - distance matrix calculation and clustering itself. Only first phase - (distance matrix calculation) is accelerated by SIMD and SMP. Thus, - acceleration is significant only for medium or high-dimensional - problems. - - Although activating multithreading gives some speedup over single- - threaded execution, you should not expect nearly-linear scaling - with respect to cores count. - - INPUT PARAMETERS: - S - clusterizer state, initialized by ClusterizerCreate() - - OUTPUT PARAMETERS: - Rep - clustering results; see description of AHCReport - structure for more information. - - NOTE 1: hierarchical clustering algorithms require large amounts of memory. - In particular, this implementation needs sizeof(double)*NPoints^2 - bytes, which are used to store distance matrix. In case we work - with user-supplied matrix, this amount is multiplied by 2 (we have - to store original matrix and to work with its copy). - - For example, problem with 10000 points would require 800M of RAM, - even when working in a 1-dimensional space. - - ! FREE EDITION OF ALGLIB: - ! - ! Free Edition of ALGLIB supports following important features for this - ! function: - ! * C++ version: x64 SIMD support using C++ intrinsics - ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics - ! - ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB - ! Reference Manual in order to find out how to activate SIMD support - ! in ALGLIB. + Average cross-entropy (in bits per element) on the test set given by + sparse matrix. ! COMMERCIAL EDITION OF ALGLIB: ! @@ -4018,81 +4037,60 @@ even when working in a 1-dimensional space. ! of this function: ! * high-performance native backend with same C# interface (C# version) ! * multithreading support (C++ and C# versions) - ! * hardware vendor (Intel, ARM) implementations of linear algebra and - ! other primitives (C++ and C# versions) ! ! We recommend you to read 'Working with commercial version' section of ! ALGLIB Reference Manual in order to find out how to use performance- ! related features provided by commercial edition of ALGLIB. + INPUT PARAMETERS: + Network - neural network; + XY - training set, see below for information on the + training set format. This function checks correctness + of the dataset (no NANs/INFs, class numbers are + correct) and throws exception when incorrect dataset + is passed. Sparse matrix must use CRS format for + storage. + NPoints - points count, >=0. + + RESULT: + CrossEntropy/(NPoints*LN(2)). + Zero if network solves regression task. + + DATASET FORMAT: + + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). + -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + Copyright 9.08.2012 by Bochkanov Sergey *************************************************************************/ - public static void clusterizerrunahc(clusterizerstate s, out ahcreport rep) + public static double mlpavgcesparse(multilayerperceptron network, sparsematrix xy, int npoints) { - rep = new ahcreport(); - clustering.clusterizerrunahc(s.innerobj, rep.innerobj, null); + + return mlpbase.mlpavgcesparse(network.innerobj, xy.innerobj, npoints, null); } - public static void clusterizerrunahc(clusterizerstate s, out ahcreport rep, alglib.xparams _params) + public static double mlpavgcesparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) { - rep = new ahcreport(); - clustering.clusterizerrunahc(s.innerobj, rep.innerobj, _params); + + return mlpbase.mlpavgcesparse(network.innerobj, xy.innerobj, npoints, _params); } /************************************************************************* - This function performs clustering by k-means++ algorithm. - - You may change algorithm properties by calling: - * ClusterizerSetKMeansLimits() to change number of restarts or iterations - * ClusterizerSetKMeansInit() to change initialization algorithm - - By default, one restart and unlimited number of iterations are used. - Initialization algorithm is chosen automatically. - - NOTE: k-means clustering algorithm has two phases: selection of initial - centers and clustering itself. ALGLIB parallelizes both phases. - Parallel version is optimized for the following scenario: medium or - high-dimensional problem (8 or more dimensions) with large number of - points and clusters. However, some speed-up can be obtained even - when assumptions above are violated. - - INPUT PARAMETERS: - S - clusterizer state, initialized by ClusterizerCreate() - K - number of clusters, K>=0. - K can be zero only when algorithm is called for empty - dataset, in this case completion code is set to - success (+1). - If K=0 and dataset size is non-zero, we can not - meaningfully assign points to some center (there are no - centers because K=0) and return -3 as completion code - (failure). - - OUTPUT PARAMETERS: - Rep - clustering results; see description of KMeansReport - structure for more information. - - NOTE 1: k-means clustering can be performed only for datasets with - Euclidean distance function. Algorithm will return negative - completion code in Rep.TerminationType in case dataset was added - to clusterizer with DistType other than Euclidean (or dataset was - specified by distance matrix instead of explicitly given points). - - NOTE 2: by default, k-means uses non-deterministic seed to initialize RNG - which is used to select initial centers. As result, each run of - algorithm may return different values. If you need deterministic - behavior, use ClusterizerSetSeed() function. - - ! FREE EDITION OF ALGLIB: - ! - ! Free Edition of ALGLIB supports following important features for this - ! function: - ! * C++ version: x64 SIMD support using C++ intrinsics - ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics - ! - ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB - ! Reference Manual in order to find out how to activate SIMD support - ! in ALGLIB. + RMS error on the test set given. ! COMMERCIAL EDITION OF ALGLIB: ! @@ -4100,76 +4098,57 @@ to clusterizer with DistType other than Euclidean (or dataset was ! of this function: ! * high-performance native backend with same C# interface (C# version) ! * multithreading support (C++ and C# versions) - ! * hardware vendor (Intel, ARM) implementations of linear algebra and - ! other primitives (C++ and C# versions) ! ! We recommend you to read 'Working with commercial version' section of ! ALGLIB Reference Manual in order to find out how to use performance- ! related features provided by commercial edition of ALGLIB. + INPUT PARAMETERS: + Network - neural network; + XY - training set, see below for information on the + training set format; + NPoints - points count. + + RESULT: + Root mean square error. Its meaning for regression task is obvious. As for + classification task, RMS error means error when estimating posterior + probabilities. + + DATASET FORMAT: + + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). + -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void clusterizerrunkmeans(clusterizerstate s, int k, out kmeansreport rep) + public static double mlprmserror(multilayerperceptron network, double[,] xy, int npoints) { - rep = new kmeansreport(); - clustering.clusterizerrunkmeans(s.innerobj, k, rep.innerobj, null); + + return mlpbase.mlprmserror(network.innerobj, xy, npoints, null); } - public static void clusterizerrunkmeans(clusterizerstate s, int k, out kmeansreport rep, alglib.xparams _params) + public static double mlprmserror(multilayerperceptron network, double[,] xy, int npoints, alglib.xparams _params) { - rep = new kmeansreport(); - clustering.clusterizerrunkmeans(s.innerobj, k, rep.innerobj, _params); + + return mlpbase.mlprmserror(network.innerobj, xy, npoints, _params); } /************************************************************************* - This function returns distance matrix for dataset - - INPUT PARAMETERS: - XY - array[NPoints,NFeatures], dataset - NPoints - number of points, >=0 - NFeatures- number of features, >=1 - DistType- distance function: - * 0 Chebyshev distance (L-inf norm) - * 1 city block distance (L1 norm) - * 2 Euclidean distance (L2 norm, non-squared) - * 10 Pearson correlation: - dist(a,b) = 1-corr(a,b) - * 11 Absolute Pearson correlation: - dist(a,b) = 1-|corr(a,b)| - * 12 Uncentered Pearson correlation (cosine of the angle): - dist(a,b) = a'*b/(|a|*|b|) - * 13 Absolute uncentered Pearson correlation - dist(a,b) = |a'*b|/(|a|*|b|) - * 20 Spearman rank correlation: - dist(a,b) = 1-rankcorr(a,b) - * 21 Absolute Spearman rank correlation - dist(a,b) = 1-|rankcorr(a,b)| - - OUTPUT PARAMETERS: - D - array[NPoints,NPoints], distance matrix - (full matrix is returned, with lower and upper triangles) - - NOTE: different distance functions have different performance penalty: - * Euclidean or Pearson correlation distances are the fastest ones - * Spearman correlation distance function is a bit slower - * city block and Chebyshev distances are order of magnitude slower - - The reason behing difference in performance is that correlation-based - distance functions are computed using optimized linear algebra kernels, - while Chebyshev and city block distance functions are computed using - simple nested loops with two branches at each iteration. - - ! FREE EDITION OF ALGLIB: - ! - ! Free Edition of ALGLIB supports following important features for this - ! function: - ! * C++ version: x64 SIMD support using C++ intrinsics - ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics - ! - ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB - ! Reference Manual in order to find out how to activate SIMD support - ! in ALGLIB. + RMS error on the test set given by sparse matrix. ! COMMERCIAL EDITION OF ALGLIB: ! @@ -4177,1803 +4156,2385 @@ simple nested loops with two branches at each iteration. ! of this function: ! * high-performance native backend with same C# interface (C# version) ! * multithreading support (C++ and C# versions) - ! * hardware vendor (Intel, ARM) implementations of linear algebra and - ! other primitives (C++ and C# versions) ! ! We recommend you to read 'Working with commercial version' section of ! ALGLIB Reference Manual in order to find out how to use performance- ! related features provided by commercial edition of ALGLIB. + INPUT PARAMETERS: + Network - neural network; + XY - training set, see below for information on the + training set format. This function checks correctness + of the dataset (no NANs/INFs, class numbers are + correct) and throws exception when incorrect dataset + is passed. Sparse matrix must use CRS format for + storage. + NPoints - points count, >=0. + + RESULT: + Root mean square error. Its meaning for regression task is obvious. As for + classification task, RMS error means error when estimating posterior + probabilities. + + DATASET FORMAT: + + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). + -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + Copyright 09.08.2012 by Bochkanov Sergey *************************************************************************/ - public static void clusterizergetdistances(double[,] xy, int npoints, int nfeatures, int disttype, out double[,] d) + public static double mlprmserrorsparse(multilayerperceptron network, sparsematrix xy, int npoints) { - d = new double[0,0]; - clustering.clusterizergetdistances(xy, npoints, nfeatures, disttype, ref d, null); + + return mlpbase.mlprmserrorsparse(network.innerobj, xy.innerobj, npoints, null); } - public static void clusterizergetdistances(double[,] xy, int npoints, int nfeatures, int disttype, out double[,] d, alglib.xparams _params) + public static double mlprmserrorsparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) { - d = new double[0,0]; - clustering.clusterizergetdistances(xy, npoints, nfeatures, disttype, ref d, _params); + + return mlpbase.mlprmserrorsparse(network.innerobj, xy.innerobj, npoints, _params); } /************************************************************************* - This function takes as input clusterization report Rep, desired clusters - count K, and builds top K clusters from hierarchical clusterization tree. - It returns assignment of points to clusters (array of cluster indexes). + Average absolute error on the test set. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - Rep - report from ClusterizerRunAHC() performed on XY - K - desired number of clusters, 1<=K<=NPoints. - K can be zero only when NPoints=0. + Network - neural network; + XY - training set, see below for information on the + training set format; + NPoints - points count. - OUTPUT PARAMETERS: - CIdx - array[NPoints], I-th element contains cluster index (from - 0 to K-1) for I-th point of the dataset. - CZ - array[K]. This array allows to convert cluster indexes - returned by this function to indexes used by Rep.Z. J-th - cluster returned by this function corresponds to CZ[J]-th - cluster stored in Rep.Z/PZ/PM. - It is guaranteed that CZ[I]=0 + Network - neural network; + XY - training set, see below for information on the + training set format. This function checks correctness + of the dataset (no NANs/INFs, class numbers are + correct) and throws exception when incorrect dataset + is passed. Sparse matrix must use CRS format for + storage. + NPoints - points count, >=0. - OUTPUT PARAMETERS: - K - number of clusters, 1<=K<=NPoints - CIdx - array[NPoints], I-th element contains cluster index (from - 0 to K-1) for I-th point of the dataset. - CZ - array[K]. This array allows to convert cluster indexes - returned by this function to indexes used by Rep.Z. J-th - cluster returned by this function corresponds to CZ[J]-th - cluster stored in Rep.Z/PZ/PM. - It is guaranteed that CZ[I]=0. + RESULT: + Its meaning for regression task is obvious. As for classification task, it + means average relative error when estimating posterior probability of + belonging to the correct class. - /************************************************************************* - A random forest (decision forest) builder object. + DATASET FORMAT: - Used to store dataset and specify decision forest training algorithm settings. + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). + + -- ALGLIB -- + Copyright 09.08.2012 by Bochkanov Sergey *************************************************************************/ - public class decisionforestbuilder : alglibobject + public static double mlpavgrelerrorsparse(multilayerperceptron network, sparsematrix xy, int npoints) { - // - // Public declarations - // - public decisionforestbuilder() - { - _innerobj = new dforest.decisionforestbuilder(); - } - - public override alglib.alglibobject make_copy() - { - return new decisionforestbuilder((dforest.decisionforestbuilder)_innerobj.make_copy()); - } + return mlpbase.mlpavgrelerrorsparse(network.innerobj, xy.innerobj, npoints, null); + } - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private dforest.decisionforestbuilder _innerobj; - public dforest.decisionforestbuilder innerobj { get { return _innerobj; } } - public decisionforestbuilder(dforest.decisionforestbuilder obj) - { - _innerobj = obj; - } + public static double mlpavgrelerrorsparse(multilayerperceptron network, sparsematrix xy, int npoints, alglib.xparams _params) + { + + return mlpbase.mlpavgrelerrorsparse(network.innerobj, xy.innerobj, npoints, _params); } + + /************************************************************************* + Gradient calculation + INPUT PARAMETERS: + Network - network initialized with one of the network creation funcs + X - input vector, length of array must be at least NIn + DesiredY- desired outputs, length of array must be at least NOut + Grad - possibly preallocated array. If size of array is smaller + than WCount, it will be reallocated. It is recommended to + reuse previously allocated array to reduce allocation + overhead. - /************************************************************************* - Buffer object which is used to perform various requests (usually model - inference) in the multithreaded mode (multiple threads working with same - DF object). + OUTPUT PARAMETERS: + E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) + Grad - gradient of E with respect to weights of network, array[WCount] - This object should be created with DFCreateBuffer(). + -- ALGLIB -- + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public class decisionforestbuffer : alglibobject + public static void mlpgrad(multilayerperceptron network, double[] x, double[] desiredy, out double e, ref double[] grad) { - // - // Public declarations - // - - public decisionforestbuffer() - { - _innerobj = new dforest.decisionforestbuffer(); - } - - public override alglib.alglibobject make_copy() - { - return new decisionforestbuffer((dforest.decisionforestbuffer)_innerobj.make_copy()); - } + e = 0; + mlpbase.mlpgrad(network.innerobj, x, desiredy, ref e, ref grad, null); + } - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private dforest.decisionforestbuffer _innerobj; - public dforest.decisionforestbuffer innerobj { get { return _innerobj; } } - public decisionforestbuffer(dforest.decisionforestbuffer obj) - { - _innerobj = obj; - } + public static void mlpgrad(multilayerperceptron network, double[] x, double[] desiredy, out double e, ref double[] grad, alglib.xparams _params) + { + e = 0; + mlpbase.mlpgrad(network.innerobj, x, desiredy, ref e, ref grad, _params); } + + /************************************************************************* + Gradient calculation (natural error function is used) + + INPUT PARAMETERS: + Network - network initialized with one of the network creation funcs + X - input vector, length of array must be at least NIn + DesiredY- desired outputs, length of array must be at least NOut + Grad - possibly preallocated array. If size of array is smaller + than WCount, it will be reallocated. It is recommended to + reuse previously allocated array to reduce allocation + overhead. + OUTPUT PARAMETERS: + E - error function, sum-of-squares for regression networks, + cross-entropy for classification networks. + Grad - gradient of E with respect to weights of network, array[WCount] - /************************************************************************* - Decision forest (random forest) model. + -- ALGLIB -- + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public class decisionforest : alglibobject + public static void mlpgradn(multilayerperceptron network, double[] x, double[] desiredy, out double e, ref double[] grad) { - // - // Public declarations - // - - public decisionforest() - { - _innerobj = new dforest.decisionforest(); - } - - public override alglib.alglibobject make_copy() - { - return new decisionforest((dforest.decisionforest)_innerobj.make_copy()); - } + e = 0; + mlpbase.mlpgradn(network.innerobj, x, desiredy, ref e, ref grad, null); + } - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private dforest.decisionforest _innerobj; - public dforest.decisionforest innerobj { get { return _innerobj; } } - public decisionforest(dforest.decisionforest obj) - { - _innerobj = obj; - } + public static void mlpgradn(multilayerperceptron network, double[] x, double[] desiredy, out double e, ref double[] grad, alglib.xparams _params) + { + e = 0; + mlpbase.mlpgradn(network.innerobj, x, desiredy, ref e, ref grad, _params); } - - + /************************************************************************* - Decision forest training report. - - === training/oob errors ================================================== - - Following fields store training set errors: - * relclserror - fraction of misclassified cases, [0,1] - * avgce - average cross-entropy in bits per symbol - * rmserror - root-mean-square error - * avgerror - average error - * avgrelerror - average relative error - - Out-of-bag estimates are stored in fields with same names, but "oob" prefix. - - For classification problems: - * RMS, AVG and AVGREL errors are calculated for posterior probabilities + Batch gradient calculation for a set of inputs/outputs - For regression problems: - * RELCLS and AVGCE errors are zero + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. - === variable importance ================================================== + INPUT PARAMETERS: + Network - network initialized with one of the network creation funcs + XY - original dataset in dense format; one sample = one row: + * first NIn columns contain inputs, + * for regression problem, next NOut columns store + desired outputs. + * for classification problem, next column (just one!) + stores class number. + SSize - number of elements in XY + Grad - possibly preallocated array. If size of array is smaller + than WCount, it will be reallocated. It is recommended to + reuse previously allocated array to reduce allocation + overhead. - Following fields are used to store variable importance information: + OUTPUT PARAMETERS: + E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) + Grad - gradient of E with respect to weights of network, array[WCount] - * topvars - variables ordered from the most important to - less important ones (according to current - choice of importance raiting). - For example, topvars[0] contains index of the - most important variable, and topvars[0:2] are - indexes of 3 most important ones and so on. + -- ALGLIB -- + Copyright 04.11.2007 by Bochkanov Sergey + *************************************************************************/ + public static void mlpgradbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad) + { + e = 0; + mlpbase.mlpgradbatch(network.innerobj, xy, ssize, ref e, ref grad, null); + } + + public static void mlpgradbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, alglib.xparams _params) + { + e = 0; + mlpbase.mlpgradbatch(network.innerobj, xy, ssize, ref e, ref grad, _params); + } + + /************************************************************************* + Batch gradient calculation for a set of inputs/outputs given by sparse + matrices - * varimportances - array[nvars], ratings (the larger, the more - important the variable is, always in [0,1] - range). - By default, filled by zeros (no importance - ratings are provided unless you explicitly - request them). - Zero rating means that variable is not important, - however you will rarely encounter such a thing, - in many cases unimportant variables produce - nearly-zero (but nonzero) ratings. + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. - Variable importance report must be EXPLICITLY requested by calling: - * dfbuildersetimportancegini() function, if you need out-of-bag Gini-based - importance rating also known as MDI (fast to calculate, resistant to - overfitting issues, but has some bias towards continuous and - high-cardinality categorical variables) - * dfbuildersetimportancetrngini() function, if you need training set Gini- - -based importance rating (what other packages typically report). - * dfbuildersetimportancepermutation() function, if you need permutation- - based importance rating also known as MDA (slower to calculate, but less - biased) - * dfbuildersetimportancenone() function, if you do not need importance - ratings - ratings will be zero, topvars[] will be [0,1,2,...] + INPUT PARAMETERS: + Network - network initialized with one of the network creation funcs + XY - original dataset in sparse format; one sample = one row: + * MATRIX MUST BE STORED IN CRS FORMAT + * first NIn columns contain inputs. + * for regression problem, next NOut columns store + desired outputs. + * for classification problem, next column (just one!) + stores class number. + SSize - number of elements in XY + Grad - possibly preallocated array. If size of array is smaller + than WCount, it will be reallocated. It is recommended to + reuse previously allocated array to reduce allocation + overhead. - Different importance ratings (Gini or permutation) produce non-comparable - values. Although in all cases rating values lie in [0,1] range, there are - exist differences: - * informally speaking, Gini importance rating tends to divide "unit amount - of importance" between several important variables, i.e. it produces - estimates which roughly sum to 1.0 (or less than 1.0, if your task can - not be solved exactly). If all variables are equally important, they - will have same rating, roughly 1/NVars, even if every variable is - critically important. - * from the other side, permutation importance tells us what percentage of - the model predictive power will be ruined by permuting this specific - variable. It does not produce estimates which sum to one. Critically - important variable will have rating close to 1.0, and you may have - multiple variables with such a rating. + OUTPUT PARAMETERS: + E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) + Grad - gradient of E with respect to weights of network, array[WCount] - More information on variable importance ratings can be found in comments - on the dfbuildersetimportancegini() and dfbuildersetimportancepermutation() - functions. + -- ALGLIB -- + Copyright 26.07.2012 by Bochkanov Sergey *************************************************************************/ - public class dfreport : alglibobject + public static void mlpgradbatchsparse(multilayerperceptron network, sparsematrix xy, int ssize, out double e, ref double[] grad) { - // - // Public declarations - // - public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } - public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } - public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } - public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } - public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } - public double oobrelclserror { get { return _innerobj.oobrelclserror; } set { _innerobj.oobrelclserror = value; } } - public double oobavgce { get { return _innerobj.oobavgce; } set { _innerobj.oobavgce = value; } } - public double oobrmserror { get { return _innerobj.oobrmserror; } set { _innerobj.oobrmserror = value; } } - public double oobavgerror { get { return _innerobj.oobavgerror; } set { _innerobj.oobavgerror = value; } } - public double oobavgrelerror { get { return _innerobj.oobavgrelerror; } set { _innerobj.oobavgrelerror = value; } } - public int[] topvars { get { return _innerobj.topvars; } set { _innerobj.topvars = value; } } - public double[] varimportances { get { return _innerobj.varimportances; } set { _innerobj.varimportances = value; } } - - public dfreport() - { - _innerobj = new dforest.dfreport(); - } - - public override alglib.alglibobject make_copy() - { - return new dfreport((dforest.dfreport)_innerobj.make_copy()); - } + e = 0; + mlpbase.mlpgradbatchsparse(network.innerobj, xy.innerobj, ssize, ref e, ref grad, null); + } - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private dforest.dfreport _innerobj; - public dforest.dfreport innerobj { get { return _innerobj; } } - public dfreport(dforest.dfreport obj) - { - _innerobj = obj; - } - } - - - /************************************************************************* - This function serializes data structure to string/stream. - - Important properties of s_out: - * it contains alphanumeric characters, dots, underscores, minus signs - * these symbols are grouped into words, which are separated by spaces - and Windows-style (CR+LF) newlines - * although serializer uses spaces and CR+LF as separators, you can - replace any separator character by arbitrary combination of spaces, - tabs, Windows or Unix newlines. It allows flexible reformatting of - the string in case you want to include it into a text or XML file. - But you should not insert separators into the middle of the "words" - nor should you change the case of letters. - * s_out can be freely moved between 32-bit and 64-bit systems, little - and big endian machines, and so on. You can serialize structure on - 32-bit machine and unserialize it on 64-bit one (or vice versa), or - serialize it on SPARC and unserialize on x86. You can also - serialize it in C++ version of ALGLIB and unserialize it in C# one, - and vice versa. - *************************************************************************/ - public static void dfserialize(decisionforest obj, out string s_out) + public static void mlpgradbatchsparse(multilayerperceptron network, sparsematrix xy, int ssize, out double e, ref double[] grad, alglib.xparams _params) { - alglib.serializer s = new alglib.serializer(); - s.alloc_start(); - dforest.dfalloc(s, obj.innerobj, null); - s.sstart_str(); - dforest.dfserialize(s, obj.innerobj, null); - s.stop(); - s_out = s.get_string(); + e = 0; + mlpbase.mlpgradbatchsparse(network.innerobj, xy.innerobj, ssize, ref e, ref grad, _params); } + + /************************************************************************* + Batch gradient calculation for a subset of dataset + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. - /************************************************************************* - This function serializes data structure to string/stream. - - Important properties of s_out: - * it contains alphanumeric characters, dots, underscores, minus signs - * these symbols are grouped into words, which are separated by spaces - and Windows-style (CR+LF) newlines - * although serializer uses spaces and CR+LF as separators, you can - replace any separator character by arbitrary combination of spaces, - tabs, Windows or Unix newlines. It allows flexible reformatting of - the string in case you want to include it into a text or XML file. - But you should not insert separators into the middle of the "words" - nor should you change the case of letters. - * s_out can be freely moved between 32-bit and 64-bit systems, little - and big endian machines, and so on. You can serialize structure on - 32-bit machine and unserialize it on 64-bit one (or vice versa), or - serialize it on SPARC and unserialize on x86. You can also - serialize it in C++ version of ALGLIB and unserialize it in C# one, - and vice versa. - *************************************************************************/ - public static void dfserialize(decisionforest obj, System.IO.Stream stream_out) - { - alglib.serializer s = new alglib.serializer(); - s.alloc_start(); - dforest.dfalloc(s, obj.innerobj, null); - s.sstart_stream(stream_out); - dforest.dfserialize(s, obj.innerobj, null); - s.stop(); - } + INPUT PARAMETERS: + Network - network initialized with one of the network creation funcs + XY - original dataset in dense format; one sample = one row: + * first NIn columns contain inputs, + * for regression problem, next NOut columns store + desired outputs. + * for classification problem, next column (just one!) + stores class number. + SetSize - real size of XY, SetSize>=0; + Idx - subset of SubsetSize elements, array[SubsetSize]: + * Idx[I] stores row index in the original dataset which is + given by XY. Gradient is calculated with respect to rows + whose indexes are stored in Idx[]. + * Idx[] must store correct indexes; this function throws + an exception in case incorrect index (less than 0 or + larger than rows(XY)) is given + * Idx[] may store indexes in any order and even with + repetitions. + SubsetSize- number of elements in Idx[] array: + * positive value means that subset given by Idx[] is processed + * zero value results in zero gradient + * negative value means that full dataset is processed + Grad - possibly preallocated array. If size of array is smaller + than WCount, it will be reallocated. It is recommended to + reuse previously allocated array to reduce allocation + overhead. + OUTPUT PARAMETERS: + E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) + Grad - gradient of E with respect to weights of network, + array[WCount] - /************************************************************************* - This function unserializes data structure from string/stream. + -- ALGLIB -- + Copyright 26.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void dfunserialize(string s_in, out decisionforest obj) + public static void mlpgradbatchsubset(multilayerperceptron network, double[,] xy, int setsize, int[] idx, int subsetsize, out double e, ref double[] grad) { - alglib.serializer s = new alglib.serializer(); - obj = new decisionforest(); - s.ustart_str(s_in); - dforest.dfunserialize(s, obj.innerobj, null); - s.stop(); + e = 0; + mlpbase.mlpgradbatchsubset(network.innerobj, xy, setsize, idx, subsetsize, ref e, ref grad, null); } - - - /************************************************************************* - This function unserializes data structure from string/stream. - *************************************************************************/ - public static void dfunserialize(System.IO.Stream stream_in, out decisionforest obj) + public static void mlpgradbatchsubset(multilayerperceptron network, double[,] xy, int setsize, int[] idx, int subsetsize, out double e, ref double[] grad, alglib.xparams _params) { - alglib.serializer s = new alglib.serializer(); - obj = new decisionforest(); - s.ustart_stream(stream_in); - dforest.dfunserialize(s, obj.innerobj, null); - s.stop(); + e = 0; + mlpbase.mlpgradbatchsubset(network.innerobj, xy, setsize, idx, subsetsize, ref e, ref grad, _params); } /************************************************************************* - This function creates buffer structure which can be used to perform - parallel inference requests. - - DF subpackage provides two sets of computing functions - ones which use - internal buffer of DF model (these functions are single-threaded because - they use same buffer, which can not shared between threads), and ones - which use external buffer. - - This function is used to initialize external buffer. + Batch gradient calculation for a set of inputs/outputs for a subset of + dataset given by set of indexes. - INPUT PARAMETERS - Model - DF model which is associated with newly created buffer + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. - OUTPUT PARAMETERS - Buf - external buffer. + INPUT PARAMETERS: + Network - network initialized with one of the network creation funcs + XY - original dataset in sparse format; one sample = one row: + * MATRIX MUST BE STORED IN CRS FORMAT + * first NIn columns contain inputs, + * for regression problem, next NOut columns store + desired outputs. + * for classification problem, next column (just one!) + stores class number. + SetSize - real size of XY, SetSize>=0; + Idx - subset of SubsetSize elements, array[SubsetSize]: + * Idx[I] stores row index in the original dataset which is + given by XY. Gradient is calculated with respect to rows + whose indexes are stored in Idx[]. + * Idx[] must store correct indexes; this function throws + an exception in case incorrect index (less than 0 or + larger than rows(XY)) is given + * Idx[] may store indexes in any order and even with + repetitions. + SubsetSize- number of elements in Idx[] array: + * positive value means that subset given by Idx[] is processed + * zero value results in zero gradient + * negative value means that full dataset is processed + Grad - possibly preallocated array. If size of array is smaller + than WCount, it will be reallocated. It is recommended to + reuse previously allocated array to reduce allocation + overhead. + OUTPUT PARAMETERS: + E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) + Grad - gradient of E with respect to weights of network, + array[WCount] - IMPORTANT: buffer object should be used only with model which was used to - initialize buffer. Any attempt to use buffer with different - object is dangerous - you may get integrity check failure - (exception) because sizes of internal arrays do not fit to - dimensions of the model structure. + NOTE: when SubsetSize<0 is used full dataset by call MLPGradBatchSparse + function. -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 26.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void dfcreatebuffer(decisionforest model, out decisionforestbuffer buf) + public static void mlpgradbatchsparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] idx, int subsetsize, out double e, ref double[] grad) { - buf = new decisionforestbuffer(); - dforest.dfcreatebuffer(model.innerobj, buf.innerobj, null); + e = 0; + mlpbase.mlpgradbatchsparsesubset(network.innerobj, xy.innerobj, setsize, idx, subsetsize, ref e, ref grad, null); } - public static void dfcreatebuffer(decisionforest model, out decisionforestbuffer buf, alglib.xparams _params) + public static void mlpgradbatchsparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] idx, int subsetsize, out double e, ref double[] grad, alglib.xparams _params) { - buf = new decisionforestbuffer(); - dforest.dfcreatebuffer(model.innerobj, buf.innerobj, _params); + e = 0; + mlpbase.mlpgradbatchsparsesubset(network.innerobj, xy.innerobj, setsize, idx, subsetsize, ref e, ref grad, _params); } /************************************************************************* - This subroutine creates DecisionForestBuilder object which is used to - train decision forests. - - By default, new builder stores empty dataset and some reasonable default - settings. At the very least, you should specify dataset prior to building - decision forest. You can also tweak settings of the forest construction - algorithm (recommended, although default setting should work well). - - Following actions are mandatory: - * calling dfbuildersetdataset() to specify dataset - * calling dfbuilderbuildrandomforest() to build decision forest using - current dataset and default settings - - Additionally, you may call: - * dfbuildersetrndvars() or dfbuildersetrndvarsratio() to specify number of - variables randomly chosen for each split - * dfbuildersetsubsampleratio() to specify fraction of the dataset randomly - subsampled to build each tree - * dfbuildersetseed() to control random seed chosen for tree construction + Batch gradient calculation for a set of inputs/outputs + (natural error function is used) INPUT PARAMETERS: - none + Network - network initialized with one of the network creation funcs + XY - set of inputs/outputs; one sample = one row; + first NIn columns contain inputs, + next NOut columns - desired outputs. + SSize - number of elements in XY + Grad - possibly preallocated array. If size of array is smaller + than WCount, it will be reallocated. It is recommended to + reuse previously allocated array to reduce allocation + overhead. OUTPUT PARAMETERS: - S - decision forest builder + E - error function, sum-of-squares for regression networks, + cross-entropy for classification networks. + Grad - gradient of E with respect to weights of network, array[WCount] -- ALGLIB -- - Copyright 21.05.2018 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void dfbuildercreate(out decisionforestbuilder s) + public static void mlpgradnbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad) { - s = new decisionforestbuilder(); - dforest.dfbuildercreate(s.innerobj, null); + e = 0; + mlpbase.mlpgradnbatch(network.innerobj, xy, ssize, ref e, ref grad, null); } - public static void dfbuildercreate(out decisionforestbuilder s, alglib.xparams _params) + public static void mlpgradnbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, alglib.xparams _params) { - s = new decisionforestbuilder(); - dforest.dfbuildercreate(s.innerobj, _params); + e = 0; + mlpbase.mlpgradnbatch(network.innerobj, xy, ssize, ref e, ref grad, _params); } /************************************************************************* - This subroutine adds dense dataset to the internal storage of the builder - object. Specifying your dataset in the dense format means that the dense - version of the forest construction algorithm will be invoked. - - INPUT PARAMETERS: - S - decision forest builder object - XY - array[NPoints,NVars+1] (minimum size; actual size can - be larger, only leading part is used anyway), dataset: - * first NVars elements of each row store values of the - independent variables - * last column store class number (in 0...NClasses-1) - or real value of the dependent variable - NPoints - number of rows in the dataset, NPoints>=1 - NVars - number of independent variables, NVars>=1 - NClasses - indicates type of the problem being solved: - * NClasses>=2 means that classification problem is - solved (last column of the dataset stores class - number) - * NClasses=1 means that regression problem is solved - (last column of the dataset stores variable value) - - OUTPUT PARAMETERS: - S - decision forest builder + Batch Hessian calculation (natural error function) using R-algorithm. + Internal subroutine. -- ALGLIB -- - Copyright 21.05.2018 by Bochkanov Sergey + Copyright 26.01.2008 by Bochkanov Sergey. + + Hessian calculation based on R-algorithm described in + "Fast Exact Multiplication by the Hessian", + B. A. Pearlmutter, + Neural Computation, 1994. *************************************************************************/ - public static void dfbuildersetdataset(decisionforestbuilder s, double[,] xy, int npoints, int nvars, int nclasses) + public static void mlphessiannbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, ref double[,] h) { - - dforest.dfbuildersetdataset(s.innerobj, xy, npoints, nvars, nclasses, null); + e = 0; + mlpbase.mlphessiannbatch(network.innerobj, xy, ssize, ref e, ref grad, ref h, null); } - public static void dfbuildersetdataset(decisionforestbuilder s, double[,] xy, int npoints, int nvars, int nclasses, alglib.xparams _params) + public static void mlphessiannbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, ref double[,] h, alglib.xparams _params) { - - dforest.dfbuildersetdataset(s.innerobj, xy, npoints, nvars, nclasses, _params); + e = 0; + mlpbase.mlphessiannbatch(network.innerobj, xy, ssize, ref e, ref grad, ref h, _params); } /************************************************************************* - This function sets number of variables (in [1,NVars] range) used by - decision forest construction algorithm. - - The default option is to use roughly sqrt(NVars) variables. - - INPUT PARAMETERS: - S - decision forest builder object - RndVars - number of randomly selected variables; values outside - of [1,NVars] range are silently clipped. - - OUTPUT PARAMETERS: - S - decision forest builder + Batch Hessian calculation using R-algorithm. + Internal subroutine. -- ALGLIB -- - Copyright 21.05.2018 by Bochkanov Sergey + Copyright 26.01.2008 by Bochkanov Sergey. + + Hessian calculation based on R-algorithm described in + "Fast Exact Multiplication by the Hessian", + B. A. Pearlmutter, + Neural Computation, 1994. *************************************************************************/ - public static void dfbuildersetrndvars(decisionforestbuilder s, int rndvars) + public static void mlphessianbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, ref double[,] h) { - - dforest.dfbuildersetrndvars(s.innerobj, rndvars, null); + e = 0; + mlpbase.mlphessianbatch(network.innerobj, xy, ssize, ref e, ref grad, ref h, null); } - public static void dfbuildersetrndvars(decisionforestbuilder s, int rndvars, alglib.xparams _params) + public static void mlphessianbatch(multilayerperceptron network, double[,] xy, int ssize, out double e, ref double[] grad, ref double[,] h, alglib.xparams _params) { - - dforest.dfbuildersetrndvars(s.innerobj, rndvars, _params); + e = 0; + mlpbase.mlphessianbatch(network.innerobj, xy, ssize, ref e, ref grad, ref h, _params); } /************************************************************************* - This function sets number of variables used by decision forest construction - algorithm as a fraction of total variable count (0,1) range. + Calculation of all types of errors on subset of dataset. - The default option is to use roughly sqrt(NVars) variables. + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - S - decision forest builder object - F - round(NVars*F) variables are selected + Network - network initialized with one of the network creation funcs + XY - original dataset; one sample = one row; + first NIn columns contain inputs, + next NOut columns - desired outputs. + SetSize - real size of XY, SetSize>=0; + Subset - subset of SubsetSize elements, array[SubsetSize]; + SubsetSize- number of elements in Subset[] array: + * if SubsetSize>0, rows of XY with indices Subset[0]... + ...Subset[SubsetSize-1] are processed + * if SubsetSize=0, zeros are returned + * if SubsetSize<0, entire dataset is processed; Subset[] + array is ignored in this case. OUTPUT PARAMETERS: - S - decision forest builder + Rep - it contains all type of errors. -- ALGLIB -- - Copyright 21.05.2018 by Bochkanov Sergey + Copyright 04.09.2012 by Bochkanov Sergey *************************************************************************/ - public static void dfbuildersetrndvarsratio(decisionforestbuilder s, double f) + public static void mlpallerrorssubset(multilayerperceptron network, double[,] xy, int setsize, int[] subset, int subsetsize, out modelerrors rep) { - - dforest.dfbuildersetrndvarsratio(s.innerobj, f, null); + rep = new modelerrors(); + mlpbase.mlpallerrorssubset(network.innerobj, xy, setsize, subset, subsetsize, rep.innerobj, null); } - public static void dfbuildersetrndvarsratio(decisionforestbuilder s, double f, alglib.xparams _params) + public static void mlpallerrorssubset(multilayerperceptron network, double[,] xy, int setsize, int[] subset, int subsetsize, out modelerrors rep, alglib.xparams _params) { - - dforest.dfbuildersetrndvarsratio(s.innerobj, f, _params); + rep = new modelerrors(); + mlpbase.mlpallerrorssubset(network.innerobj, xy, setsize, subset, subsetsize, rep.innerobj, _params); } /************************************************************************* - This function tells decision forest builder to automatically choose number - of variables used by decision forest construction algorithm. Roughly - sqrt(NVars) variables will be used. + Calculation of all types of errors on subset of dataset. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - S - decision forest builder object + Network - network initialized with one of the network creation funcs + XY - original dataset given by sparse matrix; + one sample = one row; + first NIn columns contain inputs, + next NOut columns - desired outputs. + SetSize - real size of XY, SetSize>=0; + Subset - subset of SubsetSize elements, array[SubsetSize]; + SubsetSize- number of elements in Subset[] array: + * if SubsetSize>0, rows of XY with indices Subset[0]... + ...Subset[SubsetSize-1] are processed + * if SubsetSize=0, zeros are returned + * if SubsetSize<0, entire dataset is processed; Subset[] + array is ignored in this case. OUTPUT PARAMETERS: - S - decision forest builder + Rep - it contains all type of errors. + -- ALGLIB -- - Copyright 21.05.2018 by Bochkanov Sergey + Copyright 04.09.2012 by Bochkanov Sergey *************************************************************************/ - public static void dfbuildersetrndvarsauto(decisionforestbuilder s) + public static void mlpallerrorssparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] subset, int subsetsize, out modelerrors rep) { - - dforest.dfbuildersetrndvarsauto(s.innerobj, null); + rep = new modelerrors(); + mlpbase.mlpallerrorssparsesubset(network.innerobj, xy.innerobj, setsize, subset, subsetsize, rep.innerobj, null); } - public static void dfbuildersetrndvarsauto(decisionforestbuilder s, alglib.xparams _params) + public static void mlpallerrorssparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] subset, int subsetsize, out modelerrors rep, alglib.xparams _params) { - - dforest.dfbuildersetrndvarsauto(s.innerobj, _params); + rep = new modelerrors(); + mlpbase.mlpallerrorssparsesubset(network.innerobj, xy.innerobj, setsize, subset, subsetsize, rep.innerobj, _params); } /************************************************************************* - This function sets size of dataset subsample generated the decision forest - construction algorithm. Size is specified as a fraction of total dataset - size. + Error of the neural network on subset of dataset. - The default option is to use 50% of the dataset for training, 50% for the - OOB estimates. You can decrease fraction F down to 10%, 1% or even below - in order to reduce overfitting. + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - S - decision forest builder object - F - fraction of the dataset to use, in (0,1] range. Values - outside of this range will be silently clipped. At - least one element is always selected for the training - set. + Network - neural network; + XY - training set, see below for information on the + training set format; + SetSize - real size of XY, SetSize>=0; + Subset - subset of SubsetSize elements, array[SubsetSize]; + SubsetSize- number of elements in Subset[] array: + * if SubsetSize>0, rows of XY with indices Subset[0]... + ...Subset[SubsetSize-1] are processed + * if SubsetSize=0, zeros are returned + * if SubsetSize<0, entire dataset is processed; Subset[] + array is ignored in this case. - OUTPUT PARAMETERS: - S - decision forest builder + RESULT: + sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) + + DATASET FORMAT: + + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 21.05.2018 by Bochkanov Sergey + Copyright 04.09.2012 by Bochkanov Sergey *************************************************************************/ - public static void dfbuildersetsubsampleratio(decisionforestbuilder s, double f) + public static double mlperrorsubset(multilayerperceptron network, double[,] xy, int setsize, int[] subset, int subsetsize) { - dforest.dfbuildersetsubsampleratio(s.innerobj, f, null); + return mlpbase.mlperrorsubset(network.innerobj, xy, setsize, subset, subsetsize, null); } - public static void dfbuildersetsubsampleratio(decisionforestbuilder s, double f, alglib.xparams _params) + public static double mlperrorsubset(multilayerperceptron network, double[,] xy, int setsize, int[] subset, int subsetsize, alglib.xparams _params) { - dforest.dfbuildersetsubsampleratio(s.innerobj, f, _params); + return mlpbase.mlperrorsubset(network.innerobj, xy, setsize, subset, subsetsize, _params); } /************************************************************************* - This function sets seed used by internal RNG for random subsampling and - random selection of variable subsets. - - By default random seed is used, i.e. every time you build decision forest, - we seed generator with new value obtained from system-wide RNG. Thus, - decision forest builder returns non-deterministic results. You can change - such behavior by specyfing fixed positive seed value. + Error of the neural network on subset of sparse dataset. - INPUT PARAMETERS: - S - decision forest builder object - SeedVal - seed value: - * positive values are used for seeding RNG with fixed - seed, i.e. subsequent runs on same data will return - same decision forests - * non-positive seed means that random seed is used - for every run of builder, i.e. subsequent runs on - same datasets will return slightly different - decision forests + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. - OUTPUT PARAMETERS: - S - decision forest builder, see + INPUT PARAMETERS: + Network - neural network; + XY - training set, see below for information on the + training set format. This function checks correctness + of the dataset (no NANs/INFs, class numbers are + correct) and throws exception when incorrect dataset + is passed. Sparse matrix must use CRS format for + storage. + SetSize - real size of XY, SetSize>=0; + it is used when SubsetSize<0; + Subset - subset of SubsetSize elements, array[SubsetSize]; + SubsetSize- number of elements in Subset[] array: + * if SubsetSize>0, rows of XY with indices Subset[0]... + ...Subset[SubsetSize-1] are processed + * if SubsetSize=0, zeros are returned + * if SubsetSize<0, entire dataset is processed; Subset[] + array is ignored in this case. + + RESULT: + sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) + + DATASET FORMAT: + + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + dataset format is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 21.05.2018 by Bochkanov Sergey + Copyright 04.09.2012 by Bochkanov Sergey *************************************************************************/ - public static void dfbuildersetseed(decisionforestbuilder s, int seedval) + public static double mlperrorsparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] subset, int subsetsize) { - dforest.dfbuildersetseed(s.innerobj, seedval, null); + return mlpbase.mlperrorsparsesubset(network.innerobj, xy.innerobj, setsize, subset, subsetsize, null); } - public static void dfbuildersetseed(decisionforestbuilder s, int seedval, alglib.xparams _params) + public static double mlperrorsparsesubset(multilayerperceptron network, sparsematrix xy, int setsize, int[] subset, int subsetsize, alglib.xparams _params) { - dforest.dfbuildersetseed(s.innerobj, seedval, _params); + return mlpbase.mlperrorsparsesubset(network.innerobj, xy.innerobj, setsize, subset, subsetsize, _params); } - - /************************************************************************* - This function sets random decision forest construction algorithm. - As for now, only one decision forest construction algorithm is supported - - a dense "baseline" RDF algorithm. +} +public partial class alglib +{ - INPUT PARAMETERS: - S - decision forest builder object - AlgoType - algorithm type: - * 0 = baseline dense RDF - OUTPUT PARAMETERS: - S - decision forest builder, see + /************************************************************************* - -- ALGLIB -- - Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void dfbuildersetrdfalgo(decisionforestbuilder s, int algotype) + public class logitmodel : alglibobject { + // + // Public declarations + // - dforest.dfbuildersetrdfalgo(s.innerobj, algotype, null); - } + public logitmodel() + { + _innerobj = new logit.logitmodel(); + } + + public override alglib.alglibobject make_copy() + { + return new logitmodel((logit.logitmodel)_innerobj.make_copy()); + } - public static void dfbuildersetrdfalgo(decisionforestbuilder s, int algotype, alglib.xparams _params) + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private logit.logitmodel _innerobj; + public logit.logitmodel innerobj { get { return _innerobj; } } + public logitmodel(logit.logitmodel obj) + { + _innerobj = obj; + } + } + + + /************************************************************************* + MNLReport structure contains information about training process: + * NGrad - number of gradient calculations + * NHess - number of Hessian calculations + *************************************************************************/ + public class mnlreport : alglibobject { + // + // Public declarations + // + public int ngrad { get { return _innerobj.ngrad; } set { _innerobj.ngrad = value; } } + public int nhess { get { return _innerobj.nhess; } set { _innerobj.nhess = value; } } - dforest.dfbuildersetrdfalgo(s.innerobj, algotype, _params); + public mnlreport() + { + _innerobj = new logit.mnlreport(); + } + + public override alglib.alglibobject make_copy() + { + return new mnlreport((logit.mnlreport)_innerobj.make_copy()); + } + + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private logit.mnlreport _innerobj; + public logit.mnlreport innerobj { get { return _innerobj; } } + public mnlreport(logit.mnlreport obj) + { + _innerobj = obj; + } } /************************************************************************* - This function sets split selection algorithm used by decision forest - classifier. You may choose several algorithms, with different speed and - quality of the results. + This subroutine trains logit model. INPUT PARAMETERS: - S - decision forest builder object - SplitStrength- split type: - * 0 = split at the random position, fastest one - * 1 = split at the middle of the range - * 2 = strong split at the best point of the range (default) + XY - training set, array[0..NPoints-1,0..NVars] + First NVars columns store values of independent + variables, next column stores number of class (from 0 + to NClasses-1) which dataset element belongs to. Fractional + values are rounded to nearest integer. + NPoints - training set size, NPoints>=1 + NVars - number of independent variables, NVars>=1 + NClasses - number of classes, NClasses>=2 OUTPUT PARAMETERS: - S - decision forest builder, see + Info - return code: + * -2, if there is a point with class number + outside of [0..NClasses-1]. + * -1, if incorrect parameters was passed + (NPoints=1, number of trees to train - - OUTPUT PARAMETERS: - DF - decision forest. You can compress this forest to more - compact 16-bit representation with dfbinarycompression() - Rep - report, see below for information on its fields. - - === report information produced by forest construction function ========== - - Decision forest training report includes following information: - * training set errors - * out-of-bag estimates of errors - * variable importance ratings - - Following fields are used to store information: - * training set errors are stored in rep.relclserror, rep.avgce, rep.rmserror, - rep.avgerror and rep.avgrelerror - * out-of-bag estimates of errors are stored in rep.oobrelclserror, rep.oobavgce, - rep.oobrmserror, rep.oobavgerror and rep.oobavgrelerror - - Variable importance reports, if requested by dfbuildersetimportancegini(), - dfbuildersetimportancetrngini() or dfbuildersetimportancepermutation() - call, are stored in: - * rep.varimportances field stores importance ratings - * rep.topvars stores variable indexes ordered from the most important to - less important ones - - You can find more information about report fields in: - * comments on dfreport structure - * comments on dfbuildersetimportancegini function - * comments on dfbuildersetimportancetrngini function - * comments on dfbuildersetimportancepermutation function + RESULT: + average relative error (error when estimating posterior probabilities). -- ALGLIB -- - Copyright 21.05.2018 by Bochkanov Sergey + Copyright 30.08.2008 by Bochkanov Sergey *************************************************************************/ - public static void dfbuilderbuildrandomforest(decisionforestbuilder s, int ntrees, out decisionforest df, out dfreport rep) + public static double mnlavgrelerror(logitmodel lm, double[,] xy, int ssize) { - df = new decisionforest(); - rep = new dfreport(); - dforest.dfbuilderbuildrandomforest(s.innerobj, ntrees, df.innerobj, rep.innerobj, null); + + return logit.mnlavgrelerror(lm.innerobj, xy, ssize, null); } - public static void dfbuilderbuildrandomforest(decisionforestbuilder s, int ntrees, out decisionforest df, out dfreport rep, alglib.xparams _params) + public static double mnlavgrelerror(logitmodel lm, double[,] xy, int ssize, alglib.xparams _params) { - df = new decisionforest(); - rep = new dfreport(); - dforest.dfbuilderbuildrandomforest(s.innerobj, ntrees, df.innerobj, rep.innerobj, _params); + + return logit.mnlavgrelerror(lm.innerobj, xy, ssize, _params); } /************************************************************************* - This function performs binary compression of the decision forest. - - Original decision forest produced by the forest builder is stored using - 64-bit representation for all numbers - offsets, variable indexes, split - points. - - It is possible to significantly reduce model size by means of: - * using compressed dynamic encoding for integers (offsets and variable - indexes), which uses just 1 byte to store small ints (less than 128), - just 2 bytes for larger values (less than 128^2) and so on - * storing floating point numbers using 8-bit exponent and 16-bit mantissa - - As result, model needs significantly less memory (compression factor - depends on variable and class counts). In particular: - * NVars<128 and NClasses<128 result in 4.4x-5.7x model size reduction - * NVars<16384 and NClasses<128 result in 3.7x-4.5x model size reduction - - Such storage format performs lossless compression of all integers, but - compression of floating point values (split values) is lossy, with roughly - 0.01% relative error introduced during rounding. Thus, we recommend you to - re-evaluate model accuracy after compression. - - Another downside of compression is ~1.5x reduction in the inference - speed due to necessity of dynamic decompression of the compressed model. - - INPUT PARAMETERS: - DF - decision forest built by forest builder - - OUTPUT PARAMETERS: - DF - replaced by compressed forest - - RESULT: - compression factor (in-RAM size of the compressed model vs than of the - uncompressed one), positive number larger than 1.0 + Classification error on test set = MNLRelClsError*NPoints -- ALGLIB -- - Copyright 22.07.2019 by Bochkanov Sergey + Copyright 10.09.2008 by Bochkanov Sergey *************************************************************************/ - public static double dfbinarycompression(decisionforest df) + public static int mnlclserror(logitmodel lm, double[,] xy, int npoints) { - return dforest.dfbinarycompression(df.innerobj, null); + return logit.mnlclserror(lm.innerobj, xy, npoints, null); } - public static double dfbinarycompression(decisionforest df, alglib.xparams _params) + public static int mnlclserror(logitmodel lm, double[,] xy, int npoints, alglib.xparams _params) { - return dforest.dfbinarycompression(df.innerobj, _params); + return logit.mnlclserror(lm.innerobj, xy, npoints, _params); } - - /************************************************************************* - Inference using decision forest - - IMPORTANT: this function is thread-unsafe and may modify internal - structures of the model! You can not use same model object for - parallel evaluation from several threads. - - Use dftsprocess() with independent thread-local buffers if - you need thread-safe evaluation. - INPUT PARAMETERS: - DF - decision forest model - X - input vector, array[NVars] - Y - possibly preallocated buffer, reallocated if too small +} +public partial class alglib +{ - OUTPUT PARAMETERS: - Y - result. Regression estimate when solving regression task, - vector of posterior probabilities for classification task. - See also DFProcessI. + /************************************************************************* + This structure is a clusterization engine. + You should not try to access its fields directly. + Use ALGLIB functions in order to work with this object. -- ALGLIB -- - Copyright 16.02.2009 by Bochkanov Sergey + Copyright 10.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void dfprocess(decisionforest df, double[] x, ref double[] y) + public class clusterizerstate : alglibobject { + // + // Public declarations + // - dforest.dfprocess(df.innerobj, x, ref y, null); - } - - public static void dfprocess(decisionforest df, double[] x, ref double[] y, alglib.xparams _params) - { + public clusterizerstate() + { + _innerobj = new clustering.clusterizerstate(); + } + + public override alglib.alglibobject make_copy() + { + return new clusterizerstate((clustering.clusterizerstate)_innerobj.make_copy()); + } - dforest.dfprocess(df.innerobj, x, ref y, _params); + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private clustering.clusterizerstate _innerobj; + public clustering.clusterizerstate innerobj { get { return _innerobj; } } + public clusterizerstate(clustering.clusterizerstate obj) + { + _innerobj = obj; + } } - - /************************************************************************* - 'interactive' variant of DFProcess for languages like Python which support - constructs like "Y = DFProcessI(DF,X)" and interactive mode of interpreter - - This function allocates new array on each call, so it is significantly - slower than its 'non-interactive' counterpart, but it is more convenient - when you call it from command line. - IMPORTANT: this function is thread-unsafe and may modify internal - structures of the model! You can not use same model object for - parallel evaluation from several threads. - - Use dftsprocess() with independent thread-local buffers if - you need thread-safe evaluation. - -- ALGLIB -- - Copyright 28.02.2010 by Bochkanov Sergey - *************************************************************************/ - public static void dfprocessi(decisionforest df, double[] x, out double[] y) - { - y = new double[0]; - dforest.dfprocessi(df.innerobj, x, ref y, null); - } - - public static void dfprocessi(decisionforest df, double[] x, out double[] y, alglib.xparams _params) - { - y = new double[0]; - dforest.dfprocessi(df.innerobj, x, ref y, _params); - } - /************************************************************************* - This function returns first component of the inferred vector (i.e. one - with index #0). + This structure is used to store results of the agglomerative hierarchical + clustering (AHC). - It is a convenience wrapper for dfprocess() intended for either: - * 1-dimensional regression problems - * 2-class classification problems + Following information is returned: - In the former case this function returns inference result as scalar, which - is definitely more convenient that wrapping it as vector. In the latter - case it returns probability of object belonging to class #0. + * TerminationType - completion code: + * 1 for successful completion of algorithm + * -5 inappropriate combination of clustering algorithm and distance + function was used. As for now, it is possible only when Ward's + method is called for dataset with non-Euclidean distance function. + In case negative completion code is returned, other fields of report + structure are invalid and should not be used. - If you call it for anything different from two cases above, it will work - as defined, i.e. return y[0], although it is of less use in such cases. + * NPoints contains number of points in the original dataset - IMPORTANT: this function is thread-unsafe and modifies internal structures - of the model! You can not use same model object for parallel - evaluation from several threads. + * Z contains information about merges performed (see below). Z contains + indexes from the original (unsorted) dataset and it can be used when you + need to know what points were merged. However, it is not convenient when + you want to build a dendrograd (see below). - Use dftsprocess() with independent thread-local buffers, if - you need thread-safe evaluation. + * if you want to build dendrogram, you can use Z, but it is not good + option, because Z contains indexes from unsorted dataset. Dendrogram + built from such dataset is likely to have intersections. So, you have to + reorder you points before building dendrogram. + Permutation which reorders point is returned in P. Another representation + of merges, which is more convenient for dendorgram construction, is + returned in PM. - INPUT PARAMETERS: - Model - DF model - X - input vector, array[0..NVars-1]. + * more information on format of Z, P and PM can be found below and in the + examples from ALGLIB Reference Manual. - RESULT: - Y[0] + FORMAL DESCRIPTION OF FIELDS: + NPoints number of points + Z array[NPoints-1,2], contains indexes of clusters + linked in pairs to form clustering tree. I-th row + corresponds to I-th merge: + * Z[I,0] - index of the first cluster to merge + * Z[I,1] - index of the second cluster to merge + * Z[I,0]=0 + NFeatures number of variables, >=1 + TerminationType completion code: + * -5 if distance type is anything different from + Euclidean metric + * -3 for degenerate dataset: a) less than K distinct + points, b) K=0 for non-empty dataset. + * +1 for successful completion + K number of clusters + C array[K,NFeatures], rows of the array store centers + CIdx array[NPoints], which contains cluster indexes + IterationsCount actual number of iterations performed by clusterizer. + If algorithm performed more than one random restart, + total number of iterations is returned. + Energy merit function, "energy", sum of squared deviations + from cluster centers -- ALGLIB -- - Copyright 16.02.2009 by Bochkanov Sergey + Copyright 27.11.2012 by Bochkanov Sergey *************************************************************************/ - public static void dftsprocess(decisionforest df, decisionforestbuffer buf, double[] x, ref double[] y) + public class kmeansreport : alglibobject { + // + // Public declarations + // + public int npoints { get { return _innerobj.npoints; } set { _innerobj.npoints = value; } } + public int nfeatures { get { return _innerobj.nfeatures; } set { _innerobj.nfeatures = value; } } + public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } + public int iterationscount { get { return _innerobj.iterationscount; } set { _innerobj.iterationscount = value; } } + public double energy { get { return _innerobj.energy; } set { _innerobj.energy = value; } } + public int k { get { return _innerobj.k; } set { _innerobj.k = value; } } + public double[,] c { get { return _innerobj.c; } set { _innerobj.c = value; } } + public int[] cidx { get { return _innerobj.cidx; } set { _innerobj.cidx = value; } } - dforest.dftsprocess(df.innerobj, buf.innerobj, x, ref y, null); - } - - public static void dftsprocess(decisionforest df, decisionforestbuffer buf, double[] x, ref double[] y, alglib.xparams _params) - { + public kmeansreport() + { + _innerobj = new clustering.kmeansreport(); + } + + public override alglib.alglibobject make_copy() + { + return new kmeansreport((clustering.kmeansreport)_innerobj.make_copy()); + } - dforest.dftsprocess(df.innerobj, buf.innerobj, x, ref y, _params); + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private clustering.kmeansreport _innerobj; + public clustering.kmeansreport innerobj { get { return _innerobj; } } + public kmeansreport(clustering.kmeansreport obj) + { + _innerobj = obj; + } } /************************************************************************* - Relative classification error on the test set - - INPUT PARAMETERS: - DF - decision forest model - XY - test set - NPoints - test set size - - RESULT: - percent of incorrectly classified cases. - Zero if model solves regression task. + This function initializes clusterizer object. Newly initialized object is + empty, i.e. it does not contain dataset. You should use it as follows: + 1. creation + 2. dataset is added with ClusterizerSetPoints() + 3. additional parameters are set + 3. clusterization is performed with one of the clustering functions -- ALGLIB -- - Copyright 16.02.2009 by Bochkanov Sergey + Copyright 10.07.2012 by Bochkanov Sergey *************************************************************************/ - public static double dfrelclserror(decisionforest df, double[,] xy, int npoints) + public static void clusterizercreate(out clusterizerstate s) { - - return dforest.dfrelclserror(df.innerobj, xy, npoints, null); + s = new clusterizerstate(); + clustering.clusterizercreate(s.innerobj, null); } - public static double dfrelclserror(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) + public static void clusterizercreate(out clusterizerstate s, alglib.xparams _params) { - - return dforest.dfrelclserror(df.innerobj, xy, npoints, _params); + s = new clusterizerstate(); + clustering.clusterizercreate(s.innerobj, _params); } /************************************************************************* - Average cross-entropy (in bits per element) on the test set + This function adds dataset to the clusterizer structure. - INPUT PARAMETERS: - DF - decision forest model - XY - test set - NPoints - test set size + This function overrides all previous calls of ClusterizerSetPoints() or + ClusterizerSetDistances(). - RESULT: - CrossEntropy/(NPoints*LN(2)). - Zero if model solves regression task. + INPUT PARAMETERS: + S - clusterizer state, initialized by ClusterizerCreate() + XY - array[NPoints,NFeatures], dataset + NPoints - number of points, >=0 + NFeatures- number of features, >=1 + DistType- distance function: + * 0 Chebyshev distance (L-inf norm) + * 1 city block distance (L1 norm) + * 2 Euclidean distance (L2 norm), non-squared + * 10 Pearson correlation: + dist(a,b) = 1-corr(a,b) + * 11 Absolute Pearson correlation: + dist(a,b) = 1-|corr(a,b)| + * 12 Uncentered Pearson correlation (cosine of the angle): + dist(a,b) = a'*b/(|a|*|b|) + * 13 Absolute uncentered Pearson correlation + dist(a,b) = |a'*b|/(|a|*|b|) + * 20 Spearman rank correlation: + dist(a,b) = 1-rankcorr(a,b) + * 21 Absolute Spearman rank correlation + dist(a,b) = 1-|rankcorr(a,b)| + + NOTE 1: different distance functions have different performance penalty: + * Euclidean or Pearson correlation distances are the fastest ones + * Spearman correlation distance function is a bit slower + * city block and Chebyshev distances are order of magnitude slower + + The reason behing difference in performance is that correlation-based + distance functions are computed using optimized linear algebra kernels, + while Chebyshev and city block distance functions are computed using + simple nested loops with two branches at each iteration. + + NOTE 2: different clustering algorithms have different limitations: + * agglomerative hierarchical clustering algorithms may be used with + any kind of distance metric + * k-means++ clustering algorithm may be used only with Euclidean + distance function + Thus, list of specific clustering algorithms you may use depends + on distance function you specify when you set your dataset. -- ALGLIB -- - Copyright 16.02.2009 by Bochkanov Sergey + Copyright 10.07.2012 by Bochkanov Sergey *************************************************************************/ - public static double dfavgce(decisionforest df, double[,] xy, int npoints) + public static void clusterizersetpoints(clusterizerstate s, double[,] xy, int npoints, int nfeatures, int disttype) { - return dforest.dfavgce(df.innerobj, xy, npoints, null); + clustering.clusterizersetpoints(s.innerobj, xy, npoints, nfeatures, disttype, null); } - public static double dfavgce(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) + public static void clusterizersetpoints(clusterizerstate s, double[,] xy, int npoints, int nfeatures, int disttype, alglib.xparams _params) { - return dforest.dfavgce(df.innerobj, xy, npoints, _params); + clustering.clusterizersetpoints(s.innerobj, xy, npoints, nfeatures, disttype, _params); + } + + public static void clusterizersetpoints(clusterizerstate s, double[,] xy, int disttype) + { + int npoints; + int nfeatures; + + + npoints = ap.rows(xy); + nfeatures = ap.cols(xy); + clustering.clusterizersetpoints(s.innerobj, xy, npoints, nfeatures, disttype, null); + + return; + } + + public static void clusterizersetpoints(clusterizerstate s, double[,] xy, int disttype, alglib.xparams _params) + { + int npoints; + int nfeatures; + + + npoints = ap.rows(xy); + nfeatures = ap.cols(xy); + clustering.clusterizersetpoints(s.innerobj, xy, npoints, nfeatures, disttype, _params); + + return; } /************************************************************************* - RMS error on the test set + This function adds dataset given by distance matrix to the clusterizer + structure. It is important that dataset is not given explicitly - only + distance matrix is given. + + This function overrides all previous calls of ClusterizerSetPoints() or + ClusterizerSetDistances(). INPUT PARAMETERS: - DF - decision forest model - XY - test set - NPoints - test set size + S - clusterizer state, initialized by ClusterizerCreate() + D - array[NPoints,NPoints], distance matrix given by its upper + or lower triangle (main diagonal is ignored because its + entries are expected to be zero). + NPoints - number of points + IsUpper - whether upper or lower triangle of D is given. - RESULT: - root mean square error. - Its meaning for regression task is obvious. As for - classification task, RMS error means error when estimating posterior - probabilities. + NOTE 1: different clustering algorithms have different limitations: + * agglomerative hierarchical clustering algorithms may be used with + any kind of distance metric, including one which is given by + distance matrix + * k-means++ clustering algorithm may be used only with Euclidean + distance function and explicitly given points - it can not be + used with dataset given by distance matrix + Thus, if you call this function, you will be unable to use k-means + clustering algorithm to process your problem. -- ALGLIB -- - Copyright 16.02.2009 by Bochkanov Sergey + Copyright 10.07.2012 by Bochkanov Sergey *************************************************************************/ - public static double dfrmserror(decisionforest df, double[,] xy, int npoints) + public static void clusterizersetdistances(clusterizerstate s, double[,] d, int npoints, bool isupper) { - return dforest.dfrmserror(df.innerobj, xy, npoints, null); + clustering.clusterizersetdistances(s.innerobj, d, npoints, isupper, null); } - public static double dfrmserror(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) + public static void clusterizersetdistances(clusterizerstate s, double[,] d, int npoints, bool isupper, alglib.xparams _params) { - return dforest.dfrmserror(df.innerobj, xy, npoints, _params); + clustering.clusterizersetdistances(s.innerobj, d, npoints, isupper, _params); + } + + public static void clusterizersetdistances(clusterizerstate s, double[,] d, bool isupper) + { + int npoints; + if( (ap.rows(d)!=ap.cols(d))) + throw new alglibexception("Error while calling 'clusterizersetdistances': looks like one of arguments has wrong size"); + + npoints = ap.rows(d); + clustering.clusterizersetdistances(s.innerobj, d, npoints, isupper, null); + + return; + } + + public static void clusterizersetdistances(clusterizerstate s, double[,] d, bool isupper, alglib.xparams _params) + { + int npoints; + if( (ap.rows(d)!=ap.cols(d))) + throw new alglibexception("Error while calling 'clusterizersetdistances': looks like one of arguments has wrong size"); + + npoints = ap.rows(d); + clustering.clusterizersetdistances(s.innerobj, d, npoints, isupper, _params); + + return; } /************************************************************************* - Average error on the test set + This function sets agglomerative hierarchical clustering algorithm INPUT PARAMETERS: - DF - decision forest model - XY - test set - NPoints - test set size + S - clusterizer state, initialized by ClusterizerCreate() + Algo - algorithm type: + * 0 complete linkage (default algorithm) + * 1 single linkage + * 2 unweighted average linkage + * 3 weighted average linkage + * 4 Ward's method - RESULT: - Its meaning for regression task is obvious. As for - classification task, it means average error when estimating posterior - probabilities. + NOTE: Ward's method works correctly only with Euclidean distance, that's + why algorithm will return negative termination code (failure) for + any other distance type. + + It is possible, however, to use this method with user-supplied + distance matrix. It is your responsibility to pass one which was + calculated with Euclidean distance function. -- ALGLIB -- - Copyright 16.02.2009 by Bochkanov Sergey + Copyright 10.07.2012 by Bochkanov Sergey *************************************************************************/ - public static double dfavgerror(decisionforest df, double[,] xy, int npoints) + public static void clusterizersetahcalgo(clusterizerstate s, int algo) { - return dforest.dfavgerror(df.innerobj, xy, npoints, null); + clustering.clusterizersetahcalgo(s.innerobj, algo, null); } - public static double dfavgerror(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) + public static void clusterizersetahcalgo(clusterizerstate s, int algo, alglib.xparams _params) { - return dforest.dfavgerror(df.innerobj, xy, npoints, _params); + clustering.clusterizersetahcalgo(s.innerobj, algo, _params); } /************************************************************************* - Average relative error on the test set + This function sets k-means properties: number of restarts and maximum + number of iterations per one run. INPUT PARAMETERS: - DF - decision forest model - XY - test set - NPoints - test set size - - RESULT: - Its meaning for regression task is obvious. As for - classification task, it means average relative error when estimating - posterior probability of belonging to the correct class. + S - clusterizer state, initialized by ClusterizerCreate() + Restarts- restarts count, >=1. + k-means++ algorithm performs several restarts and chooses + best set of centers (one with minimum squared distance). + MaxIts - maximum number of k-means iterations performed during one + run. >=0, zero value means that algorithm performs unlimited + number of iterations. -- ALGLIB -- - Copyright 16.02.2009 by Bochkanov Sergey + Copyright 10.07.2012 by Bochkanov Sergey *************************************************************************/ - public static double dfavgrelerror(decisionforest df, double[,] xy, int npoints) + public static void clusterizersetkmeanslimits(clusterizerstate s, int restarts, int maxits) { - return dforest.dfavgrelerror(df.innerobj, xy, npoints, null); + clustering.clusterizersetkmeanslimits(s.innerobj, restarts, maxits, null); } - public static double dfavgrelerror(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) + public static void clusterizersetkmeanslimits(clusterizerstate s, int restarts, int maxits, alglib.xparams _params) { - return dforest.dfavgrelerror(df.innerobj, xy, npoints, _params); + clustering.clusterizersetkmeanslimits(s.innerobj, restarts, maxits, _params); } /************************************************************************* - This subroutine builds random decision forest. + This function sets k-means initialization algorithm. Several different + algorithms can be chosen, including k-means++. - --------- DEPRECATED VERSION! USE DECISION FOREST BUILDER OBJECT --------- + INPUT PARAMETERS: + S - clusterizer state, initialized by ClusterizerCreate() + InitAlgo- initialization algorithm: + * 0 automatic selection ( different versions of ALGLIB + may select different algorithms) + * 1 random initialization + * 2 k-means++ initialization (best quality of initial + centers, but long non-parallelizable initialization + phase with bad cache locality) + * 3 "fast-greedy" algorithm with efficient, easy to + parallelize initialization. Quality of initial centers + is somewhat worse than that of k-means++. This + algorithm is a default one in the current version of + ALGLIB. + *-1 "debug" algorithm which always selects first K rows + of dataset; this algorithm is used for debug purposes + only. Do not use it in the industrial code! -- ALGLIB -- - Copyright 19.02.2009 by Bochkanov Sergey + Copyright 21.01.2015 by Bochkanov Sergey *************************************************************************/ - public static void dfbuildrandomdecisionforest(double[,] xy, int npoints, int nvars, int nclasses, int ntrees, double r, out int info, out decisionforest df, out dfreport rep) + public static void clusterizersetkmeansinit(clusterizerstate s, int initalgo) { - info = 0; - df = new decisionforest(); - rep = new dfreport(); - dforest.dfbuildrandomdecisionforest(xy, npoints, nvars, nclasses, ntrees, r, ref info, df.innerobj, rep.innerobj, null); + + clustering.clusterizersetkmeansinit(s.innerobj, initalgo, null); } - public static void dfbuildrandomdecisionforest(double[,] xy, int npoints, int nvars, int nclasses, int ntrees, double r, out int info, out decisionforest df, out dfreport rep, alglib.xparams _params) + public static void clusterizersetkmeansinit(clusterizerstate s, int initalgo, alglib.xparams _params) { - info = 0; - df = new decisionforest(); - rep = new dfreport(); - dforest.dfbuildrandomdecisionforest(xy, npoints, nvars, nclasses, ntrees, r, ref info, df.innerobj, rep.innerobj, _params); + + clustering.clusterizersetkmeansinit(s.innerobj, initalgo, _params); } /************************************************************************* - This subroutine builds random decision forest. + This function sets seed which is used to initialize internal RNG. By + default, deterministic seed is used - same for each run of clusterizer. If + you specify non-deterministic seed value, then some algorithms which + depend on random initialization (in current version: k-means) may return + slightly different results after each run. - --------- DEPRECATED VERSION! USE DECISION FOREST BUILDER OBJECT --------- + INPUT PARAMETERS: + S - clusterizer state, initialized by ClusterizerCreate() + Seed - seed: + * positive values = use deterministic seed for each run of + algorithms which depend on random initialization + * zero or negative values = use non-deterministic seed -- ALGLIB -- - Copyright 19.02.2009 by Bochkanov Sergey + Copyright 08.06.2017 by Bochkanov Sergey *************************************************************************/ - public static void dfbuildrandomdecisionforestx1(double[,] xy, int npoints, int nvars, int nclasses, int ntrees, int nrndvars, double r, out int info, out decisionforest df, out dfreport rep) + public static void clusterizersetseed(clusterizerstate s, int seed) { - info = 0; - df = new decisionforest(); - rep = new dfreport(); - dforest.dfbuildrandomdecisionforestx1(xy, npoints, nvars, nclasses, ntrees, nrndvars, r, ref info, df.innerobj, rep.innerobj, null); + + clustering.clusterizersetseed(s.innerobj, seed, null); } - public static void dfbuildrandomdecisionforestx1(double[,] xy, int npoints, int nvars, int nclasses, int ntrees, int nrndvars, double r, out int info, out decisionforest df, out dfreport rep, alglib.xparams _params) + public static void clusterizersetseed(clusterizerstate s, int seed, alglib.xparams _params) { - info = 0; - df = new decisionforest(); - rep = new dfreport(); - dforest.dfbuildrandomdecisionforestx1(xy, npoints, nvars, nclasses, ntrees, nrndvars, r, ref info, df.innerobj, rep.innerobj, _params); + + clustering.clusterizersetseed(s.innerobj, seed, _params); } + + /************************************************************************* + This function performs agglomerative hierarchical clustering -} -public partial class alglib -{ + NOTE: Agglomerative hierarchical clustering algorithm has two phases: + distance matrix calculation and clustering itself. Only first phase + (distance matrix calculation) is accelerated by SIMD and SMP. Thus, + acceleration is significant only for medium or high-dimensional + problems. + Although activating multithreading gives some speedup over single- + threaded execution, you should not expect nearly-linear scaling + with respect to cores count. - /************************************************************************* + INPUT PARAMETERS: + S - clusterizer state, initialized by ClusterizerCreate() - *************************************************************************/ - public class linearmodel : alglibobject - { - // - // Public declarations - // - - public linearmodel() - { - _innerobj = new linreg.linearmodel(); - } - - public override alglib.alglibobject make_copy() - { - return new linearmodel((linreg.linearmodel)_innerobj.make_copy()); - } - - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private linreg.linearmodel _innerobj; - public linreg.linearmodel innerobj { get { return _innerobj; } } - public linearmodel(linreg.linearmodel obj) - { - _innerobj = obj; - } - } + OUTPUT PARAMETERS: + Rep - clustering results; see description of AHCReport + structure for more information. + NOTE 1: hierarchical clustering algorithms require large amounts of memory. + In particular, this implementation needs sizeof(double)*NPoints^2 + bytes, which are used to store distance matrix. In case we work + with user-supplied matrix, this amount is multiplied by 2 (we have + to store original matrix and to work with its copy). - /************************************************************************* - LRReport structure contains additional information about linear model: - * C - covariation matrix, array[0..NVars,0..NVars]. - C[i,j] = Cov(A[i],A[j]) - * RMSError - root mean square error on a training set - * AvgError - average error on a training set - * AvgRelError - average relative error on a training set (excluding - observations with zero function value). - * CVRMSError - leave-one-out cross-validation estimate of - generalization error. Calculated using fast algorithm - with O(NVars*NPoints) complexity. - * CVAvgError - cross-validation estimate of average error - * CVAvgRelError - cross-validation estimate of average relative error + For example, problem with 10000 points would require 800M of RAM, + even when working in a 1-dimensional space. - All other fields of the structure are intended for internal use and should - not be used outside ALGLIB. + ! FREE EDITION OF ALGLIB: + ! + ! Free Edition of ALGLIB supports following important features for this + ! function: + ! * C++ version: x64 SIMD support using C++ intrinsics + ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics + ! + ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB + ! Reference Manual in order to find out how to activate SIMD support + ! in ALGLIB. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! * hardware vendor (Intel, ARM) implementations of linear algebra and + ! other primitives (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. + + -- ALGLIB -- + Copyright 10.07.2012 by Bochkanov Sergey *************************************************************************/ - public class lrreport : alglibobject + public static void clusterizerrunahc(clusterizerstate s, out ahcreport rep) { - // - // Public declarations - // - public double[,] c { get { return _innerobj.c; } set { _innerobj.c = value; } } - public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } - public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } - public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } - public double cvrmserror { get { return _innerobj.cvrmserror; } set { _innerobj.cvrmserror = value; } } - public double cvavgerror { get { return _innerobj.cvavgerror; } set { _innerobj.cvavgerror = value; } } - public double cvavgrelerror { get { return _innerobj.cvavgrelerror; } set { _innerobj.cvavgrelerror = value; } } - public int ncvdefects { get { return _innerobj.ncvdefects; } set { _innerobj.ncvdefects = value; } } - public int[] cvdefects { get { return _innerobj.cvdefects; } set { _innerobj.cvdefects = value; } } - - public lrreport() - { - _innerobj = new linreg.lrreport(); - } - - public override alglib.alglibobject make_copy() - { - return new lrreport((linreg.lrreport)_innerobj.make_copy()); - } + rep = new ahcreport(); + clustering.clusterizerrunahc(s.innerobj, rep.innerobj, null); + } - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private linreg.lrreport _innerobj; - public linreg.lrreport innerobj { get { return _innerobj; } } - public lrreport(linreg.lrreport obj) - { - _innerobj = obj; - } + public static void clusterizerrunahc(clusterizerstate s, out ahcreport rep, alglib.xparams _params) + { + rep = new ahcreport(); + clustering.clusterizerrunahc(s.innerobj, rep.innerobj, _params); } /************************************************************************* - Linear regression - - Subroutine builds model: + This function performs clustering by k-means++ algorithm. - Y = A(0)*X[0] + ... + A(N-1)*X[N-1] + A(N) + You may change algorithm properties by calling: + * ClusterizerSetKMeansLimits() to change number of restarts or iterations + * ClusterizerSetKMeansInit() to change initialization algorithm - and model found in ALGLIB format, covariation matrix, training set errors - (rms, average, average relative) and leave-one-out cross-validation - estimate of the generalization error. CV estimate calculated using fast - algorithm with O(NPoints*NVars) complexity. + By default, one restart and unlimited number of iterations are used. + Initialization algorithm is chosen automatically. - When covariation matrix is calculated standard deviations of function - values are assumed to be equal to RMS error on the training set. + NOTE: k-means clustering algorithm has two phases: selection of initial + centers and clustering itself. ALGLIB parallelizes both phases. + Parallel version is optimized for the following scenario: medium or + high-dimensional problem (8 or more dimensions) with large number of + points and clusters. However, some speed-up can be obtained even + when assumptions above are violated. INPUT PARAMETERS: - XY - training set, array [0..NPoints-1,0..NVars]: - * NVars columns - independent variables - * last column - dependent variable - NPoints - training set size, NPoints>NVars+1. An exception is - generated otherwise. - NVars - number of independent variables + S - clusterizer state, initialized by ClusterizerCreate() + K - number of clusters, K>=0. + K can be zero only when algorithm is called for empty + dataset, in this case completion code is set to + success (+1). + If K=0 and dataset size is non-zero, we can not + meaningfully assign points to some center (there are no + centers because K=0) and return -3 as completion code + (failure). OUTPUT PARAMETERS: - LM - linear model in the ALGLIB format. Use subroutines of - this unit to work with the model. - Rep - additional results, see comments on LRReport structure. + Rep - clustering results; see description of KMeansReport + structure for more information. + + NOTE 1: k-means clustering can be performed only for datasets with + Euclidean distance function. Algorithm will return negative + completion code in Rep.TerminationType in case dataset was added + to clusterizer with DistType other than Euclidean (or dataset was + specified by distance matrix instead of explicitly given points). + + NOTE 2: by default, k-means uses non-deterministic seed to initialize RNG + which is used to select initial centers. As result, each run of + algorithm may return different values. If you need deterministic + behavior, use ClusterizerSetSeed() function. + + ! FREE EDITION OF ALGLIB: + ! + ! Free Edition of ALGLIB supports following important features for this + ! function: + ! * C++ version: x64 SIMD support using C++ intrinsics + ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics + ! + ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB + ! Reference Manual in order to find out how to activate SIMD support + ! in ALGLIB. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! * hardware vendor (Intel, ARM) implementations of linear algebra and + ! other primitives (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. -- ALGLIB -- - Copyright 02.08.2008 by Bochkanov Sergey + Copyright 10.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void lrbuild(double[,] xy, int npoints, int nvars, out linearmodel lm, out lrreport rep) + public static void clusterizerrunkmeans(clusterizerstate s, int k, out kmeansreport rep) { - lm = new linearmodel(); - rep = new lrreport(); - linreg.lrbuild(xy, npoints, nvars, lm.innerobj, rep.innerobj, null); + rep = new kmeansreport(); + clustering.clusterizerrunkmeans(s.innerobj, k, rep.innerobj, null); } - public static void lrbuild(double[,] xy, int npoints, int nvars, out linearmodel lm, out lrreport rep, alglib.xparams _params) + public static void clusterizerrunkmeans(clusterizerstate s, int k, out kmeansreport rep, alglib.xparams _params) { - lm = new linearmodel(); - rep = new lrreport(); - linreg.lrbuild(xy, npoints, nvars, lm.innerobj, rep.innerobj, _params); + rep = new kmeansreport(); + clustering.clusterizerrunkmeans(s.innerobj, k, rep.innerobj, _params); } - - public static void lrbuild(double[,] xy, out linearmodel lm, out lrreport rep) - { - int npoints; - int nvars; - lm = new linearmodel(); - rep = new lrreport(); - npoints = ap.rows(xy); - nvars = ap.cols(xy)-1; + /************************************************************************* + This function returns distance matrix for dataset + + INPUT PARAMETERS: + XY - array[NPoints,NFeatures], dataset + NPoints - number of points, >=0 + NFeatures- number of features, >=1 + DistType- distance function: + * 0 Chebyshev distance (L-inf norm) + * 1 city block distance (L1 norm) + * 2 Euclidean distance (L2 norm, non-squared) + * 10 Pearson correlation: + dist(a,b) = 1-corr(a,b) + * 11 Absolute Pearson correlation: + dist(a,b) = 1-|corr(a,b)| + * 12 Uncentered Pearson correlation (cosine of the angle): + dist(a,b) = a'*b/(|a|*|b|) + * 13 Absolute uncentered Pearson correlation + dist(a,b) = |a'*b|/(|a|*|b|) + * 20 Spearman rank correlation: + dist(a,b) = 1-rankcorr(a,b) + * 21 Absolute Spearman rank correlation + dist(a,b) = 1-|rankcorr(a,b)| + + OUTPUT PARAMETERS: + D - array[NPoints,NPoints], distance matrix + (full matrix is returned, with lower and upper triangles) + + NOTE: different distance functions have different performance penalty: + * Euclidean or Pearson correlation distances are the fastest ones + * Spearman correlation distance function is a bit slower + * city block and Chebyshev distances are order of magnitude slower + + The reason behing difference in performance is that correlation-based + distance functions are computed using optimized linear algebra kernels, + while Chebyshev and city block distance functions are computed using + simple nested loops with two branches at each iteration. + + ! FREE EDITION OF ALGLIB: + ! + ! Free Edition of ALGLIB supports following important features for this + ! function: + ! * C++ version: x64 SIMD support using C++ intrinsics + ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics + ! + ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB + ! Reference Manual in order to find out how to activate SIMD support + ! in ALGLIB. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! * hardware vendor (Intel, ARM) implementations of linear algebra and + ! other primitives (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. + + -- ALGLIB -- + Copyright 10.07.2012 by Bochkanov Sergey + *************************************************************************/ + public static void clusterizergetdistances(double[,] xy, int npoints, int nfeatures, int disttype, out double[,] d) + { + d = new double[0,0]; + clustering.clusterizergetdistances(xy, npoints, nfeatures, disttype, ref d, null); + } + + public static void clusterizergetdistances(double[,] xy, int npoints, int nfeatures, int disttype, out double[,] d, alglib.xparams _params) + { + d = new double[0,0]; + clustering.clusterizergetdistances(xy, npoints, nfeatures, disttype, ref d, _params); + } + + /************************************************************************* + This function takes as input clusterization report Rep, desired clusters + count K, and builds top K clusters from hierarchical clusterization tree. + It returns assignment of points to clusters (array of cluster indexes). + + INPUT PARAMETERS: + Rep - report from ClusterizerRunAHC() performed on XY + K - desired number of clusters, 1<=K<=NPoints. + K can be zero only when NPoints=0. + + OUTPUT PARAMETERS: + CIdx - array[NPoints], I-th element contains cluster index (from + 0 to K-1) for I-th point of the dataset. + CZ - array[K]. This array allows to convert cluster indexes + returned by this function to indexes used by Rep.Z. J-th + cluster returned by this function corresponds to CZ[J]-th + cluster stored in Rep.Z/PZ/PM. + It is guaranteed that CZ[I]=0 + + OUTPUT PARAMETERS: + K - number of clusters, 1<=K<=NPoints + CIdx - array[NPoints], I-th element contains cluster index (from + 0 to K-1) for I-th point of the dataset. + CZ - array[K]. This array allows to convert cluster indexes + returned by this function to indexes used by Rep.Z. J-th + cluster returned by this function corresponds to CZ[J]-th + cluster stored in Rep.Z/PZ/PM. + It is guaranteed that CZ[I]NVars+1. An exception is + generated otherwise. + NVars - number of independent variables + + OUTPUT PARAMETERS: + LM - linear model in the ALGLIB format. Use subroutines of + this unit to work with the model. + Rep - additional results, see comments on LRReport structure. + + -- ALGLIB -- + Copyright 02.08.2008 by Bochkanov Sergey + *************************************************************************/ + public static void lrbuild(double[,] xy, int npoints, int nvars, out linearmodel lm, out lrreport rep) + { + lm = new linearmodel(); + rep = new lrreport(); + linreg.lrbuild(xy, npoints, nvars, lm.innerobj, rep.innerobj, null); + } + + public static void lrbuild(double[,] xy, int npoints, int nvars, out linearmodel lm, out lrreport rep, alglib.xparams _params) + { + lm = new linearmodel(); + rep = new lrreport(); + linreg.lrbuild(xy, npoints, nvars, lm.innerobj, rep.innerobj, _params); + } + + public static void lrbuild(double[,] xy, out linearmodel lm, out lrreport rep) + { + int npoints; + int nvars; + + lm = new linearmodel(); + rep = new lrreport(); + npoints = ap.rows(xy); + nvars = ap.cols(xy)-1; linreg.lrbuild(xy, npoints, nvars, lm.innerobj, rep.innerobj, null); return; @@ -6350,463 +6911,857 @@ public static double lravgrelerror(linearmodel lm, double[,] xy, int npoints, al public partial class alglib { - - /************************************************************************* - Filters: simple moving averages (unsymmetric). - - This filter replaces array by results of SMA(K) filter. SMA(K) is defined - as filter which averages at most K previous points (previous - not points - AROUND central point) - or less, in case of the first K-1 points. - - INPUT PARAMETERS: - X - array[N], array to process. It can be larger than N, - in this case only first N points are processed. - N - points count, N>=0 - K - K>=1 (K can be larger than N , such cases will be - correctly handled). Window width. K=1 corresponds to - identity transformation (nothing changes). - - OUTPUT PARAMETERS: - X - array, whose first N elements were processed with SMA(K) - - NOTE 1: this function uses efficient in-place algorithm which does not - allocate temporary arrays. - - NOTE 2: this algorithm makes only one pass through array and uses running - sum to speed-up calculation of the averages. Additional measures - are taken to ensure that running sum on a long sequence of zero - elements will be correctly reset to zero even in the presence of - round-off error. - - NOTE 3: this is unsymmetric version of the algorithm, which does NOT - averages points after the current one. Only X[i], X[i-1], ... are - used when calculating new value of X[i]. We should also note that - this algorithm uses BOTH previous points and current one, i.e. - new value of X[i] depends on BOTH previous point and X[i] itself. - -- ALGLIB -- - Copyright 25.10.2011 by Bochkanov Sergey + /************************************************************************* + Neural networks ensemble *************************************************************************/ - public static void filtersma(double[] x, int n, int k) + public class mlpensemble : alglibobject { + // + // Public declarations + // - filters.filtersma(x, n, k, null); + public mlpensemble() + { + _innerobj = new mlpe.mlpensemble(); + } + + public override alglib.alglibobject make_copy() + { + return new mlpensemble((mlpe.mlpensemble)_innerobj.make_copy()); + } + + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private mlpe.mlpensemble _innerobj; + public mlpe.mlpensemble innerobj { get { return _innerobj; } } + public mlpensemble(mlpe.mlpensemble obj) + { + _innerobj = obj; + } } + + + /************************************************************************* + This function serializes data structure to string/stream. - public static void filtersma(double[] x, int n, int k, alglib.xparams _params) + Important properties of s_out: + * it contains alphanumeric characters, dots, underscores, minus signs + * these symbols are grouped into words, which are separated by spaces + and Windows-style (CR+LF) newlines + * although serializer uses spaces and CR+LF as separators, you can + replace any separator character by arbitrary combination of spaces, + tabs, Windows or Unix newlines. It allows flexible reformatting of + the string in case you want to include it into a text or XML file. + But you should not insert separators into the middle of the "words" + nor should you change the case of letters. + * s_out can be freely moved between 32-bit and 64-bit systems, little + and big endian machines, and so on. You can serialize structure on + 32-bit machine and unserialize it on 64-bit one (or vice versa), or + serialize it on SPARC and unserialize on x86. You can also + serialize it in C++ version of ALGLIB and unserialize it in C# one, + and vice versa. + *************************************************************************/ + public static void mlpeserialize(mlpensemble obj, out string s_out) { - - filters.filtersma(x, n, k, _params); + alglib.serializer s = new alglib.serializer(); + s.alloc_start(); + mlpe.mlpealloc(s, obj.innerobj, null); + s.sstart_str(); + mlpe.mlpeserialize(s, obj.innerobj, null); + s.stop(); + s_out = s.get_string(); } - - public static void filtersma(double[] x, int k) - { - int n; + + + /************************************************************************* + This function serializes data structure to string/stream. - - n = ap.len(x); - filters.filtersma(x, n, k, null); - - return; - } - - public static void filtersma(double[] x, int k, alglib.xparams _params) + Important properties of s_out: + * it contains alphanumeric characters, dots, underscores, minus signs + * these symbols are grouped into words, which are separated by spaces + and Windows-style (CR+LF) newlines + * although serializer uses spaces and CR+LF as separators, you can + replace any separator character by arbitrary combination of spaces, + tabs, Windows or Unix newlines. It allows flexible reformatting of + the string in case you want to include it into a text or XML file. + But you should not insert separators into the middle of the "words" + nor should you change the case of letters. + * s_out can be freely moved between 32-bit and 64-bit systems, little + and big endian machines, and so on. You can serialize structure on + 32-bit machine and unserialize it on 64-bit one (or vice versa), or + serialize it on SPARC and unserialize on x86. You can also + serialize it in C++ version of ALGLIB and unserialize it in C# one, + and vice versa. + *************************************************************************/ + public static void mlpeserialize(mlpensemble obj, System.IO.Stream stream_out) { - int n; - - - n = ap.len(x); - filters.filtersma(x, n, k, _params); - - return; + alglib.serializer s = new alglib.serializer(); + s.alloc_start(); + mlpe.mlpealloc(s, obj.innerobj, null); + s.sstart_stream(stream_out); + mlpe.mlpeserialize(s, obj.innerobj, null); + s.stop(); } - - /************************************************************************* - Filters: exponential moving averages. - - This filter replaces array by results of EMA(alpha) filter. EMA(alpha) is - defined as filter which replaces X[] by S[]: - S[0] = X[0] - S[t] = alpha*X[t] + (1-alpha)*S[t-1] - - INPUT PARAMETERS: - X - array[N], array to process. It can be larger than N, - in this case only first N points are processed. - N - points count, N>=0 - alpha - 0=0 - K - K>=1 (K can be larger than N , such cases will be - correctly handled). Window width. K=1 corresponds to - identity transformation (nothing changes). - - OUTPUT PARAMETERS: - X - array, whose first N elements were processed with LRMA(K) - - NOTE 1: this function uses efficient in-place algorithm which does not - allocate temporary arrays. - - NOTE 2: this algorithm makes only one pass through array and uses running - sum to speed-up calculation of the averages. Additional measures - are taken to ensure that running sum on a long sequence of zero - elements will be correctly reset to zero even in the presence of - round-off error. - - NOTE 3: this is unsymmetric version of the algorithm, which does NOT - averages points after the current one. Only X[i], X[i-1], ... are - used when calculating new value of X[i]. We should also note that - this algorithm uses BOTH previous points and current one, i.e. - new value of X[i] depends on BOTH previous point and X[i] itself. + Like MLPCreate2, but for ensembles. -- ALGLIB -- - Copyright 25.10.2011 by Bochkanov Sergey + Copyright 18.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void filterlrma(double[] x, int n, int k) + public static void mlpecreate2(int nin, int nhid1, int nhid2, int nout, int ensemblesize, out mlpensemble ensemble) { - - filters.filterlrma(x, n, k, null); + ensemble = new mlpensemble(); + mlpe.mlpecreate2(nin, nhid1, nhid2, nout, ensemblesize, ensemble.innerobj, null); } - public static void filterlrma(double[] x, int n, int k, alglib.xparams _params) + public static void mlpecreate2(int nin, int nhid1, int nhid2, int nout, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) { - - filters.filterlrma(x, n, k, _params); + ensemble = new mlpensemble(); + mlpe.mlpecreate2(nin, nhid1, nhid2, nout, ensemblesize, ensemble.innerobj, _params); } - - public static void filterlrma(double[] x, int k) - { - int n; - - - n = ap.len(x); - filters.filterlrma(x, n, k, null); - return; - } - - public static void filterlrma(double[] x, int k, alglib.xparams _params) + /************************************************************************* + Like MLPCreateB0, but for ensembles. + + -- ALGLIB -- + Copyright 18.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static void mlpecreateb0(int nin, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble) { - int n; + ensemble = new mlpensemble(); + mlpe.mlpecreateb0(nin, nout, b, d, ensemblesize, ensemble.innerobj, null); + } + public static void mlpecreateb0(int nin, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + { + ensemble = new mlpensemble(); + mlpe.mlpecreateb0(nin, nout, b, d, ensemblesize, ensemble.innerobj, _params); + } - n = ap.len(x); - filters.filterlrma(x, n, k, _params); + /************************************************************************* + Like MLPCreateB1, but for ensembles. + + -- ALGLIB -- + Copyright 18.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static void mlpecreateb1(int nin, int nhid, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble) + { + ensemble = new mlpensemble(); + mlpe.mlpecreateb1(nin, nhid, nout, b, d, ensemblesize, ensemble.innerobj, null); + } - return; + public static void mlpecreateb1(int nin, int nhid, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + { + ensemble = new mlpensemble(); + mlpe.mlpecreateb1(nin, nhid, nout, b, d, ensemblesize, ensemble.innerobj, _params); } - -} -public partial class alglib -{ - - + /************************************************************************* - This object stores state of the SSA model. + Like MLPCreateB2, but for ensembles. - You should use ALGLIB functions to work with this object. + -- ALGLIB -- + Copyright 18.02.2009 by Bochkanov Sergey *************************************************************************/ - public class ssamodel : alglibobject + public static void mlpecreateb2(int nin, int nhid1, int nhid2, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble) { - // - // Public declarations - // - - public ssamodel() - { - _innerobj = new ssa.ssamodel(); - } - - public override alglib.alglibobject make_copy() - { - return new ssamodel((ssa.ssamodel)_innerobj.make_copy()); - } + ensemble = new mlpensemble(); + mlpe.mlpecreateb2(nin, nhid1, nhid2, nout, b, d, ensemblesize, ensemble.innerobj, null); + } - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private ssa.ssamodel _innerobj; - public ssa.ssamodel innerobj { get { return _innerobj; } } - public ssamodel(ssa.ssamodel obj) - { - _innerobj = obj; - } + public static void mlpecreateb2(int nin, int nhid1, int nhid2, int nout, double b, double d, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + { + ensemble = new mlpensemble(); + mlpe.mlpecreateb2(nin, nhid1, nhid2, nout, b, d, ensemblesize, ensemble.innerobj, _params); } /************************************************************************* - This function creates SSA model object. Right after creation model is in - "dummy" mode - you can add data, but analyzing/prediction will return - just zeros (it assumes that basis is empty). - - HOW TO USE SSA MODEL: - - 1. create model with ssacreate() - 2. add data with one/many ssaaddsequence() calls - 3. choose SSA algorithm with one of ssasetalgo...() functions: - * ssasetalgotopkdirect() for direct one-run analysis - * ssasetalgotopkrealtime() for algorithm optimized for many subsequent - runs with warm-start capabilities - * ssasetalgoprecomputed() for user-supplied basis - 4. set window width with ssasetwindow() - 5. perform one of the analysis-related activities: - a) call ssagetbasis() to get basis - b) call ssaanalyzelast() ssaanalyzesequence() or ssaanalyzelastwindow() - to perform analysis (trend/noise separation) - c) call one of the forecasting functions (ssaforecastlast() or - ssaforecastsequence()) to perform prediction; alternatively, you can - extract linear recurrence coefficients with ssagetlrr(). - SSA analysis will be performed during first call to analysis-related - function. SSA model is smart enough to track all changes in the dataset - and model settings, to cache previously computed basis and to - re-evaluate basis only when necessary. - - Additionally, if your setting involves constant stream of incoming data, - you can perform quick update already calculated model with one of the - incremental append-and-update functions: ssaappendpointandupdate() or - ssaappendsequenceandupdate(). - - NOTE: steps (2), (3), (4) can be performed in arbitrary order. - - INPUT PARAMETERS: - none - - OUTPUT PARAMETERS: - S - structure which stores model state + Like MLPCreateR0, but for ensembles. -- ALGLIB -- - Copyright 30.10.2017 by Bochkanov Sergey + Copyright 18.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void ssacreate(out ssamodel s) + public static void mlpecreater0(int nin, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble) { - s = new ssamodel(); - ssa.ssacreate(s.innerobj, null); + ensemble = new mlpensemble(); + mlpe.mlpecreater0(nin, nout, a, b, ensemblesize, ensemble.innerobj, null); } - public static void ssacreate(out ssamodel s, alglib.xparams _params) + public static void mlpecreater0(int nin, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) { - s = new ssamodel(); - ssa.ssacreate(s.innerobj, _params); + ensemble = new mlpensemble(); + mlpe.mlpecreater0(nin, nout, a, b, ensemblesize, ensemble.innerobj, _params); } /************************************************************************* - This function sets window width for SSA model. You should call it before - analysis phase. Default window width is 1 (not for real use). - - Special notes: - * this function call can be performed at any moment before first call to - analysis-related functions - * changing window width invalidates internally stored basis; if you change - window width AFTER you call analysis-related function, next analysis - phase will require re-calculation of the basis according to current - algorithm. - * calling this function with exactly same window width as current one has - no effect - * if you specify window width larger than any data sequence stored in the - model, analysis will return zero basis. - - INPUT PARAMETERS: - S - SSA model created with ssacreate() - WindowWidth - >=1, new window width - - OUTPUT PARAMETERS: - S - SSA model, updated + Like MLPCreateR1, but for ensembles. -- ALGLIB -- - Copyright 30.10.2017 by Bochkanov Sergey + Copyright 18.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void ssasetwindow(ssamodel s, int windowwidth) + public static void mlpecreater1(int nin, int nhid, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble) { + ensemble = new mlpensemble(); + mlpe.mlpecreater1(nin, nhid, nout, a, b, ensemblesize, ensemble.innerobj, null); + } - ssa.ssasetwindow(s.innerobj, windowwidth, null); + public static void mlpecreater1(int nin, int nhid, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + { + ensemble = new mlpensemble(); + mlpe.mlpecreater1(nin, nhid, nout, a, b, ensemblesize, ensemble.innerobj, _params); } - public static void ssasetwindow(ssamodel s, int windowwidth, alglib.xparams _params) + /************************************************************************* + Like MLPCreateR2, but for ensembles. + + -- ALGLIB -- + Copyright 18.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static void mlpecreater2(int nin, int nhid1, int nhid2, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble) { + ensemble = new mlpensemble(); + mlpe.mlpecreater2(nin, nhid1, nhid2, nout, a, b, ensemblesize, ensemble.innerobj, null); + } - ssa.ssasetwindow(s.innerobj, windowwidth, _params); + public static void mlpecreater2(int nin, int nhid1, int nhid2, int nout, double a, double b, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + { + ensemble = new mlpensemble(); + mlpe.mlpecreater2(nin, nhid1, nhid2, nout, a, b, ensemblesize, ensemble.innerobj, _params); } /************************************************************************* - This function sets seed which is used to initialize internal RNG when - we make pseudorandom decisions on model updates. - - By default, deterministic seed is used - which results in same sequence of - pseudorandom decisions every time you run SSA model. If you specify non- - deterministic seed value, then SSA model may return slightly different - results after each run. - - This function can be useful when you have several SSA models updated with - sseappendpointandupdate() called with 01 means that delayed power-up is performed + Like MLPCreateC2, but for ensembles. -- ALGLIB -- - Copyright 03.11.2017 by Bochkanov Sergey + Copyright 18.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void ssasetpoweruplength(ssamodel s, int pwlen) + public static void mlpecreatec2(int nin, int nhid1, int nhid2, int nout, int ensemblesize, out mlpensemble ensemble) { + ensemble = new mlpensemble(); + mlpe.mlpecreatec2(nin, nhid1, nhid2, nout, ensemblesize, ensemble.innerobj, null); + } - ssa.ssasetpoweruplength(s.innerobj, pwlen, null); + public static void mlpecreatec2(int nin, int nhid1, int nhid2, int nout, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + { + ensemble = new mlpensemble(); + mlpe.mlpecreatec2(nin, nhid1, nhid2, nout, ensemblesize, ensemble.innerobj, _params); } - public static void ssasetpoweruplength(ssamodel s, int pwlen, alglib.xparams _params) + /************************************************************************* + Creates ensemble from network. Only network geometry is copied. + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static void mlpecreatefromnetwork(multilayerperceptron network, int ensemblesize, out mlpensemble ensemble) { + ensemble = new mlpensemble(); + mlpe.mlpecreatefromnetwork(network.innerobj, ensemblesize, ensemble.innerobj, null); + } - ssa.ssasetpoweruplength(s.innerobj, pwlen, _params); + public static void mlpecreatefromnetwork(multilayerperceptron network, int ensemblesize, out mlpensemble ensemble, alglib.xparams _params) + { + ensemble = new mlpensemble(); + mlpe.mlpecreatefromnetwork(network.innerobj, ensemblesize, ensemble.innerobj, _params); } /************************************************************************* - This function sets memory limit of SSA analysis. - - Straightforward SSA with sequence length T and window width W needs O(T*W) - memory. It is possible to reduce memory consumption by splitting task into - smaller chunks. - - Thus function allows you to specify approximate memory limit (measured in - double precision numbers used for buffers). Actual memory consumption will - be comparable to the number specified by you. - - Default memory limit is 50.000.000 (400Mbytes) in current version. - - INPUT PARAMETERS: - S - SSA model - MemLimit- memory limit, >=0. Zero value means no limit. + Randomization of MLP ensemble -- ALGLIB -- - Copyright 20.12.2017 by Bochkanov Sergey + Copyright 17.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void ssasetmemorylimit(ssamodel s, int memlimit) + public static void mlperandomize(mlpensemble ensemble) { - ssa.ssasetmemorylimit(s.innerobj, memlimit, null); + mlpe.mlperandomize(ensemble.innerobj, null); } - public static void ssasetmemorylimit(ssamodel s, int memlimit, alglib.xparams _params) + public static void mlperandomize(mlpensemble ensemble, alglib.xparams _params) { - ssa.ssasetmemorylimit(s.innerobj, memlimit, _params); + mlpe.mlperandomize(ensemble.innerobj, _params); } /************************************************************************* - This function adds data sequence to SSA model. Only single-dimensional - sequences are supported. - - What is a sequences? Following definitions/requirements apply: - * a sequence is an array of values measured in subsequent, equally - separated time moments (ticks). - * you may have many sequences in your dataset; say, one sequence may - correspond to one trading session. - * sequence length should be larger than current window length (shorter - sequences will be ignored during analysis). - * analysis is performed within a sequence; different sequences are NOT - stacked together to produce one large contiguous stream of data. - * analysis is performed for all sequences at once, i.e. same set of basis - vectors is computed for all sequences + Return ensemble properties (number of inputs and outputs). + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static void mlpeproperties(mlpensemble ensemble, out int nin, out int nout) + { + nin = 0; + nout = 0; + mlpe.mlpeproperties(ensemble.innerobj, ref nin, ref nout, null); + } + + public static void mlpeproperties(mlpensemble ensemble, out int nin, out int nout, alglib.xparams _params) + { + nin = 0; + nout = 0; + mlpe.mlpeproperties(ensemble.innerobj, ref nin, ref nout, _params); + } + + /************************************************************************* + Return normalization type (whether ensemble is SOFTMAX-normalized or not). + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static bool mlpeissoftmax(mlpensemble ensemble) + { + + return mlpe.mlpeissoftmax(ensemble.innerobj, null); + } + + public static bool mlpeissoftmax(mlpensemble ensemble, alglib.xparams _params) + { + + return mlpe.mlpeissoftmax(ensemble.innerobj, _params); + } + + /************************************************************************* + Procesing + + INPUT PARAMETERS: + Ensemble- neural networks ensemble + X - input vector, array[0..NIn-1]. + Y - (possibly) preallocated buffer; if size of Y is less than + NOut, it will be reallocated. If it is large enough, it + is NOT reallocated, so we can save some time on reallocation. + + + OUTPUT PARAMETERS: + Y - result. Regression estimate when solving regression task, + vector of posterior probabilities for classification task. + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static void mlpeprocess(mlpensemble ensemble, double[] x, ref double[] y) + { + + mlpe.mlpeprocess(ensemble.innerobj, x, ref y, null); + } + + public static void mlpeprocess(mlpensemble ensemble, double[] x, ref double[] y, alglib.xparams _params) + { + + mlpe.mlpeprocess(ensemble.innerobj, x, ref y, _params); + } + + /************************************************************************* + 'interactive' variant of MLPEProcess for languages like Python which + support constructs like "Y = MLPEProcess(LM,X)" and interactive mode of the + interpreter + + This function allocates new array on each call, so it is significantly + slower than its 'non-interactive' counterpart, but it is more convenient + when you call it from command line. + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static void mlpeprocessi(mlpensemble ensemble, double[] x, out double[] y) + { + y = new double[0]; + mlpe.mlpeprocessi(ensemble.innerobj, x, ref y, null); + } + + public static void mlpeprocessi(mlpensemble ensemble, double[] x, out double[] y, alglib.xparams _params) + { + y = new double[0]; + mlpe.mlpeprocessi(ensemble.innerobj, x, ref y, _params); + } + + /************************************************************************* + Relative classification error on the test set + + INPUT PARAMETERS: + Ensemble- ensemble + XY - test set + NPoints - test set size + + RESULT: + percent of incorrectly classified cases. + Works both for classifier betwork and for regression networks which + are used as classifiers. + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static double mlperelclserror(mlpensemble ensemble, double[,] xy, int npoints) + { + + return mlpe.mlperelclserror(ensemble.innerobj, xy, npoints, null); + } + + public static double mlperelclserror(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + { + + return mlpe.mlperelclserror(ensemble.innerobj, xy, npoints, _params); + } + + /************************************************************************* + Average cross-entropy (in bits per element) on the test set + + INPUT PARAMETERS: + Ensemble- ensemble + XY - test set + NPoints - test set size + + RESULT: + CrossEntropy/(NPoints*LN(2)). + Zero if ensemble solves regression task. + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static double mlpeavgce(mlpensemble ensemble, double[,] xy, int npoints) + { + + return mlpe.mlpeavgce(ensemble.innerobj, xy, npoints, null); + } + + public static double mlpeavgce(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + { + + return mlpe.mlpeavgce(ensemble.innerobj, xy, npoints, _params); + } + + /************************************************************************* + RMS error on the test set + + INPUT PARAMETERS: + Ensemble- ensemble + XY - test set + NPoints - test set size + + RESULT: + root mean square error. + Its meaning for regression task is obvious. As for classification task + RMS error means error when estimating posterior probabilities. + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static double mlpermserror(mlpensemble ensemble, double[,] xy, int npoints) + { + + return mlpe.mlpermserror(ensemble.innerobj, xy, npoints, null); + } + + public static double mlpermserror(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + { + + return mlpe.mlpermserror(ensemble.innerobj, xy, npoints, _params); + } + + /************************************************************************* + Average error on the test set + + INPUT PARAMETERS: + Ensemble- ensemble + XY - test set + NPoints - test set size + + RESULT: + Its meaning for regression task is obvious. As for classification task + it means average error when estimating posterior probabilities. + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static double mlpeavgerror(mlpensemble ensemble, double[,] xy, int npoints) + { + + return mlpe.mlpeavgerror(ensemble.innerobj, xy, npoints, null); + } + + public static double mlpeavgerror(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + { + + return mlpe.mlpeavgerror(ensemble.innerobj, xy, npoints, _params); + } + + /************************************************************************* + Average relative error on the test set + + INPUT PARAMETERS: + Ensemble- ensemble + XY - test set + NPoints - test set size + + RESULT: + Its meaning for regression task is obvious. As for classification task + it means average relative error when estimating posterior probabilities. + + -- ALGLIB -- + Copyright 17.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static double mlpeavgrelerror(mlpensemble ensemble, double[,] xy, int npoints) + { + + return mlpe.mlpeavgrelerror(ensemble.innerobj, xy, npoints, null); + } + + public static double mlpeavgrelerror(mlpensemble ensemble, double[,] xy, int npoints, alglib.xparams _params) + { + + return mlpe.mlpeavgrelerror(ensemble.innerobj, xy, npoints, _params); + } + +} +public partial class alglib +{ + + + /************************************************************************* + This object stores state of the SSA model. + + You should use ALGLIB functions to work with this object. + *************************************************************************/ + public class ssamodel : alglibobject + { + // + // Public declarations + // + + public ssamodel() + { + _innerobj = new ssa.ssamodel(); + } + + public override alglib.alglibobject make_copy() + { + return new ssamodel((ssa.ssamodel)_innerobj.make_copy()); + } + + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private ssa.ssamodel _innerobj; + public ssa.ssamodel innerobj { get { return _innerobj; } } + public ssamodel(ssa.ssamodel obj) + { + _innerobj = obj; + } + } + + /************************************************************************* + This function creates SSA model object. Right after creation model is in + "dummy" mode - you can add data, but analyzing/prediction will return + just zeros (it assumes that basis is empty). + + HOW TO USE SSA MODEL: + + 1. create model with ssacreate() + 2. add data with one/many ssaaddsequence() calls + 3. choose SSA algorithm with one of ssasetalgo...() functions: + * ssasetalgotopkdirect() for direct one-run analysis + * ssasetalgotopkrealtime() for algorithm optimized for many subsequent + runs with warm-start capabilities + * ssasetalgoprecomputed() for user-supplied basis + 4. set window width with ssasetwindow() + 5. perform one of the analysis-related activities: + a) call ssagetbasis() to get basis + b) call ssaanalyzelast() ssaanalyzesequence() or ssaanalyzelastwindow() + to perform analysis (trend/noise separation) + c) call one of the forecasting functions (ssaforecastlast() or + ssaforecastsequence()) to perform prediction; alternatively, you can + extract linear recurrence coefficients with ssagetlrr(). + SSA analysis will be performed during first call to analysis-related + function. SSA model is smart enough to track all changes in the dataset + and model settings, to cache previously computed basis and to + re-evaluate basis only when necessary. + + Additionally, if your setting involves constant stream of incoming data, + you can perform quick update already calculated model with one of the + incremental append-and-update functions: ssaappendpointandupdate() or + ssaappendsequenceandupdate(). + + NOTE: steps (2), (3), (4) can be performed in arbitrary order. + + INPUT PARAMETERS: + none + + OUTPUT PARAMETERS: + S - structure which stores model state + + -- ALGLIB -- + Copyright 30.10.2017 by Bochkanov Sergey + *************************************************************************/ + public static void ssacreate(out ssamodel s) + { + s = new ssamodel(); + ssa.ssacreate(s.innerobj, null); + } + + public static void ssacreate(out ssamodel s, alglib.xparams _params) + { + s = new ssamodel(); + ssa.ssacreate(s.innerobj, _params); + } + + /************************************************************************* + This function sets window width for SSA model. You should call it before + analysis phase. Default window width is 1 (not for real use). + + Special notes: + * this function call can be performed at any moment before first call to + analysis-related functions + * changing window width invalidates internally stored basis; if you change + window width AFTER you call analysis-related function, next analysis + phase will require re-calculation of the basis according to current + algorithm. + * calling this function with exactly same window width as current one has + no effect + * if you specify window width larger than any data sequence stored in the + model, analysis will return zero basis. + + INPUT PARAMETERS: + S - SSA model created with ssacreate() + WindowWidth - >=1, new window width + + OUTPUT PARAMETERS: + S - SSA model, updated + + -- ALGLIB -- + Copyright 30.10.2017 by Bochkanov Sergey + *************************************************************************/ + public static void ssasetwindow(ssamodel s, int windowwidth) + { + + ssa.ssasetwindow(s.innerobj, windowwidth, null); + } + + public static void ssasetwindow(ssamodel s, int windowwidth, alglib.xparams _params) + { + + ssa.ssasetwindow(s.innerobj, windowwidth, _params); + } + + /************************************************************************* + This function sets seed which is used to initialize internal RNG when + we make pseudorandom decisions on model updates. + + By default, deterministic seed is used - which results in same sequence of + pseudorandom decisions every time you run SSA model. If you specify non- + deterministic seed value, then SSA model may return slightly different + results after each run. + + This function can be useful when you have several SSA models updated with + sseappendpointandupdate() called with 01 means that delayed power-up is performed + + -- ALGLIB -- + Copyright 03.11.2017 by Bochkanov Sergey + *************************************************************************/ + public static void ssasetpoweruplength(ssamodel s, int pwlen) + { + + ssa.ssasetpoweruplength(s.innerobj, pwlen, null); + } + + public static void ssasetpoweruplength(ssamodel s, int pwlen, alglib.xparams _params) + { + + ssa.ssasetpoweruplength(s.innerobj, pwlen, _params); + } + + /************************************************************************* + This function sets memory limit of SSA analysis. + + Straightforward SSA with sequence length T and window width W needs O(T*W) + memory. It is possible to reduce memory consumption by splitting task into + smaller chunks. + + Thus function allows you to specify approximate memory limit (measured in + double precision numbers used for buffers). Actual memory consumption will + be comparable to the number specified by you. + + Default memory limit is 50.000.000 (400Mbytes) in current version. + + INPUT PARAMETERS: + S - SSA model + MemLimit- memory limit, >=0. Zero value means no limit. + + -- ALGLIB -- + Copyright 20.12.2017 by Bochkanov Sergey + *************************************************************************/ + public static void ssasetmemorylimit(ssamodel s, int memlimit) + { + + ssa.ssasetmemorylimit(s.innerobj, memlimit, null); + } + + public static void ssasetmemorylimit(ssamodel s, int memlimit, alglib.xparams _params) + { + + ssa.ssasetmemorylimit(s.innerobj, memlimit, _params); + } + + /************************************************************************* + This function adds data sequence to SSA model. Only single-dimensional + sequences are supported. + + What is a sequences? Following definitions/requirements apply: + * a sequence is an array of values measured in subsequent, equally + separated time moments (ticks). + * you may have many sequences in your dataset; say, one sequence may + correspond to one trading session. + * sequence length should be larger than current window length (shorter + sequences will be ignored during analysis). + * analysis is performed within a sequence; different sequences are NOT + stacked together to produce one large contiguous stream of data. + * analysis is performed for all sequences at once, i.e. same set of basis + vectors is computed for all sequences INCREMENTAL ANALYSIS @@ -8048,179 +9003,209 @@ public partial class alglib /************************************************************************* - Multiclass Fisher LDA + Filters: simple moving averages (unsymmetric). - The function finds coefficients of a linear combination which optimally - separates training set. Most suited for 2-class problems, see fisherldan() - for an variant that returns N-dimensional basis. + This filter replaces array by results of SMA(K) filter. SMA(K) is defined + as filter which averages at most K previous points (previous - not points + AROUND central point) - or less, in case of the first K-1 points. INPUT PARAMETERS: - XY - training set, array[NPoints,NVars+1]. - First NVars columns store values of independent - variables, the next column stores class index (from 0 - to NClasses-1) which dataset element belongs to. - Fractional values are rounded to the nearest integer. - The class index must be in the [0,NClasses-1] range, - an exception is generated otherwise. - NPoints - training set size, NPoints>=0 - NVars - number of independent variables, NVars>=1 - NClasses - number of classes, NClasses>=2 - + X - array[N], array to process. It can be larger than N, + in this case only first N points are processed. + N - points count, N>=0 + K - K>=1 (K can be larger than N , such cases will be + correctly handled). Window width. K=1 corresponds to + identity transformation (nothing changes). OUTPUT PARAMETERS: - W - linear combination coefficients, array[NVars] + X - array, whose first N elements were processed with SMA(K) - ! FREE EDITION OF ALGLIB: - ! - ! Free Edition of ALGLIB supports following important features for this - ! function: - ! * C++ version: x64 SIMD support using C++ intrinsics - ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics - ! - ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB - ! Reference Manual in order to find out how to activate SIMD support - ! in ALGLIB. + NOTE 1: this function uses efficient in-place algorithm which does not + allocate temporary arrays. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! * hardware vendor (Intel, ARM) implementations of linear algebra and - ! other primitives (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + NOTE 2: this algorithm makes only one pass through array and uses running + sum to speed-up calculation of the averages. Additional measures + are taken to ensure that running sum on a long sequence of zero + elements will be correctly reset to zero even in the presence of + round-off error. + + NOTE 3: this is unsymmetric version of the algorithm, which does NOT + averages points after the current one. Only X[i], X[i-1], ... are + used when calculating new value of X[i]. We should also note that + this algorithm uses BOTH previous points and current one, i.e. + new value of X[i] depends on BOTH previous point and X[i] itself. -- ALGLIB -- - Copyright 31.05.2008 by Bochkanov Sergey + Copyright 25.10.2011 by Bochkanov Sergey *************************************************************************/ - public static void fisherlda(double[,] xy, int npoints, int nvars, int nclasses, out double[] w) + public static void filtersma(double[] x, int n, int k) { - w = new double[0]; - lda.fisherlda(xy, npoints, nvars, nclasses, ref w, null); + + filters.filtersma(x, n, k, null); } - public static void fisherlda(double[,] xy, int npoints, int nvars, int nclasses, out double[] w, alglib.xparams _params) + public static void filtersma(double[] x, int n, int k, alglib.xparams _params) { - w = new double[0]; - lda.fisherlda(xy, npoints, nvars, nclasses, ref w, _params); + + filters.filtersma(x, n, k, _params); } - public static void fisherlda(double[,] xy, int nclasses, out double[] w) + public static void filtersma(double[] x, int k) { - int npoints; - int nvars; + int n; - w = new double[0]; - npoints = ap.rows(xy); - nvars = ap.cols(xy)-1; - lda.fisherlda(xy, npoints, nvars, nclasses, ref w, null); + + n = ap.len(x); + filters.filtersma(x, n, k, null); return; } - public static void fisherlda(double[,] xy, int nclasses, out double[] w, alglib.xparams _params) + public static void filtersma(double[] x, int k, alglib.xparams _params) { - int npoints; - int nvars; + int n; - w = new double[0]; - npoints = ap.rows(xy); - nvars = ap.cols(xy)-1; - lda.fisherlda(xy, npoints, nvars, nclasses, ref w, _params); + + n = ap.len(x); + filters.filtersma(x, n, k, _params); return; } /************************************************************************* - N-dimensional multiclass Fisher LDA + Filters: exponential moving averages. - Subroutine finds coefficients of linear combinations which optimally separates - training set on classes. It returns N-dimensional basis whose vector are sorted - by quality of training set separation (in descending order). + This filter replaces array by results of EMA(alpha) filter. EMA(alpha) is + defined as filter which replaces X[] by S[]: + S[0] = X[0] + S[t] = alpha*X[t] + (1-alpha)*S[t-1] INPUT PARAMETERS: - XY - training set, array[NPoints,NVars+1]. - First NVars columns store values of independent - variables, the next column stores class index (from 0 - to NClasses-1) which dataset element belongs to. - Fractional values are rounded to the nearest integer. - The class index must be in the [0,NClasses-1] range, - an exception is generated otherwise. - NPoints - training set size, NPoints>=0 - NVars - number of independent variables, NVars>=1 - NClasses - number of classes, NClasses>=2 + X - array[N], array to process. It can be larger than N, + in this case only first N points are processed. + N - points count, N>=0 + alpha - 0=0 + K - K>=1 (K can be larger than N , such cases will be + correctly handled). Window width. K=1 corresponds to + identity transformation (nothing changes). OUTPUT PARAMETERS: - W - basis, array[NVars,NVars] - columns of matrix stores basis vectors, sorted by - quality of training set separation (in descending order) + X - array, whose first N elements were processed with LRMA(K) - ! FREE EDITION OF ALGLIB: - ! - ! Free Edition of ALGLIB supports following important features for this - ! function: - ! * C++ version: x64 SIMD support using C++ intrinsics - ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics - ! - ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB - ! Reference Manual in order to find out how to activate SIMD support - ! in ALGLIB. + NOTE 1: this function uses efficient in-place algorithm which does not + allocate temporary arrays. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! * hardware vendor (Intel, ARM) implementations of linear algebra and - ! other primitives (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + NOTE 2: this algorithm makes only one pass through array and uses running + sum to speed-up calculation of the averages. Additional measures + are taken to ensure that running sum on a long sequence of zero + elements will be correctly reset to zero even in the presence of + round-off error. + + NOTE 3: this is unsymmetric version of the algorithm, which does NOT + averages points after the current one. Only X[i], X[i-1], ... are + used when calculating new value of X[i]. We should also note that + this algorithm uses BOTH previous points and current one, i.e. + new value of X[i] depends on BOTH previous point and X[i] itself. -- ALGLIB -- - Copyright 31.05.2008 by Bochkanov Sergey + Copyright 25.10.2011 by Bochkanov Sergey *************************************************************************/ - public static void fisherldan(double[,] xy, int npoints, int nvars, int nclasses, out double[,] w) + public static void filterlrma(double[] x, int n, int k) { - w = new double[0,0]; - lda.fisherldan(xy, npoints, nvars, nclasses, ref w, null); + + filters.filterlrma(x, n, k, null); } - public static void fisherldan(double[,] xy, int npoints, int nvars, int nclasses, out double[,] w, alglib.xparams _params) + public static void filterlrma(double[] x, int n, int k, alglib.xparams _params) { - w = new double[0,0]; - lda.fisherldan(xy, npoints, nvars, nclasses, ref w, _params); + + filters.filterlrma(x, n, k, _params); } - public static void fisherldan(double[,] xy, int nclasses, out double[,] w) + public static void filterlrma(double[] x, int k) { - int npoints; - int nvars; + int n; - w = new double[0,0]; - npoints = ap.rows(xy); - nvars = ap.cols(xy)-1; - lda.fisherldan(xy, npoints, nvars, nclasses, ref w, null); + + n = ap.len(x); + filters.filterlrma(x, n, k, null); return; } - public static void fisherldan(double[,] xy, int nclasses, out double[,] w, alglib.xparams _params) + public static void filterlrma(double[] x, int k, alglib.xparams _params) { - int npoints; - int nvars; + int n; - w = new double[0,0]; - npoints = ap.rows(xy); - nvars = ap.cols(xy)-1; - lda.fisherldan(xy, npoints, nvars, nclasses, ref w, _params); + + n = ap.len(x); + filters.filterlrma(x, n, k, _params); return; } @@ -8231,36 +9216,52 @@ public partial class alglib /************************************************************************* - This structure is a MCPD (Markov Chains for Population Data) solver. + Training report: + * RelCLSError - fraction of misclassified cases. + * AvgCE - acerage cross-entropy + * RMSError - root-mean-square error + * AvgError - average error + * AvgRelError - average relative error + * NGrad - number of gradient calculations + * NHess - number of Hessian calculations + * NCholesky - number of Cholesky decompositions - You should use ALGLIB functions in order to work with this object. + NOTE 1: RelCLSError/AvgCE are zero on regression problems. - -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + NOTE 2: on classification problems RMSError/AvgError/AvgRelError contain + errors in prediction of posterior probabilities *************************************************************************/ - public class mcpdstate : alglibobject + public class mlpreport : alglibobject { // // Public declarations // + public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } + public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } + public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } + public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } + public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } + public int ngrad { get { return _innerobj.ngrad; } set { _innerobj.ngrad = value; } } + public int nhess { get { return _innerobj.nhess; } set { _innerobj.nhess = value; } } + public int ncholesky { get { return _innerobj.ncholesky; } set { _innerobj.ncholesky = value; } } - public mcpdstate() + public mlpreport() { - _innerobj = new mcpd.mcpdstate(); + _innerobj = new mlptrain.mlpreport(); } public override alglib.alglibobject make_copy() { - return new mcpdstate((mcpd.mcpdstate)_innerobj.make_copy()); + return new mlpreport((mlptrain.mlpreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // - private mcpd.mcpdstate _innerobj; - public mcpd.mcpdstate innerobj { get { return _innerobj; } } - public mcpdstate(mcpd.mcpdstate obj) + private mlptrain.mlpreport _innerobj; + public mlptrain.mlpreport innerobj { get { return _innerobj; } } + public mlpreport(mlptrain.mlpreport obj) { _innerobj = obj; } @@ -8268,1233 +9269,1020 @@ public mcpdstate(mcpd.mcpdstate obj) /************************************************************************* - This structure is a MCPD training report: - InnerIterationsCount - number of inner iterations of the - underlying optimization algorithm - OuterIterationsCount - number of outer iterations of the - underlying optimization algorithm - NFEV - number of merit function evaluations - TerminationType - termination type - (same as for MinBLEIC optimizer, positive - values denote success, negative ones - - failure) - - -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Cross-validation estimates of generalization error *************************************************************************/ - public class mcpdreport : alglibobject + public class mlpcvreport : alglibobject { // // Public declarations // - public int inneriterationscount { get { return _innerobj.inneriterationscount; } set { _innerobj.inneriterationscount = value; } } - public int outeriterationscount { get { return _innerobj.outeriterationscount; } set { _innerobj.outeriterationscount = value; } } - public int nfev { get { return _innerobj.nfev; } set { _innerobj.nfev = value; } } - public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } + public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } + public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } + public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } + public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } + public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } - public mcpdreport() + public mlpcvreport() { - _innerobj = new mcpd.mcpdreport(); + _innerobj = new mlptrain.mlpcvreport(); } public override alglib.alglibobject make_copy() { - return new mcpdreport((mcpd.mcpdreport)_innerobj.make_copy()); + return new mlpcvreport((mlptrain.mlpcvreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // - private mcpd.mcpdreport _innerobj; - public mcpd.mcpdreport innerobj { get { return _innerobj; } } - public mcpdreport(mcpd.mcpdreport obj) + private mlptrain.mlpcvreport _innerobj; + public mlptrain.mlpcvreport innerobj { get { return _innerobj; } } + public mlpcvreport(mlptrain.mlpcvreport obj) { _innerobj = obj; } } - - /************************************************************************* - DESCRIPTION: - - This function creates MCPD (Markov Chains for Population Data) solver. - - This solver can be used to find transition matrix P for N-dimensional - prediction problem where transition from X[i] to X[i+1] is modelled as - X[i+1] = P*X[i] - where X[i] and X[i+1] are N-dimensional population vectors (components of - each X are non-negative), and P is a N*N transition matrix (elements of P - are non-negative, each column sums to 1.0). - - Such models arise when when: - * there is some population of individuals - * individuals can have different states - * individuals can transit from one state to another - * population size is constant, i.e. there is no new individuals and no one - leaves population - * you want to model transitions of individuals from one state into another - - USAGE: - - Here we give very brief outline of the MCPD. We strongly recommend you to - read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide - on data analysis which is available at http://www.alglib.net/dataanalysis/ - - 1. User initializes algorithm state with MCPDCreate() call - - 2. User adds one or more tracks - sequences of states which describe - evolution of a system being modelled from different starting conditions - - 3. User may add optional boundary, equality and/or linear constraints on - the coefficients of P by calling one of the following functions: - * MCPDSetEC() to set equality constraints - * MCPDSetBC() to set bound constraints - * MCPDSetLC() to set linear constraints - - 4. Optionally, user may set custom weights for prediction errors (by - default, algorithm assigns non-equal, automatically chosen weights for - errors in the prediction of different components of X). It can be done - with a call of MCPDSetPredictionWeights() function. - - 5. User calls MCPDSolve() function which takes algorithm state and - pointer (delegate, etc.) to callback function which calculates F/G. - 6. User calls MCPDResults() to get solution - - INPUT PARAMETERS: - N - problem dimension, N>=1 - OUTPUT PARAMETERS: - State - structure stores algorithm state + /************************************************************************* + Trainer object for neural network. - -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + You should not try to access fields of this object directly - use ALGLIB + functions to work with this object. *************************************************************************/ - public static void mcpdcreate(int n, out mcpdstate s) + public class mlptrainer : alglibobject { - s = new mcpdstate(); - mcpd.mcpdcreate(n, s.innerobj, null); - } + // + // Public declarations + // - public static void mcpdcreate(int n, out mcpdstate s, alglib.xparams _params) - { - s = new mcpdstate(); - mcpd.mcpdcreate(n, s.innerobj, _params); + public mlptrainer() + { + _innerobj = new mlptrain.mlptrainer(); + } + + public override alglib.alglibobject make_copy() + { + return new mlptrainer((mlptrain.mlptrainer)_innerobj.make_copy()); + } + + // + // Although some of declarations below are public, you should not use them + // They are intended for internal use only + // + private mlptrain.mlptrainer _innerobj; + public mlptrain.mlptrainer innerobj { get { return _innerobj; } } + public mlptrainer(mlptrain.mlptrainer obj) + { + _innerobj = obj; + } } /************************************************************************* - DESCRIPTION: - - This function is a specialized version of MCPDCreate() function, and we - recommend you to read comments for this function for general information - about MCPD solver. - - This function creates MCPD (Markov Chains for Population Data) solver - for "Entry-state" model, i.e. model where transition from X[i] to X[i+1] - is modelled as - X[i+1] = P*X[i] - where - X[i] and X[i+1] are N-dimensional state vectors - P is a N*N transition matrix - and one selected component of X[] is called "entry" state and is treated - in a special way: - system state always transits from "entry" state to some another state - system state can not transit from any state into "entry" state - Such conditions basically mean that row of P which corresponds to "entry" - state is zero. - - Such models arise when: - * there is some population of individuals - * individuals can have different states - * individuals can transit from one state to another - * population size is NOT constant - at every moment of time there is some - (unpredictable) amount of "new" individuals, which can transit into one - of the states at the next turn, but still no one leaves population - * you want to model transitions of individuals from one state into another - * but you do NOT want to predict amount of "new" individuals because it - does not depends on individuals already present (hence system can not - transit INTO entry state - it can only transit FROM it). - - This model is discussed in more details in the ALGLIB User Guide (see - http://www.alglib.net/dataanalysis/ for more data). + Neural network training using modified Levenberg-Marquardt with exact + Hessian calculation and regularization. Subroutine trains neural network + with restarts from random positions. Algorithm is well suited for small + and medium scale problems (hundreds of weights). INPUT PARAMETERS: - N - problem dimension, N>=2 - EntryState- index of entry state, in 0..N-1 + Network - neural network with initialized geometry + XY - training set + NPoints - training set size + Decay - weight decay constant, >=0.001 + Decay term 'Decay*||Weights||^2' is added to error + function. + If you don't know what Decay to choose, use 0.001. + Restarts - number of restarts from random position, >0. + If you don't know what Restarts to choose, use 2. OUTPUT PARAMETERS: - State - structure stores algorithm state + Network - trained neural network. + Info - return code: + * -9, if internal matrix inverse subroutine failed + * -2, if there is a point with class number + outside of [0..NOut-1]. + * -1, if wrong parameters specified + (NPoints<0, Restarts<1). + * 2, if task has been solved. + Rep - training report -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 10.03.2009 by Bochkanov Sergey *************************************************************************/ - public static void mcpdcreateentry(int n, int entrystate, out mcpdstate s) + public static void mlptrainlm(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep) { - s = new mcpdstate(); - mcpd.mcpdcreateentry(n, entrystate, s.innerobj, null); + info = 0; + rep = new mlpreport(); + mlptrain.mlptrainlm(network.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, null); } - public static void mcpdcreateentry(int n, int entrystate, out mcpdstate s, alglib.xparams _params) + public static void mlptrainlm(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep, alglib.xparams _params) { - s = new mcpdstate(); - mcpd.mcpdcreateentry(n, entrystate, s.innerobj, _params); + info = 0; + rep = new mlpreport(); + mlptrain.mlptrainlm(network.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, _params); } /************************************************************************* - DESCRIPTION: - - This function is a specialized version of MCPDCreate() function, and we - recommend you to read comments for this function for general information - about MCPD solver. - - This function creates MCPD (Markov Chains for Population Data) solver - for "Exit-state" model, i.e. model where transition from X[i] to X[i+1] - is modelled as - X[i+1] = P*X[i] - where - X[i] and X[i+1] are N-dimensional state vectors - P is a N*N transition matrix - and one selected component of X[] is called "exit" state and is treated - in a special way: - system state can transit from any state into "exit" state - system state can not transit from "exit" state into any other state - transition operator discards "exit" state (makes it zero at each turn) - Such conditions basically mean that column of P which corresponds to - "exit" state is zero. Multiplication by such P may decrease sum of vector - components. - - Such models arise when: - * there is some population of individuals - * individuals can have different states - * individuals can transit from one state to another - * population size is NOT constant - individuals can move into "exit" state - and leave population at the next turn, but there are no new individuals - * amount of individuals which leave population can be predicted - * you want to model transitions of individuals from one state into another - (including transitions into the "exit" state) - - This model is discussed in more details in the ALGLIB User Guide (see - http://www.alglib.net/dataanalysis/ for more data). + Neural network training using L-BFGS algorithm with regularization. + Subroutine trains neural network with restarts from random positions. + Algorithm is well suited for problems of any dimensionality (memory + requirements and step complexity are linear by weights number). INPUT PARAMETERS: - N - problem dimension, N>=2 - ExitState- index of exit state, in 0..N-1 + Network - neural network with initialized geometry + XY - training set + NPoints - training set size + Decay - weight decay constant, >=0.001 + Decay term 'Decay*||Weights||^2' is added to error + function. + If you don't know what Decay to choose, use 0.001. + Restarts - number of restarts from random position, >0. + If you don't know what Restarts to choose, use 2. + WStep - stopping criterion. Algorithm stops if step size is + less than WStep. Recommended value - 0.01. Zero step + size means stopping after MaxIts iterations. + MaxIts - stopping criterion. Algorithm stops after MaxIts + iterations (NOT gradient calculations). Zero MaxIts + means stopping when step is sufficiently small. OUTPUT PARAMETERS: - State - structure stores algorithm state + Network - trained neural network. + Info - return code: + * -8, if both WStep=0 and MaxIts=0 + * -2, if there is a point with class number + outside of [0..NOut-1]. + * -1, if wrong parameters specified + (NPoints<0, Restarts<1). + * 2, if task has been solved. + Rep - training report -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 09.12.2007 by Bochkanov Sergey *************************************************************************/ - public static void mcpdcreateexit(int n, int exitstate, out mcpdstate s) + public static void mlptrainlbfgs(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, out int info, out mlpreport rep) { - s = new mcpdstate(); - mcpd.mcpdcreateexit(n, exitstate, s.innerobj, null); + info = 0; + rep = new mlpreport(); + mlptrain.mlptrainlbfgs(network.innerobj, xy, npoints, decay, restarts, wstep, maxits, ref info, rep.innerobj, null); } - public static void mcpdcreateexit(int n, int exitstate, out mcpdstate s, alglib.xparams _params) + public static void mlptrainlbfgs(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, out int info, out mlpreport rep, alglib.xparams _params) { - s = new mcpdstate(); - mcpd.mcpdcreateexit(n, exitstate, s.innerobj, _params); + info = 0; + rep = new mlpreport(); + mlptrain.mlptrainlbfgs(network.innerobj, xy, npoints, decay, restarts, wstep, maxits, ref info, rep.innerobj, _params); } /************************************************************************* - DESCRIPTION: - - This function is a specialized version of MCPDCreate() function, and we - recommend you to read comments for this function for general information - about MCPD solver. - - This function creates MCPD (Markov Chains for Population Data) solver - for "Entry-Exit-states" model, i.e. model where transition from X[i] to - X[i+1] is modelled as - X[i+1] = P*X[i] - where - X[i] and X[i+1] are N-dimensional state vectors - P is a N*N transition matrix - one selected component of X[] is called "entry" state and is treated in a - special way: - system state always transits from "entry" state to some another state - system state can not transit from any state into "entry" state - and another one component of X[] is called "exit" state and is treated in - a special way too: - system state can transit from any state into "exit" state - system state can not transit from "exit" state into any other state - transition operator discards "exit" state (makes it zero at each turn) - Such conditions basically mean that: - row of P which corresponds to "entry" state is zero - column of P which corresponds to "exit" state is zero - Multiplication by such P may decrease sum of vector components. - - Such models arise when: - * there is some population of individuals - * individuals can have different states - * individuals can transit from one state to another - * population size is NOT constant - * at every moment of time there is some (unpredictable) amount of "new" - individuals, which can transit into one of the states at the next turn - * some individuals can move (predictably) into "exit" state and leave - population at the next turn - * you want to model transitions of individuals from one state into another, - including transitions from the "entry" state and into the "exit" state. - * but you do NOT want to predict amount of "new" individuals because it - does not depends on individuals already present (hence system can not - transit INTO entry state - it can only transit FROM it). - - This model is discussed in more details in the ALGLIB User Guide (see - http://www.alglib.net/dataanalysis/ for more data). + Neural network training using early stopping (base algorithm - L-BFGS with + regularization). INPUT PARAMETERS: - N - problem dimension, N>=2 - EntryState- index of entry state, in 0..N-1 - ExitState- index of exit state, in 0..N-1 + Network - neural network with initialized geometry + TrnXY - training set + TrnSize - training set size, TrnSize>0 + ValXY - validation set + ValSize - validation set size, ValSize>0 + Decay - weight decay constant, >=0.001 + Decay term 'Decay*||Weights||^2' is added to error + function. + If you don't know what Decay to choose, use 0.001. + Restarts - number of restarts, either: + * strictly positive number - algorithm make specified + number of restarts from random position. + * -1, in which case algorithm makes exactly one run + from the initial state of the network (no randomization). + If you don't know what Restarts to choose, choose one + one the following: + * -1 (deterministic start) + * +1 (one random restart) + * +5 (moderate amount of random restarts) OUTPUT PARAMETERS: - State - structure stores algorithm state + Network - trained neural network. + Info - return code: + * -2, if there is a point with class number + outside of [0..NOut-1]. + * -1, if wrong parameters specified + (NPoints<0, Restarts<1, ...). + * 2, task has been solved, stopping criterion met - + sufficiently small step size. Not expected (we + use EARLY stopping) but possible and not an + error. + * 6, task has been solved, stopping criterion met - + increasing of validation set error. + Rep - training report + + NOTE: + + Algorithm stops if validation set error increases for a long enough or + step size is small enought (there are task where validation set may + decrease for eternity). In any case solution returned corresponds to the + minimum of validation set error. -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 10.03.2009 by Bochkanov Sergey *************************************************************************/ - public static void mcpdcreateentryexit(int n, int entrystate, int exitstate, out mcpdstate s) + public static void mlptraines(multilayerperceptron network, double[,] trnxy, int trnsize, double[,] valxy, int valsize, double decay, int restarts, out int info, out mlpreport rep) { - s = new mcpdstate(); - mcpd.mcpdcreateentryexit(n, entrystate, exitstate, s.innerobj, null); + info = 0; + rep = new mlpreport(); + mlptrain.mlptraines(network.innerobj, trnxy, trnsize, valxy, valsize, decay, restarts, ref info, rep.innerobj, null); } - public static void mcpdcreateentryexit(int n, int entrystate, int exitstate, out mcpdstate s, alglib.xparams _params) + public static void mlptraines(multilayerperceptron network, double[,] trnxy, int trnsize, double[,] valxy, int valsize, double decay, int restarts, out int info, out mlpreport rep, alglib.xparams _params) { - s = new mcpdstate(); - mcpd.mcpdcreateentryexit(n, entrystate, exitstate, s.innerobj, _params); + info = 0; + rep = new mlpreport(); + mlptrain.mlptraines(network.innerobj, trnxy, trnsize, valxy, valsize, decay, restarts, ref info, rep.innerobj, _params); } /************************************************************************* - This function is used to add a track - sequence of system states at the - different moments of its evolution. + Cross-validation estimate of generalization error. - You may add one or several tracks to the MCPD solver. In case you have - several tracks, they won't overwrite each other. For example, if you pass - two tracks, A1-A2-A3 (system at t=A+1, t=A+2 and t=A+3) and B1-B2-B3, then - solver will try to model transitions from t=A+1 to t=A+2, t=A+2 to t=A+3, - t=B+1 to t=B+2, t=B+2 to t=B+3. But it WONT mix these two tracks - i.e. it - wont try to model transition from t=A+3 to t=B+1. + Base algorithm - L-BFGS. INPUT PARAMETERS: - S - solver - XY - track, array[K,N]: - * I-th row is a state at t=I - * elements of XY must be non-negative (exception will be - thrown on negative elements) - K - number of points in a track - * if given, only leading K rows of XY are used - * if not given, automatically determined from size of XY - - NOTES: + Network - neural network with initialized geometry. Network is + not changed during cross-validation - it is used only + as a representative of its architecture. + XY - training set. + SSize - training set size + Decay - weight decay, same as in MLPTrainLBFGS + Restarts - number of restarts, >0. + restarts are counted for each partition separately, so + total number of restarts will be Restarts*FoldsCount. + WStep - stopping criterion, same as in MLPTrainLBFGS + MaxIts - stopping criterion, same as in MLPTrainLBFGS + FoldsCount - number of folds in k-fold cross-validation, + 2<=FoldsCount<=SSize. + recommended value: 10. - 1. Track may contain either proportional or population data: - * with proportional data all rows of XY must sum to 1.0, i.e. we have - proportions instead of absolute population values - * with population data rows of XY contain population counts and generally - do not sum to 1.0 (although they still must be non-negative) + OUTPUT PARAMETERS: + Info - return code, same as in MLPTrainLBFGS + Rep - report, same as in MLPTrainLM/MLPTrainLBFGS + CVRep - generalization error estimates -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 09.12.2007 by Bochkanov Sergey *************************************************************************/ - public static void mcpdaddtrack(mcpdstate s, double[,] xy, int k) - { - - mcpd.mcpdaddtrack(s.innerobj, xy, k, null); - } - - public static void mcpdaddtrack(mcpdstate s, double[,] xy, int k, alglib.xparams _params) + public static void mlpkfoldcvlbfgs(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, int foldscount, out int info, out mlpreport rep, out mlpcvreport cvrep) { - - mcpd.mcpdaddtrack(s.innerobj, xy, k, _params); + info = 0; + rep = new mlpreport(); + cvrep = new mlpcvreport(); + mlptrain.mlpkfoldcvlbfgs(network.innerobj, xy, npoints, decay, restarts, wstep, maxits, foldscount, ref info, rep.innerobj, cvrep.innerobj, null); } - - public static void mcpdaddtrack(mcpdstate s, double[,] xy) - { - int k; - - - k = ap.rows(xy); - mcpd.mcpdaddtrack(s.innerobj, xy, k, null); - return; - } - - public static void mcpdaddtrack(mcpdstate s, double[,] xy, alglib.xparams _params) + public static void mlpkfoldcvlbfgs(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, int foldscount, out int info, out mlpreport rep, out mlpcvreport cvrep, alglib.xparams _params) { - int k; - - - k = ap.rows(xy); - mcpd.mcpdaddtrack(s.innerobj, xy, k, _params); - - return; + info = 0; + rep = new mlpreport(); + cvrep = new mlpcvreport(); + mlptrain.mlpkfoldcvlbfgs(network.innerobj, xy, npoints, decay, restarts, wstep, maxits, foldscount, ref info, rep.innerobj, cvrep.innerobj, _params); } /************************************************************************* - This function is used to add equality constraints on the elements of the - transition matrix P. - - MCPD solver has four types of constraints which can be placed on P: - * user-specified equality constraints (optional) - * user-specified bound constraints (optional) - * user-specified general linear constraints (optional) - * basic constraints (always present): - * non-negativity: P[i,j]>=0 - * consistency: every column of P sums to 1.0 - - Final constraints which are passed to the underlying optimizer are - calculated as intersection of all present constraints. For example, you - may specify boundary constraint on P[0,0] and equality one: - 0.1<=P[0,0]<=0.9 - P[0,0]=0.5 - Such combination of constraints will be silently reduced to their - intersection, which is P[0,0]=0.5. - - This function can be used to place equality constraints on arbitrary - subset of elements of P. Set of constraints is specified by EC, which may - contain either NAN's or finite numbers from [0,1]. NAN denotes absence of - constraint, finite number denotes equality constraint on specific element - of P. - - You can also use MCPDAddEC() function which allows to ADD equality - constraint for one element of P without changing constraints for other - elements. + Cross-validation estimate of generalization error. - These functions (MCPDSetEC and MCPDAddEC) interact as follows: - * there is internal matrix of equality constraints which is stored in the - MCPD solver - * MCPDSetEC() replaces this matrix by another one (SET) - * MCPDAddEC() modifies one element of this matrix and leaves other ones - unchanged (ADD) - * thus MCPDAddEC() call preserves all modifications done by previous - calls, while MCPDSetEC() completely discards all changes done to the - equality constraints. + Base algorithm - Levenberg-Marquardt. INPUT PARAMETERS: - S - solver - EC - equality constraints, array[N,N]. Elements of EC can be - either NAN's or finite numbers from [0,1]. NAN denotes - absence of constraints, while finite value denotes - equality constraint on the corresponding element of P. - - NOTES: + Network - neural network with initialized geometry. Network is + not changed during cross-validation - it is used only + as a representative of its architecture. + XY - training set. + SSize - training set size + Decay - weight decay, same as in MLPTrainLBFGS + Restarts - number of restarts, >0. + restarts are counted for each partition separately, so + total number of restarts will be Restarts*FoldsCount. + FoldsCount - number of folds in k-fold cross-validation, + 2<=FoldsCount<=SSize. + recommended value: 10. - 1. infinite values of EC will lead to exception being thrown. Values less - than 0.0 or greater than 1.0 will lead to error code being returned after - call to MCPDSolve(). + OUTPUT PARAMETERS: + Info - return code, same as in MLPTrainLBFGS + Rep - report, same as in MLPTrainLM/MLPTrainLBFGS + CVRep - generalization error estimates -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 09.12.2007 by Bochkanov Sergey *************************************************************************/ - public static void mcpdsetec(mcpdstate s, double[,] ec) + public static void mlpkfoldcvlm(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, int foldscount, out int info, out mlpreport rep, out mlpcvreport cvrep) { - - mcpd.mcpdsetec(s.innerobj, ec, null); + info = 0; + rep = new mlpreport(); + cvrep = new mlpcvreport(); + mlptrain.mlpkfoldcvlm(network.innerobj, xy, npoints, decay, restarts, foldscount, ref info, rep.innerobj, cvrep.innerobj, null); } - public static void mcpdsetec(mcpdstate s, double[,] ec, alglib.xparams _params) + public static void mlpkfoldcvlm(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, int foldscount, out int info, out mlpreport rep, out mlpcvreport cvrep, alglib.xparams _params) { - - mcpd.mcpdsetec(s.innerobj, ec, _params); + info = 0; + rep = new mlpreport(); + cvrep = new mlpcvreport(); + mlptrain.mlpkfoldcvlm(network.innerobj, xy, npoints, decay, restarts, foldscount, ref info, rep.innerobj, cvrep.innerobj, _params); } /************************************************************************* - This function is used to add equality constraints on the elements of the - transition matrix P. - - MCPD solver has four types of constraints which can be placed on P: - * user-specified equality constraints (optional) - * user-specified bound constraints (optional) - * user-specified general linear constraints (optional) - * basic constraints (always present): - * non-negativity: P[i,j]>=0 - * consistency: every column of P sums to 1.0 + This function estimates generalization error using cross-validation on the + current dataset with current training settings. - Final constraints which are passed to the underlying optimizer are - calculated as intersection of all present constraints. For example, you - may specify boundary constraint on P[0,0] and equality one: - 0.1<=P[0,0]<=0.9 - P[0,0]=0.5 - Such combination of constraints will be silently reduced to their - intersection, which is P[0,0]=0.5. + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. - This function can be used to ADD equality constraint for one element of P - without changing constraints for other elements. + INPUT PARAMETERS: + S - trainer object + Network - neural network. It must have same number of inputs and + output/classes as was specified during creation of the + trainer object. Network is not changed during cross- + validation and is not trained - it is used only as + representative of its architecture. I.e., we estimate + generalization properties of ARCHITECTURE, not some + specific network. + NRestarts - number of restarts, >=0: + * NRestarts>0 means that for each cross-validation + round specified number of random restarts is + performed, with best network being chosen after + training. + * NRestarts=0 is same as NRestarts=1 + FoldsCount - number of folds in k-fold cross-validation: + * 2<=FoldsCount<=size of dataset + * recommended value: 10. + * values larger than dataset size will be silently + truncated down to dataset size - You can also use MCPDSetEC() function which allows you to specify - arbitrary set of equality constraints in one call. + OUTPUT PARAMETERS: + Rep - structure which contains cross-validation estimates: + * Rep.RelCLSError - fraction of misclassified cases. + * Rep.AvgCE - acerage cross-entropy + * Rep.RMSError - root-mean-square error + * Rep.AvgError - average error + * Rep.AvgRelError - average relative error - These functions (MCPDSetEC and MCPDAddEC) interact as follows: - * there is internal matrix of equality constraints which is stored in the - MCPD solver - * MCPDSetEC() replaces this matrix by another one (SET) - * MCPDAddEC() modifies one element of this matrix and leaves other ones - unchanged (ADD) - * thus MCPDAddEC() call preserves all modifications done by previous - calls, while MCPDSetEC() completely discards all changes done to the - equality constraints. + NOTE: when no dataset was specified with MLPSetDataset/SetSparseDataset(), + or subset with only one point was given, zeros are returned as + estimates. - INPUT PARAMETERS: - S - solver - I - row index of element being constrained - J - column index of element being constrained - C - value (constraint for P[I,J]). Can be either NAN (no - constraint) or finite value from [0,1]. + NOTE: this method performs FoldsCount cross-validation rounds, each one + with NRestarts random starts. Thus, FoldsCount*NRestarts networks + are trained in total. - NOTES: + NOTE: Rep.RelCLSError/Rep.AvgCE are zero on regression problems. - 1. infinite values of C will lead to exception being thrown. Values less - than 0.0 or greater than 1.0 will lead to error code being returned after - call to MCPDSolve(). + NOTE: on classification problems Rep.RMSError/Rep.AvgError/Rep.AvgRelError + contain errors in prediction of posterior probabilities. -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void mcpdaddec(mcpdstate s, int i, int j, double c) + public static void mlpkfoldcv(mlptrainer s, multilayerperceptron network, int nrestarts, int foldscount, out mlpreport rep) { - - mcpd.mcpdaddec(s.innerobj, i, j, c, null); + rep = new mlpreport(); + mlptrain.mlpkfoldcv(s.innerobj, network.innerobj, nrestarts, foldscount, rep.innerobj, null); } - public static void mcpdaddec(mcpdstate s, int i, int j, double c, alglib.xparams _params) + public static void mlpkfoldcv(mlptrainer s, multilayerperceptron network, int nrestarts, int foldscount, out mlpreport rep, alglib.xparams _params) { - - mcpd.mcpdaddec(s.innerobj, i, j, c, _params); + rep = new mlpreport(); + mlptrain.mlpkfoldcv(s.innerobj, network.innerobj, nrestarts, foldscount, rep.innerobj, _params); } /************************************************************************* - This function is used to add bound constraints on the elements of the - transition matrix P. - - MCPD solver has four types of constraints which can be placed on P: - * user-specified equality constraints (optional) - * user-specified bound constraints (optional) - * user-specified general linear constraints (optional) - * basic constraints (always present): - * non-negativity: P[i,j]>=0 - * consistency: every column of P sums to 1.0 - - Final constraints which are passed to the underlying optimizer are - calculated as intersection of all present constraints. For example, you - may specify boundary constraint on P[0,0] and equality one: - 0.1<=P[0,0]<=0.9 - P[0,0]=0.5 - Such combination of constraints will be silently reduced to their - intersection, which is P[0,0]=0.5. - - This function can be used to place bound constraints on arbitrary - subset of elements of P. Set of constraints is specified by BndL/BndU - matrices, which may contain arbitrary combination of finite numbers or - infinities (like -INF=1 + NOut - number of outputs, NOut>=1 + + OUTPUT PARAMETERS: + S - neural network trainer object. + This structure can be used to train any regression + network with NIn inputs and NOut outputs. -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void mcpdsetbc(mcpdstate s, double[,] bndl, double[,] bndu) + public static void mlpcreatetrainer(int nin, int nout, out mlptrainer s) { - - mcpd.mcpdsetbc(s.innerobj, bndl, bndu, null); + s = new mlptrainer(); + mlptrain.mlpcreatetrainer(nin, nout, s.innerobj, null); } - public static void mcpdsetbc(mcpdstate s, double[,] bndl, double[,] bndu, alglib.xparams _params) + public static void mlpcreatetrainer(int nin, int nout, out mlptrainer s, alglib.xparams _params) { - - mcpd.mcpdsetbc(s.innerobj, bndl, bndu, _params); + s = new mlptrainer(); + mlptrain.mlpcreatetrainer(nin, nout, s.innerobj, _params); } /************************************************************************* - This function is used to add bound constraints on the elements of the - transition matrix P. - - MCPD solver has four types of constraints which can be placed on P: - * user-specified equality constraints (optional) - * user-specified bound constraints (optional) - * user-specified general linear constraints (optional) - * basic constraints (always present): - * non-negativity: P[i,j]>=0 - * consistency: every column of P sums to 1.0 - - Final constraints which are passed to the underlying optimizer are - calculated as intersection of all present constraints. For example, you - may specify boundary constraint on P[0,0] and equality one: - 0.1<=P[0,0]<=0.9 - P[0,0]=0.5 - Such combination of constraints will be silently reduced to their - intersection, which is P[0,0]=0.5. - - This function can be used to ADD bound constraint for one element of P - without changing constraints for other elements. - - You can also use MCPDSetBC() function which allows to place bound - constraints on arbitrary subset of elements of P. Set of constraints is - specified by BndL/BndU matrices, which may contain arbitrary combination - of finite numbers or infinities (like -INF=1 + NClasses - number of classes, NClasses>=2 + + OUTPUT PARAMETERS: + S - neural network trainer object. + This structure can be used to train any classification + network with NIn inputs and NOut outputs. -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void mcpdaddbc(mcpdstate s, int i, int j, double bndl, double bndu) + public static void mlpcreatetrainercls(int nin, int nclasses, out mlptrainer s) { - - mcpd.mcpdaddbc(s.innerobj, i, j, bndl, bndu, null); + s = new mlptrainer(); + mlptrain.mlpcreatetrainercls(nin, nclasses, s.innerobj, null); } - public static void mcpdaddbc(mcpdstate s, int i, int j, double bndl, double bndu, alglib.xparams _params) + public static void mlpcreatetrainercls(int nin, int nclasses, out mlptrainer s, alglib.xparams _params) { - - mcpd.mcpdaddbc(s.innerobj, i, j, bndl, bndu, _params); + s = new mlptrainer(); + mlptrain.mlpcreatetrainercls(nin, nclasses, s.innerobj, _params); } /************************************************************************* - This function is used to set linear equality/inequality constraints on the - elements of the transition matrix P. + This function sets "current dataset" of the trainer object to one passed + by user. - This function can be used to set one or several general linear constraints - on the elements of P. Two types of constraints are supported: - * equality constraints - * inequality constraints (both less-or-equal and greater-or-equal) + INPUT PARAMETERS: + S - trainer object + XY - training set, see below for information on the + training set format. This function checks correctness + of the dataset (no NANs/INFs, class numbers are + correct) and throws exception when incorrect dataset + is passed. + NPoints - points count, >=0. - Coefficients of constraints are specified by matrix C (one of the - parameters). One row of C corresponds to one constraint. Because - transition matrix P has N*N elements, we need N*N columns to store all - coefficients (they are stored row by row), and one more column to store - right part - hence C has N*N+1 columns. Constraint kind is stored in the - CT array. + DATASET FORMAT: - Thus, I-th linear constraint is - P[0,0]*C[I,0] + P[0,1]*C[I,1] + .. + P[0,N-1]*C[I,N-1] + - + P[1,0]*C[I,N] + P[1,1]*C[I,N+1] + ... + - + P[N-1,N-1]*C[I,N*N-1] ?=? C[I,N*N] - where ?=? can be either "=" (CT[i]=0), "<=" (CT[i]<0) or ">=" (CT[i]>0). + This function uses two different dataset formats - one for regression + networks, another one for classification networks. - Your constraint may involve only some subset of P (less than N*N elements). - For example it can be something like - P[0,0] + P[0,1] = 0.5 - In this case you still should pass matrix with N*N+1 columns, but all its - elements (except for C[0,0], C[0,1] and C[0,N*N-1]) will be zero. + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs - INPUT PARAMETERS: - S - solver - C - array[K,N*N+1] - coefficients of constraints - (see above for complete description) - CT - array[K] - constraint types - (see above for complete description) - K - number of equality/inequality constraints, K>=0: - * if given, only leading K elements of C/CT are used - * if not given, automatically determined from sizes of C/CT + For classification networks with NIn inputs and NClasses clases following + datasetformat is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void mcpdsetlc(mcpdstate s, double[,] c, int[] ct, int k) - { - - mcpd.mcpdsetlc(s.innerobj, c, ct, k, null); - } - - public static void mcpdsetlc(mcpdstate s, double[,] c, int[] ct, int k, alglib.xparams _params) + public static void mlpsetdataset(mlptrainer s, double[,] xy, int npoints) { - mcpd.mcpdsetlc(s.innerobj, c, ct, k, _params); + mlptrain.mlpsetdataset(s.innerobj, xy, npoints, null); } - - public static void mcpdsetlc(mcpdstate s, double[,] c, int[] ct) - { - int k; - if( (ap.rows(c)!=ap.len(ct))) - throw new alglibexception("Error while calling 'mcpdsetlc': looks like one of arguments has wrong size"); - k = ap.rows(c); - mcpd.mcpdsetlc(s.innerobj, c, ct, k, null); - - return; - } - - public static void mcpdsetlc(mcpdstate s, double[,] c, int[] ct, alglib.xparams _params) + public static void mlpsetdataset(mlptrainer s, double[,] xy, int npoints, alglib.xparams _params) { - int k; - if( (ap.rows(c)!=ap.len(ct))) - throw new alglibexception("Error while calling 'mcpdsetlc': looks like one of arguments has wrong size"); - - k = ap.rows(c); - mcpd.mcpdsetlc(s.innerobj, c, ct, k, _params); - return; + mlptrain.mlpsetdataset(s.innerobj, xy, npoints, _params); } /************************************************************************* - This function allows to tune amount of Tikhonov regularization being - applied to your problem. + This function sets "current dataset" of the trainer object to one passed + by user (sparse matrix is used to store dataset). - By default, regularizing term is equal to r*||P-prior_P||^2, where r is a - small non-zero value, P is transition matrix, prior_P is identity matrix, - ||X||^2 is a sum of squared elements of X. + INPUT PARAMETERS: + S - trainer object + XY - training set, see below for information on the + training set format. This function checks correctness + of the dataset (no NANs/INFs, class numbers are + correct) and throws exception when incorrect dataset + is passed. Any sparse storage format can be used: + Hash-table, CRS... + NPoints - points count, >=0 - This function allows you to change coefficient r. You can also change - prior values with MCPDSetPrior() function. + DATASET FORMAT: - INPUT PARAMETERS: - S - solver - V - regularization coefficient, finite non-negative value. It - is not recommended to specify zero value unless you are - pretty sure that you want it. + This function uses two different dataset formats - one for regression + networks, another one for classification networks. + + For regression networks with NIn inputs and NOut outputs following dataset + format is used: + * dataset is given by NPoints*(NIn+NOut) matrix + * each row corresponds to one example + * first NIn columns are inputs, next NOut columns are outputs + + For classification networks with NIn inputs and NClasses clases following + datasetformat is used: + * dataset is given by NPoints*(NIn+1) matrix + * each row corresponds to one example + * first NIn columns are inputs, last column stores class number (from 0 to + NClasses-1). -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void mcpdsettikhonovregularizer(mcpdstate s, double v) + public static void mlpsetsparsedataset(mlptrainer s, sparsematrix xy, int npoints) { - mcpd.mcpdsettikhonovregularizer(s.innerobj, v, null); + mlptrain.mlpsetsparsedataset(s.innerobj, xy.innerobj, npoints, null); } - public static void mcpdsettikhonovregularizer(mcpdstate s, double v, alglib.xparams _params) + public static void mlpsetsparsedataset(mlptrainer s, sparsematrix xy, int npoints, alglib.xparams _params) { - mcpd.mcpdsettikhonovregularizer(s.innerobj, v, _params); + mlptrain.mlpsetsparsedataset(s.innerobj, xy.innerobj, npoints, _params); } /************************************************************************* - This function allows to set prior values used for regularization of your - problem. - - By default, regularizing term is equal to r*||P-prior_P||^2, where r is a - small non-zero value, P is transition matrix, prior_P is identity matrix, - ||X||^2 is a sum of squared elements of X. - - This function allows you to change prior values prior_P. You can also - change r with MCPDSetTikhonovRegularizer() function. + This function sets weight decay coefficient which is used for training. INPUT PARAMETERS: - S - solver - PP - array[N,N], matrix of prior values: - 1. elements must be real numbers from [0,1] - 2. columns must sum to 1.0. - First property is checked (exception is thrown otherwise), - while second one is not checked/enforced. + S - trainer object + Decay - weight decay coefficient, >=0. Weight decay term + 'Decay*||Weights||^2' is added to error function. If + you don't know what Decay to choose, use 1.0E-3. + Weight decay can be set to zero, in this case network + is trained without weight decay. + + NOTE: by default network uses some small nonzero value for weight decay. -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void mcpdsetprior(mcpdstate s, double[,] pp) + public static void mlpsetdecay(mlptrainer s, double decay) { - mcpd.mcpdsetprior(s.innerobj, pp, null); + mlptrain.mlpsetdecay(s.innerobj, decay, null); } - public static void mcpdsetprior(mcpdstate s, double[,] pp, alglib.xparams _params) + public static void mlpsetdecay(mlptrainer s, double decay, alglib.xparams _params) { - mcpd.mcpdsetprior(s.innerobj, pp, _params); + mlptrain.mlpsetdecay(s.innerobj, decay, _params); } /************************************************************************* - This function is used to change prediction weights + This function sets stopping criteria for the optimizer. - MCPD solver scales prediction errors as follows - Error(P) = ||W*(y-P*x)||^2 - where - x is a system state at time t - y is a system state at time t+1 - P is a transition matrix - W is a diagonal scaling matrix + INPUT PARAMETERS: + S - trainer object + WStep - stopping criterion. Algorithm stops if step size is + less than WStep. Recommended value - 0.01. Zero step + size means stopping after MaxIts iterations. + WStep>=0. + MaxIts - stopping criterion. Algorithm stops after MaxIts + epochs (full passes over entire dataset). Zero MaxIts + means stopping when step is sufficiently small. + MaxIts>=0. - By default, weights are chosen in order to minimize relative prediction - error instead of absolute one. For example, if one component of state is - about 0.5 in magnitude and another one is about 0.05, then algorithm will - make corresponding weights equal to 2.0 and 20.0. + NOTE: by default, WStep=0.005 and MaxIts=0 are used. These values are also + used when MLPSetCond() is called with WStep=0 and MaxIts=0. - INPUT PARAMETERS: - S - solver - PW - array[N], weights: - * must be non-negative values (exception will be thrown otherwise) - * zero values will be replaced by automatically chosen values + NOTE: these stopping criteria are used for all kinds of neural training - + from "conventional" networks to early stopping ensembles. When used + for "conventional" networks, they are used as the only stopping + criteria. When combined with early stopping, they used as ADDITIONAL + stopping criteria which can terminate early stopping algorithm. -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void mcpdsetpredictionweights(mcpdstate s, double[] pw) + public static void mlpsetcond(mlptrainer s, double wstep, int maxits) { - mcpd.mcpdsetpredictionweights(s.innerobj, pw, null); + mlptrain.mlpsetcond(s.innerobj, wstep, maxits, null); } - public static void mcpdsetpredictionweights(mcpdstate s, double[] pw, alglib.xparams _params) + public static void mlpsetcond(mlptrainer s, double wstep, int maxits, alglib.xparams _params) { - mcpd.mcpdsetpredictionweights(s.innerobj, pw, _params); + mlptrain.mlpsetcond(s.innerobj, wstep, maxits, _params); } /************************************************************************* - This function is used to start solution of the MCPD problem. + This function sets training algorithm: batch training using L-BFGS will be + used. - After return from this function, you can use MCPDResults() to get solution - and completion code. + This algorithm: + * the most robust for small-scale problems, but may be too slow for large + scale ones. + * perfoms full pass through the dataset before performing step + * uses conditions specified by MLPSetCond() for stopping + * is default one used by trainer object + + INPUT PARAMETERS: + S - trainer object -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void mcpdsolve(mcpdstate s) + public static void mlpsetalgobatch(mlptrainer s) { - mcpd.mcpdsolve(s.innerobj, null); + mlptrain.mlpsetalgobatch(s.innerobj, null); } - public static void mcpdsolve(mcpdstate s, alglib.xparams _params) + public static void mlpsetalgobatch(mlptrainer s, alglib.xparams _params) { - mcpd.mcpdsolve(s.innerobj, _params); + mlptrain.mlpsetalgobatch(s.innerobj, _params); } /************************************************************************* - MCPD results + This function trains neural network passed to this function, using current + dataset (one which was passed to MLPSetDataset() or MLPSetSparseDataset()) + and current training settings. Training from NRestarts random starting + positions is performed, best network is chosen. + + Training is performed using current training algorithm. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - State - algorithm state + S - trainer object + Network - neural network. It must have same number of inputs and + output/classes as was specified during creation of the + trainer object. + NRestarts - number of restarts, >=0: + * NRestarts>0 means that specified number of random + restarts are performed, best network is chosen after + training + * NRestarts=0 means that current state of the network + is used for training. OUTPUT PARAMETERS: - P - array[N,N], transition matrix - Rep - optimization report. You should check Rep.TerminationType - in order to distinguish successful termination from - unsuccessful one. Speaking short, positive values denote - success, negative ones are failures. - More information about fields of this structure can be - found in the comments on MCPDReport datatype. + Network - trained network + + NOTE: when no dataset was specified with MLPSetDataset/SetSparseDataset(), + network is filled by zero values. Same behavior for functions + MLPStartTraining and MLPContinueTraining. + NOTE: this method uses sum-of-squares error function for training. -- ALGLIB -- - Copyright 23.05.2010 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static void mcpdresults(mcpdstate s, out double[,] p, out mcpdreport rep) + public static void mlptrainnetwork(mlptrainer s, multilayerperceptron network, int nrestarts, out mlpreport rep) { - p = new double[0,0]; - rep = new mcpdreport(); - mcpd.mcpdresults(s.innerobj, ref p, rep.innerobj, null); + rep = new mlpreport(); + mlptrain.mlptrainnetwork(s.innerobj, network.innerobj, nrestarts, rep.innerobj, null); } - public static void mcpdresults(mcpdstate s, out double[,] p, out mcpdreport rep, alglib.xparams _params) + public static void mlptrainnetwork(mlptrainer s, multilayerperceptron network, int nrestarts, out mlpreport rep, alglib.xparams _params) { - p = new double[0,0]; - rep = new mcpdreport(); - mcpd.mcpdresults(s.innerobj, ref p, rep.innerobj, _params); + rep = new mlpreport(); + mlptrain.mlptrainnetwork(s.innerobj, network.innerobj, nrestarts, rep.innerobj, _params); } - -} -public partial class alglib -{ - - + /************************************************************************* + IMPORTANT: this is an "expert" version of the MLPTrain() function. We do + not recommend you to use it unless you are pretty sure that you + need ability to monitor training progress. - *************************************************************************/ - public class logitmodel : alglibobject - { - // - // Public declarations - // - - public logitmodel() - { - _innerobj = new logit.logitmodel(); - } - - public override alglib.alglibobject make_copy() - { - return new logitmodel((logit.logitmodel)_innerobj.make_copy()); - } - - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private logit.logitmodel _innerobj; - public logit.logitmodel innerobj { get { return _innerobj; } } - public logitmodel(logit.logitmodel obj) - { - _innerobj = obj; - } - } + This function performs step-by-step training of the neural network. Here + "step-by-step" means that training starts with MLPStartTraining() call, + and then user subsequently calls MLPContinueTraining() to perform one more + iteration of the training. + After call to this function trainer object remembers network and is ready + to train it. However, no training is performed until first call to + MLPContinueTraining() function. Subsequent calls to MLPContinueTraining() + will advance training progress one iteration further. - /************************************************************************* - MNLReport structure contains information about training process: - * NGrad - number of gradient calculations - * NHess - number of Hessian calculations - *************************************************************************/ - public class mnlreport : alglibobject - { - // - // Public declarations - // - public int ngrad { get { return _innerobj.ngrad; } set { _innerobj.ngrad = value; } } - public int nhess { get { return _innerobj.nhess; } set { _innerobj.nhess = value; } } - - public mnlreport() - { - _innerobj = new logit.mnlreport(); - } - - public override alglib.alglibobject make_copy() - { - return new mnlreport((logit.mnlreport)_innerobj.make_copy()); - } - - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private logit.mnlreport _innerobj; - public logit.mnlreport innerobj { get { return _innerobj; } } - public mnlreport(logit.mnlreport obj) - { - _innerobj = obj; - } - } - - /************************************************************************* - This subroutine trains logit model. + EXAMPLE: + > + > ...initialize network and trainer object.... + > + > MLPStartTraining(Trainer, Network, True) + > while MLPContinueTraining(Trainer, Network) do + > ...visualize training progress... + > INPUT PARAMETERS: - XY - training set, array[0..NPoints-1,0..NVars] - First NVars columns store values of independent - variables, next column stores number of class (from 0 - to NClasses-1) which dataset element belongs to. Fractional - values are rounded to nearest integer. - NPoints - training set size, NPoints>=1 - NVars - number of independent variables, NVars>=1 - NClasses - number of classes, NClasses>=2 + S - trainer object + Network - neural network. It must have same number of inputs and + output/classes as was specified during creation of the + trainer object. + RandomStart - randomize network before training or not: + * True means that network is randomized and its + initial state (one which was passed to the trainer + object) is lost. + * False means that training is started from the + current state of the network OUTPUT PARAMETERS: - Info - return code: - * -2, if there is a point with class number - outside of [0..NClasses-1]. - * -1, if incorrect parameters was passed - (NPoints + > [initialize network and trainer object] + > + > MLPStartTraining(Trainer, Network, True) + > while MLPContinueTraining(Trainer, Network) do + > [visualize training progress] + > INPUT PARAMETERS: - LM - logit model in ALGLIB format + S - trainer object + Network - neural network structure, which is used to store + current state of the training process. OUTPUT PARAMETERS: - V - coefficients, array[0..NClasses-2,0..NVars] - NVars - number of independent variables - NClasses - number of classes - - -- ALGLIB -- - Copyright 10.09.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mnlunpack(logitmodel lm, out double[,] a, out int nvars, out int nclasses) - { - a = new double[0,0]; - nvars = 0; - nclasses = 0; - logit.mnlunpack(lm.innerobj, ref a, ref nvars, ref nclasses, null); - } - - public static void mnlunpack(logitmodel lm, out double[,] a, out int nvars, out int nclasses, alglib.xparams _params) - { - a = new double[0,0]; - nvars = 0; - nclasses = 0; - logit.mnlunpack(lm.innerobj, ref a, ref nvars, ref nclasses, _params); - } - - /************************************************************************* - "Packs" coefficients and creates logit model in ALGLIB format (MNLUnpack - reversed). + Network - weights of the neural network are rewritten by the + current approximation. - INPUT PARAMETERS: - A - model (see MNLUnpack) - NVars - number of independent variables - NClasses - number of classes + NOTE: this method uses sum-of-squares error function for training. - OUTPUT PARAMETERS: - LM - logit model. + NOTE: it is expected that trainer object settings are NOT changed during + step-by-step training, i.e. no one changes stopping criteria or + training set during training. It is possible and there is no defense + against such actions, but algorithm behavior in such cases is + undefined and can be unpredictable. - -- ALGLIB -- - Copyright 10.09.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mnlpack(double[,] a, int nvars, int nclasses, out logitmodel lm) - { - lm = new logitmodel(); - logit.mnlpack(a, nvars, nclasses, lm.innerobj, null); - } - - public static void mnlpack(double[,] a, int nvars, int nclasses, out logitmodel lm, alglib.xparams _params) - { - lm = new logitmodel(); - logit.mnlpack(a, nvars, nclasses, lm.innerobj, _params); - } - - /************************************************************************* - Average cross-entropy (in bits per element) on the test set + NOTE: It is expected that Network is the same one which was passed to + MLPStartTraining() function. However, THIS function checks only + following: + * that number of network inputs is consistent with trainer object + settings + * that number of network outputs/classes is consistent with trainer + object settings + * that number of network weights is the same as number of weights in + the network passed to MLPStartTraining() function + Exception is thrown when these conditions are violated. - INPUT PARAMETERS: - LM - logit model - XY - test set - NPoints - test set size + It is also expected that you do not change state of the network on + your own - the only party who has right to change network during its + training is a trainer object. Any attempt to interfere with trainer + may lead to unpredictable results. - RESULT: - CrossEntropy/(NPoints*ln(2)). -- ALGLIB -- - Copyright 10.09.2008 by Bochkanov Sergey + Copyright 23.07.2012 by Bochkanov Sergey *************************************************************************/ - public static double mnlavgce(logitmodel lm, double[,] xy, int npoints) + public static bool mlpcontinuetraining(mlptrainer s, multilayerperceptron network) { - return logit.mnlavgce(lm.innerobj, xy, npoints, null); + return mlptrain.mlpcontinuetraining(s.innerobj, network.innerobj, null); } - public static double mnlavgce(logitmodel lm, double[,] xy, int npoints, alglib.xparams _params) + public static bool mlpcontinuetraining(mlptrainer s, multilayerperceptron network, alglib.xparams _params) { - return logit.mnlavgce(lm.innerobj, xy, npoints, _params); + return mlptrain.mlpcontinuetraining(s.innerobj, network.innerobj, _params); } /************************************************************************* - Relative classification error on the test set + Training neural networks ensemble using bootstrap aggregating (bagging). + Modified Levenberg-Marquardt algorithm is used as base training method. INPUT PARAMETERS: - LM - logit model - XY - test set - NPoints - test set size + Ensemble - model with initialized geometry + XY - training set + NPoints - training set size + Decay - weight decay coefficient, >=0.001 + Restarts - restarts, >0. - RESULT: - percent of incorrectly classified cases. + OUTPUT PARAMETERS: + Ensemble - trained model + Info - return code: + * -2, if there is a point with class number + outside of [0..NClasses-1]. + * -1, if incorrect parameters was passed + (NPoints<0, Restarts<1). + * 2, if task has been solved. + Rep - training report. + OOBErrors - out-of-bag generalization error estimate -- ALGLIB -- - Copyright 10.09.2008 by Bochkanov Sergey + Copyright 17.02.2009 by Bochkanov Sergey *************************************************************************/ - public static double mnlrelclserror(logitmodel lm, double[,] xy, int npoints) + public static void mlpebagginglm(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep, out mlpcvreport ooberrors) { - - return logit.mnlrelclserror(lm.innerobj, xy, npoints, null); + info = 0; + rep = new mlpreport(); + ooberrors = new mlpcvreport(); + mlptrain.mlpebagginglm(ensemble.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, ooberrors.innerobj, null); } - public static double mnlrelclserror(logitmodel lm, double[,] xy, int npoints, alglib.xparams _params) + public static void mlpebagginglm(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep, out mlpcvreport ooberrors, alglib.xparams _params) { - - return logit.mnlrelclserror(lm.innerobj, xy, npoints, _params); + info = 0; + rep = new mlpreport(); + ooberrors = new mlpcvreport(); + mlptrain.mlpebagginglm(ensemble.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, ooberrors.innerobj, _params); } /************************************************************************* - RMS error on the test set + Training neural networks ensemble using bootstrap aggregating (bagging). + L-BFGS algorithm is used as base training method. INPUT PARAMETERS: - LM - logit model - XY - test set - NPoints - test set size + Ensemble - model with initialized geometry + XY - training set + NPoints - training set size + Decay - weight decay coefficient, >=0.001 + Restarts - restarts, >0. + WStep - stopping criterion, same as in MLPTrainLBFGS + MaxIts - stopping criterion, same as in MLPTrainLBFGS - RESULT: - root mean square error (error when estimating posterior probabilities). + OUTPUT PARAMETERS: + Ensemble - trained model + Info - return code: + * -8, if both WStep=0 and MaxIts=0 + * -2, if there is a point with class number + outside of [0..NClasses-1]. + * -1, if incorrect parameters was passed + (NPoints<0, Restarts<1). + * 2, if task has been solved. + Rep - training report. + OOBErrors - out-of-bag generalization error estimate -- ALGLIB -- - Copyright 30.08.2008 by Bochkanov Sergey + Copyright 17.02.2009 by Bochkanov Sergey *************************************************************************/ - public static double mnlrmserror(logitmodel lm, double[,] xy, int npoints) + public static void mlpebagginglbfgs(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, out int info, out mlpreport rep, out mlpcvreport ooberrors) { - - return logit.mnlrmserror(lm.innerobj, xy, npoints, null); + info = 0; + rep = new mlpreport(); + ooberrors = new mlpcvreport(); + mlptrain.mlpebagginglbfgs(ensemble.innerobj, xy, npoints, decay, restarts, wstep, maxits, ref info, rep.innerobj, ooberrors.innerobj, null); } - public static double mnlrmserror(logitmodel lm, double[,] xy, int npoints, alglib.xparams _params) + public static void mlpebagginglbfgs(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, out int info, out mlpreport rep, out mlpcvreport ooberrors, alglib.xparams _params) { - - return logit.mnlrmserror(lm.innerobj, xy, npoints, _params); + info = 0; + rep = new mlpreport(); + ooberrors = new mlpcvreport(); + mlptrain.mlpebagginglbfgs(ensemble.innerobj, xy, npoints, decay, restarts, wstep, maxits, ref info, rep.innerobj, ooberrors.innerobj, _params); } /************************************************************************* - Average error on the test set + Training neural networks ensemble using early stopping. INPUT PARAMETERS: - LM - logit model - XY - test set - NPoints - test set size + Ensemble - model with initialized geometry + XY - training set + NPoints - training set size + Decay - weight decay coefficient, >=0.001 + Restarts - restarts, >0. - RESULT: - average error (error when estimating posterior probabilities). + OUTPUT PARAMETERS: + Ensemble - trained model + Info - return code: + * -2, if there is a point with class number + outside of [0..NClasses-1]. + * -1, if incorrect parameters was passed + (NPoints<0, Restarts<1). + * 6, if task has been solved. + Rep - training report. + OOBErrors - out-of-bag generalization error estimate -- ALGLIB -- - Copyright 30.08.2008 by Bochkanov Sergey + Copyright 10.03.2009 by Bochkanov Sergey *************************************************************************/ - public static double mnlavgerror(logitmodel lm, double[,] xy, int npoints) + public static void mlpetraines(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep) { - - return logit.mnlavgerror(lm.innerobj, xy, npoints, null); + info = 0; + rep = new mlpreport(); + mlptrain.mlpetraines(ensemble.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, null); } - public static double mnlavgerror(logitmodel lm, double[,] xy, int npoints, alglib.xparams _params) + public static void mlpetraines(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep, alglib.xparams _params) { - - return logit.mnlavgerror(lm.innerobj, xy, npoints, _params); + info = 0; + rep = new mlpreport(); + mlptrain.mlpetraines(ensemble.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, _params); } /************************************************************************* - Average relative error on the test set + 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). + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - LM - logit model - XY - test set - NPoints - test set size + S - trainer object; + Ensemble - neural network ensemble. It must have same number of + inputs and outputs/classes as was specified during + creation of the trainer object. + NRestarts - number of restarts, >=0: + * NRestarts>0 means that specified number of random + restarts are performed during each ES round; + * NRestarts=0 is silently replaced by 1. - RESULT: - average relative error (error when estimating posterior probabilities). + OUTPUT PARAMETERS: + Ensemble - trained ensemble; + Rep - it contains all type of errors. - -- ALGLIB -- - Copyright 30.08.2008 by Bochkanov Sergey - *************************************************************************/ - public static double mnlavgrelerror(logitmodel lm, double[,] xy, int ssize) - { - - return logit.mnlavgrelerror(lm.innerobj, xy, ssize, null); - } - - public static double mnlavgrelerror(logitmodel lm, double[,] xy, int ssize, alglib.xparams _params) - { - - return logit.mnlavgrelerror(lm.innerobj, xy, ssize, _params); - } - - /************************************************************************* - Classification error on test set = MNLRelClsError*NPoints + NOTE: this training method uses BOTH early stopping and weight decay! So, + you should select weight decay before starting training just as you + select it before training "conventional" networks. + + NOTE: when no dataset was specified with MLPSetDataset/SetSparseDataset(), + or single-point dataset was passed, ensemble is filled by zero + values. + + NOTE: this method uses sum-of-squares error function for training. -- ALGLIB -- - Copyright 10.09.2008 by Bochkanov Sergey + Copyright 22.08.2012 by Bochkanov Sergey *************************************************************************/ - public static int mnlclserror(logitmodel lm, double[,] xy, int npoints) + public static void mlptrainensemblees(mlptrainer s, mlpensemble ensemble, int nrestarts, out mlpreport rep) { - - return logit.mnlclserror(lm.innerobj, xy, npoints, null); + rep = new mlpreport(); + mlptrain.mlptrainensemblees(s.innerobj, ensemble.innerobj, nrestarts, rep.innerobj, null); } - public static int mnlclserror(logitmodel lm, double[,] xy, int npoints, alglib.xparams _params) + public static void mlptrainensemblees(mlptrainer s, mlpensemble ensemble, int nrestarts, out mlpreport rep, alglib.xparams _params) { - - return logit.mnlclserror(lm.innerobj, xy, npoints, _params); + rep = new mlpreport(); + mlptrain.mlptrainensemblees(s.innerobj, ensemble.innerobj, nrestarts, rep.innerobj, _params); } } @@ -9503,35 +10291,36 @@ public partial class alglib /************************************************************************* - Buffer object which is used to perform various requests (usually model - inference) in the multithreaded mode (multiple threads working with same - KNN object). + This structure is a MCPD (Markov Chains for Population Data) solver. - This object should be created with KNNCreateBuffer(). + You should use ALGLIB functions in order to work with this object. + + -- ALGLIB -- + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public class knnbuffer : alglibobject + public class mcpdstate : alglibobject { // // Public declarations // - public knnbuffer() + public mcpdstate() { - _innerobj = new knn.knnbuffer(); + _innerobj = new mcpd.mcpdstate(); } public override alglib.alglibobject make_copy() { - return new knnbuffer((knn.knnbuffer)_innerobj.make_copy()); + return new mcpdstate((mcpd.mcpdstate)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // - private knn.knnbuffer _innerobj; - public knn.knnbuffer innerobj { get { return _innerobj; } } - public knnbuffer(knn.knnbuffer obj) + private mcpd.mcpdstate _innerobj; + public mcpd.mcpdstate innerobj { get { return _innerobj; } } + public mcpdstate(mcpd.mcpdstate obj) { _innerobj = obj; } @@ -9539,6993 +10328,3594 @@ public knnbuffer(knn.knnbuffer obj) /************************************************************************* - A KNN builder object; this object encapsulates dataset and all related - settings, it is used to create an actual instance of KNN model. + This structure is a MCPD training report: + InnerIterationsCount - number of inner iterations of the + underlying optimization algorithm + OuterIterationsCount - number of outer iterations of the + underlying optimization algorithm + NFEV - number of merit function evaluations + TerminationType - termination type + (same as for MinBLEIC optimizer, positive + values denote success, negative ones - + failure) + + -- ALGLIB -- + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public class knnbuilder : alglibobject + public class mcpdreport : alglibobject { // // Public declarations // + public int inneriterationscount { get { return _innerobj.inneriterationscount; } set { _innerobj.inneriterationscount = value; } } + public int outeriterationscount { get { return _innerobj.outeriterationscount; } set { _innerobj.outeriterationscount = value; } } + public int nfev { get { return _innerobj.nfev; } set { _innerobj.nfev = value; } } + public int terminationtype { get { return _innerobj.terminationtype; } set { _innerobj.terminationtype = value; } } - public knnbuilder() + public mcpdreport() { - _innerobj = new knn.knnbuilder(); + _innerobj = new mcpd.mcpdreport(); } public override alglib.alglibobject make_copy() { - return new knnbuilder((knn.knnbuilder)_innerobj.make_copy()); + return new mcpdreport((mcpd.mcpdreport)_innerobj.make_copy()); } // // Although some of declarations below are public, you should not use them // They are intended for internal use only // - private knn.knnbuilder _innerobj; - public knn.knnbuilder innerobj { get { return _innerobj; } } - public knnbuilder(knn.knnbuilder obj) + private mcpd.mcpdreport _innerobj; + public mcpd.mcpdreport innerobj { get { return _innerobj; } } + public mcpdreport(mcpd.mcpdreport obj) { _innerobj = obj; } } + + /************************************************************************* + DESCRIPTION: + This function creates MCPD (Markov Chains for Population Data) solver. - /************************************************************************* - KNN model, can be used for classification or regression - *************************************************************************/ - public class knnmodel : alglibobject - { - // - // Public declarations - // - - public knnmodel() - { - _innerobj = new knn.knnmodel(); - } - - public override alglib.alglibobject make_copy() - { - return new knnmodel((knn.knnmodel)_innerobj.make_copy()); - } - - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private knn.knnmodel _innerobj; - public knn.knnmodel innerobj { get { return _innerobj; } } - public knnmodel(knn.knnmodel obj) - { - _innerobj = obj; - } - } + This solver can be used to find transition matrix P for N-dimensional + prediction problem where transition from X[i] to X[i+1] is modelled as + X[i+1] = P*X[i] + where X[i] and X[i+1] are N-dimensional population vectors (components of + each X are non-negative), and P is a N*N transition matrix (elements of P + are non-negative, each column sums to 1.0). + Such models arise when when: + * there is some population of individuals + * individuals can have different states + * individuals can transit from one state to another + * population size is constant, i.e. there is no new individuals and no one + leaves population + * you want to model transitions of individuals from one state into another - /************************************************************************* - KNN training report. + USAGE: - Following fields store training set errors: - * relclserror - fraction of misclassified cases, [0,1] - * avgce - average cross-entropy in bits per symbol - * rmserror - root-mean-square error - * avgerror - average error - * avgrelerror - average relative error + Here we give very brief outline of the MCPD. We strongly recommend you to + read examples in the ALGLIB Reference Manual and to read ALGLIB User Guide + on data analysis which is available at http://www.alglib.net/dataanalysis/ - For classification problems: - * RMS, AVG and AVGREL errors are calculated for posterior probabilities + 1. User initializes algorithm state with MCPDCreate() call - For regression problems: - * RELCLS and AVGCE errors are zero - *************************************************************************/ - public class knnreport : alglibobject - { - // - // Public declarations - // - public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } - public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } - public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } - public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } - public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } - - public knnreport() - { - _innerobj = new knn.knnreport(); - } - - public override alglib.alglibobject make_copy() - { - return new knnreport((knn.knnreport)_innerobj.make_copy()); - } - - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private knn.knnreport _innerobj; - public knn.knnreport innerobj { get { return _innerobj; } } - public knnreport(knn.knnreport obj) - { - _innerobj = obj; - } - } + 2. User adds one or more tracks - sequences of states which describe + evolution of a system being modelled from different starting conditions + 3. User may add optional boundary, equality and/or linear constraints on + the coefficients of P by calling one of the following functions: + * MCPDSetEC() to set equality constraints + * MCPDSetBC() to set bound constraints + * MCPDSetLC() to set linear constraints - /************************************************************************* - This function serializes data structure to string/stream. - - Important properties of s_out: - * it contains alphanumeric characters, dots, underscores, minus signs - * these symbols are grouped into words, which are separated by spaces - and Windows-style (CR+LF) newlines - * although serializer uses spaces and CR+LF as separators, you can - replace any separator character by arbitrary combination of spaces, - tabs, Windows or Unix newlines. It allows flexible reformatting of - the string in case you want to include it into a text or XML file. - But you should not insert separators into the middle of the "words" - nor should you change the case of letters. - * s_out can be freely moved between 32-bit and 64-bit systems, little - and big endian machines, and so on. You can serialize structure on - 32-bit machine and unserialize it on 64-bit one (or vice versa), or - serialize it on SPARC and unserialize on x86. You can also - serialize it in C++ version of ALGLIB and unserialize it in C# one, - and vice versa. - *************************************************************************/ - public static void knnserialize(knnmodel obj, out string s_out) - { - alglib.serializer s = new alglib.serializer(); - s.alloc_start(); - knn.knnalloc(s, obj.innerobj, null); - s.sstart_str(); - knn.knnserialize(s, obj.innerobj, null); - s.stop(); - s_out = s.get_string(); - } + 4. Optionally, user may set custom weights for prediction errors (by + default, algorithm assigns non-equal, automatically chosen weights for + errors in the prediction of different components of X). It can be done + with a call of MCPDSetPredictionWeights() function. + 5. User calls MCPDSolve() function which takes algorithm state and + pointer (delegate, etc.) to callback function which calculates F/G. - /************************************************************************* - This function serializes data structure to string/stream. - - Important properties of s_out: - * it contains alphanumeric characters, dots, underscores, minus signs - * these symbols are grouped into words, which are separated by spaces - and Windows-style (CR+LF) newlines - * although serializer uses spaces and CR+LF as separators, you can - replace any separator character by arbitrary combination of spaces, - tabs, Windows or Unix newlines. It allows flexible reformatting of - the string in case you want to include it into a text or XML file. - But you should not insert separators into the middle of the "words" - nor should you change the case of letters. - * s_out can be freely moved between 32-bit and 64-bit systems, little - and big endian machines, and so on. You can serialize structure on - 32-bit machine and unserialize it on 64-bit one (or vice versa), or - serialize it on SPARC and unserialize on x86. You can also - serialize it in C++ version of ALGLIB and unserialize it in C# one, - and vice versa. - *************************************************************************/ - public static void knnserialize(knnmodel obj, System.IO.Stream stream_out) - { - alglib.serializer s = new alglib.serializer(); - s.alloc_start(); - knn.knnalloc(s, obj.innerobj, null); - s.sstart_stream(stream_out); - knn.knnserialize(s, obj.innerobj, null); - s.stop(); - } + 6. User calls MCPDResults() to get solution + INPUT PARAMETERS: + N - problem dimension, N>=1 - /************************************************************************* - This function unserializes data structure from string/stream. + OUTPUT PARAMETERS: + State - structure stores algorithm state + + -- ALGLIB -- + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static void knnunserialize(string s_in, out knnmodel obj) + public static void mcpdcreate(int n, out mcpdstate s) { - alglib.serializer s = new alglib.serializer(); - obj = new knnmodel(); - s.ustart_str(s_in); - knn.knnunserialize(s, obj.innerobj, null); - s.stop(); + s = new mcpdstate(); + mcpd.mcpdcreate(n, s.innerobj, null); } - - - /************************************************************************* - This function unserializes data structure from string/stream. - *************************************************************************/ - public static void knnunserialize(System.IO.Stream stream_in, out knnmodel obj) + public static void mcpdcreate(int n, out mcpdstate s, alglib.xparams _params) { - alglib.serializer s = new alglib.serializer(); - obj = new knnmodel(); - s.ustart_stream(stream_in); - knn.knnunserialize(s, obj.innerobj, null); - s.stop(); + s = new mcpdstate(); + mcpd.mcpdcreate(n, s.innerobj, _params); } /************************************************************************* - This function creates buffer structure which can be used to perform - parallel KNN requests. + DESCRIPTION: - KNN subpackage provides two sets of computing functions - ones which use - internal buffer of KNN model (these functions are single-threaded because - they use same buffer, which can not shared between threads), and ones - which use external buffer. + This function is a specialized version of MCPDCreate() function, and we + recommend you to read comments for this function for general information + about MCPD solver. - This function is used to initialize external buffer. + This function creates MCPD (Markov Chains for Population Data) solver + for "Entry-state" model, i.e. model where transition from X[i] to X[i+1] + is modelled as + X[i+1] = P*X[i] + where + X[i] and X[i+1] are N-dimensional state vectors + P is a N*N transition matrix + and one selected component of X[] is called "entry" state and is treated + in a special way: + system state always transits from "entry" state to some another state + system state can not transit from any state into "entry" state + Such conditions basically mean that row of P which corresponds to "entry" + state is zero. - INPUT PARAMETERS - Model - KNN model which is associated with newly created buffer + Such models arise when: + * there is some population of individuals + * individuals can have different states + * individuals can transit from one state to another + * population size is NOT constant - at every moment of time there is some + (unpredictable) amount of "new" individuals, which can transit into one + of the states at the next turn, but still no one leaves population + * you want to model transitions of individuals from one state into another + * but you do NOT want to predict amount of "new" individuals because it + does not depends on individuals already present (hence system can not + transit INTO entry state - it can only transit FROM it). - OUTPUT PARAMETERS - Buf - external buffer. + This model is discussed in more details in the ALGLIB User Guide (see + http://www.alglib.net/dataanalysis/ for more data). + INPUT PARAMETERS: + N - problem dimension, N>=2 + EntryState- index of entry state, in 0..N-1 - IMPORTANT: buffer object should be used only with model which was used to - initialize buffer. Any attempt to use buffer with different - object is dangerous - you may get integrity check failure - (exception) because sizes of internal arrays do not fit to - dimensions of the model structure. + OUTPUT PARAMETERS: + State - structure stores algorithm state -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static void knncreatebuffer(knnmodel model, out knnbuffer buf) + public static void mcpdcreateentry(int n, int entrystate, out mcpdstate s) { - buf = new knnbuffer(); - knn.knncreatebuffer(model.innerobj, buf.innerobj, null); + s = new mcpdstate(); + mcpd.mcpdcreateentry(n, entrystate, s.innerobj, null); } - public static void knncreatebuffer(knnmodel model, out knnbuffer buf, alglib.xparams _params) + public static void mcpdcreateentry(int n, int entrystate, out mcpdstate s, alglib.xparams _params) { - buf = new knnbuffer(); - knn.knncreatebuffer(model.innerobj, buf.innerobj, _params); + s = new mcpdstate(); + mcpd.mcpdcreateentry(n, entrystate, s.innerobj, _params); } /************************************************************************* - This subroutine creates KNNBuilder object which is used to train KNN models. + DESCRIPTION: - By default, new builder stores empty dataset and some reasonable default - settings. At the very least, you should specify dataset prior to building - KNN model. You can also tweak settings of the model construction algorithm - (recommended, although default settings should work well). + This function is a specialized version of MCPDCreate() function, and we + recommend you to read comments for this function for general information + about MCPD solver. - Following actions are mandatory: - * calling knnbuildersetdataset() to specify dataset - * calling knnbuilderbuildknnmodel() to build KNN model using current - dataset and default settings + This function creates MCPD (Markov Chains for Population Data) solver + for "Exit-state" model, i.e. model where transition from X[i] to X[i+1] + is modelled as + X[i+1] = P*X[i] + where + X[i] and X[i+1] are N-dimensional state vectors + P is a N*N transition matrix + and one selected component of X[] is called "exit" state and is treated + in a special way: + system state can transit from any state into "exit" state + system state can not transit from "exit" state into any other state + transition operator discards "exit" state (makes it zero at each turn) + Such conditions basically mean that column of P which corresponds to + "exit" state is zero. Multiplication by such P may decrease sum of vector + components. - Additionally, you may call: - * knnbuildersetnorm() to change norm being used + Such models arise when: + * there is some population of individuals + * individuals can have different states + * individuals can transit from one state to another + * population size is NOT constant - individuals can move into "exit" state + and leave population at the next turn, but there are no new individuals + * amount of individuals which leave population can be predicted + * you want to model transitions of individuals from one state into another + (including transitions into the "exit" state) + + This model is discussed in more details in the ALGLIB User Guide (see + http://www.alglib.net/dataanalysis/ for more data). INPUT PARAMETERS: - none + N - problem dimension, N>=2 + ExitState- index of exit state, in 0..N-1 OUTPUT PARAMETERS: - S - KNN builder + State - structure stores algorithm state -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static void knnbuildercreate(out knnbuilder s) + public static void mcpdcreateexit(int n, int exitstate, out mcpdstate s) { - s = new knnbuilder(); - knn.knnbuildercreate(s.innerobj, null); + s = new mcpdstate(); + mcpd.mcpdcreateexit(n, exitstate, s.innerobj, null); } - public static void knnbuildercreate(out knnbuilder s, alglib.xparams _params) + public static void mcpdcreateexit(int n, int exitstate, out mcpdstate s, alglib.xparams _params) { - s = new knnbuilder(); - knn.knnbuildercreate(s.innerobj, _params); + s = new mcpdstate(); + mcpd.mcpdcreateexit(n, exitstate, s.innerobj, _params); } /************************************************************************* - Specifies regression problem (one or more continuous output variables are - predicted). There also exists "classification" version of this function. + DESCRIPTION: - This subroutine adds dense dataset to the internal storage of the builder - object. Specifying your dataset in the dense format means that the dense - version of the KNN construction algorithm will be invoked. + This function is a specialized version of MCPDCreate() function, and we + recommend you to read comments for this function for general information + about MCPD solver. + + This function creates MCPD (Markov Chains for Population Data) solver + for "Entry-Exit-states" model, i.e. model where transition from X[i] to + X[i+1] is modelled as + X[i+1] = P*X[i] + where + X[i] and X[i+1] are N-dimensional state vectors + P is a N*N transition matrix + one selected component of X[] is called "entry" state and is treated in a + special way: + system state always transits from "entry" state to some another state + system state can not transit from any state into "entry" state + and another one component of X[] is called "exit" state and is treated in + a special way too: + system state can transit from any state into "exit" state + system state can not transit from "exit" state into any other state + transition operator discards "exit" state (makes it zero at each turn) + Such conditions basically mean that: + row of P which corresponds to "entry" state is zero + column of P which corresponds to "exit" state is zero + Multiplication by such P may decrease sum of vector components. + + Such models arise when: + * there is some population of individuals + * individuals can have different states + * individuals can transit from one state to another + * population size is NOT constant + * at every moment of time there is some (unpredictable) amount of "new" + individuals, which can transit into one of the states at the next turn + * some individuals can move (predictably) into "exit" state and leave + population at the next turn + * you want to model transitions of individuals from one state into another, + including transitions from the "entry" state and into the "exit" state. + * but you do NOT want to predict amount of "new" individuals because it + does not depends on individuals already present (hence system can not + transit INTO entry state - it can only transit FROM it). + + This model is discussed in more details in the ALGLIB User Guide (see + http://www.alglib.net/dataanalysis/ for more data). INPUT PARAMETERS: - S - KNN builder object - XY - array[NPoints,NVars+NOut] (note: actual size can be - larger, only leading part is used anyway), dataset: - * first NVars elements of each row store values of the - independent variables - * next NOut elements store values of the dependent - variables - NPoints - number of rows in the dataset, NPoints>=1 - NVars - number of independent variables, NVars>=1 - NOut - number of dependent variables, NOut>=1 + N - problem dimension, N>=2 + EntryState- index of entry state, in 0..N-1 + ExitState- index of exit state, in 0..N-1 OUTPUT PARAMETERS: - S - KNN builder + State - structure stores algorithm state -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static void knnbuildersetdatasetreg(knnbuilder s, double[,] xy, int npoints, int nvars, int nout) + public static void mcpdcreateentryexit(int n, int entrystate, int exitstate, out mcpdstate s) { - - knn.knnbuildersetdatasetreg(s.innerobj, xy, npoints, nvars, nout, null); + s = new mcpdstate(); + mcpd.mcpdcreateentryexit(n, entrystate, exitstate, s.innerobj, null); } - public static void knnbuildersetdatasetreg(knnbuilder s, double[,] xy, int npoints, int nvars, int nout, alglib.xparams _params) + public static void mcpdcreateentryexit(int n, int entrystate, int exitstate, out mcpdstate s, alglib.xparams _params) { - - knn.knnbuildersetdatasetreg(s.innerobj, xy, npoints, nvars, nout, _params); + s = new mcpdstate(); + mcpd.mcpdcreateentryexit(n, entrystate, exitstate, s.innerobj, _params); } /************************************************************************* - Specifies classification problem (two or more classes are predicted). - There also exists "regression" version of this function. + This function is used to add a track - sequence of system states at the + different moments of its evolution. - This subroutine adds dense dataset to the internal storage of the builder - object. Specifying your dataset in the dense format means that the dense - version of the KNN construction algorithm will be invoked. + You may add one or several tracks to the MCPD solver. In case you have + several tracks, they won't overwrite each other. For example, if you pass + two tracks, A1-A2-A3 (system at t=A+1, t=A+2 and t=A+3) and B1-B2-B3, then + solver will try to model transitions from t=A+1 to t=A+2, t=A+2 to t=A+3, + t=B+1 to t=B+2, t=B+2 to t=B+3. But it WONT mix these two tracks - i.e. it + wont try to model transition from t=A+3 to t=B+1. INPUT PARAMETERS: - S - KNN builder object - XY - array[NPoints,NVars+1] (note: actual size can be - larger, only leading part is used anyway), dataset: - * first NVars elements of each row store values of the - independent variables - * next element stores class index, in [0,NClasses) - NPoints - number of rows in the dataset, NPoints>=1 - NVars - number of independent variables, NVars>=1 - NClasses - number of classes, NClasses>=2 + S - solver + XY - track, array[K,N]: + * I-th row is a state at t=I + * elements of XY must be non-negative (exception will be + thrown on negative elements) + K - number of points in a track + * if given, only leading K rows of XY are used + * if not given, automatically determined from size of XY - OUTPUT PARAMETERS: - S - KNN builder + NOTES: + + 1. Track may contain either proportional or population data: + * with proportional data all rows of XY must sum to 1.0, i.e. we have + proportions instead of absolute population values + * with population data rows of XY contain population counts and generally + do not sum to 1.0 (although they still must be non-negative) -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static void knnbuildersetdatasetcls(knnbuilder s, double[,] xy, int npoints, int nvars, int nclasses) + public static void mcpdaddtrack(mcpdstate s, double[,] xy, int k) { - knn.knnbuildersetdatasetcls(s.innerobj, xy, npoints, nvars, nclasses, null); + mcpd.mcpdaddtrack(s.innerobj, xy, k, null); } - public static void knnbuildersetdatasetcls(knnbuilder s, double[,] xy, int npoints, int nvars, int nclasses, alglib.xparams _params) + public static void mcpdaddtrack(mcpdstate s, double[,] xy, int k, alglib.xparams _params) { - knn.knnbuildersetdatasetcls(s.innerobj, xy, npoints, nvars, nclasses, _params); + mcpd.mcpdaddtrack(s.innerobj, xy, k, _params); } - - /************************************************************************* - This function sets norm type used for neighbor search. - - INPUT PARAMETERS: - S - decision forest builder object - NormType - norm type: - * 0 inf-norm - * 1 1-norm - * 2 Euclidean norm (default) - - OUTPUT PARAMETERS: - S - decision forest builder - - -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey - *************************************************************************/ - public static void knnbuildersetnorm(knnbuilder s, int nrmtype) + + public static void mcpdaddtrack(mcpdstate s, double[,] xy) { + int k; - knn.knnbuildersetnorm(s.innerobj, nrmtype, null); - } - public static void knnbuildersetnorm(knnbuilder s, int nrmtype, alglib.xparams _params) + k = ap.rows(xy); + mcpd.mcpdaddtrack(s.innerobj, xy, k, null); + + return; + } + + public static void mcpdaddtrack(mcpdstate s, double[,] xy, alglib.xparams _params) { + int k; - knn.knnbuildersetnorm(s.innerobj, nrmtype, _params); + + k = ap.rows(xy); + mcpd.mcpdaddtrack(s.innerobj, xy, k, _params); + + return; } /************************************************************************* - This subroutine builds KNN model according to current settings, using - dataset internally stored in the builder object. + This function is used to add equality constraints on the elements of the + transition matrix P. - The model being built performs inference using Eps-approximate K nearest - neighbors search algorithm, with: - * K=1, Eps=0 corresponding to the "nearest neighbor algorithm" - * K>1, Eps=0 corresponding to the "K nearest neighbors algorithm" - * K>=1, Eps>0 corresponding to "approximate nearest neighbors algorithm" + MCPD solver has four types of constraints which can be placed on P: + * user-specified equality constraints (optional) + * user-specified bound constraints (optional) + * user-specified general linear constraints (optional) + * basic constraints (always present): + * non-negativity: P[i,j]>=0 + * consistency: every column of P sums to 1.0 - An approximate KNN is a good option for high-dimensional datasets (exact - KNN works slowly when dimensions count grows). + Final constraints which are passed to the underlying optimizer are + calculated as intersection of all present constraints. For example, you + may specify boundary constraint on P[0,0] and equality one: + 0.1<=P[0,0]<=0.9 + P[0,0]=0.5 + Such combination of constraints will be silently reduced to their + intersection, which is P[0,0]=0.5. - An ALGLIB implementation of kd-trees is used to perform k-nn searches. + This function can be used to place equality constraints on arbitrary + subset of elements of P. Set of constraints is specified by EC, which may + contain either NAN's or finite numbers from [0,1]. NAN denotes absence of + constraint, finite number denotes equality constraint on specific element + of P. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + You can also use MCPDAddEC() function which allows to ADD equality + constraint for one element of P without changing constraints for other + elements. + + These functions (MCPDSetEC and MCPDAddEC) interact as follows: + * there is internal matrix of equality constraints which is stored in the + MCPD solver + * MCPDSetEC() replaces this matrix by another one (SET) + * MCPDAddEC() modifies one element of this matrix and leaves other ones + unchanged (ADD) + * thus MCPDAddEC() call preserves all modifications done by previous + calls, while MCPDSetEC() completely discards all changes done to the + equality constraints. INPUT PARAMETERS: - S - KNN builder object - K - number of neighbors to search for, K>=1 - Eps - approximation factor: - * Eps=0 means that exact kNN search is performed - * Eps>0 means that (1+Eps)-approximate search is performed + S - solver + EC - equality constraints, array[N,N]. Elements of EC can be + either NAN's or finite numbers from [0,1]. NAN denotes + absence of constraints, while finite value denotes + equality constraint on the corresponding element of P. - OUTPUT PARAMETERS: - Model - KNN model - Rep - report + NOTES: + + 1. infinite values of EC will lead to exception being thrown. Values less + than 0.0 or greater than 1.0 will lead to error code being returned after + call to MCPDSolve(). -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static void knnbuilderbuildknnmodel(knnbuilder s, int k, double eps, out knnmodel model, out knnreport rep) + public static void mcpdsetec(mcpdstate s, double[,] ec) { - model = new knnmodel(); - rep = new knnreport(); - knn.knnbuilderbuildknnmodel(s.innerobj, k, eps, model.innerobj, rep.innerobj, null); + + mcpd.mcpdsetec(s.innerobj, ec, null); } - public static void knnbuilderbuildknnmodel(knnbuilder s, int k, double eps, out knnmodel model, out knnreport rep, alglib.xparams _params) + public static void mcpdsetec(mcpdstate s, double[,] ec, alglib.xparams _params) { - model = new knnmodel(); - rep = new knnreport(); - knn.knnbuilderbuildknnmodel(s.innerobj, k, eps, model.innerobj, rep.innerobj, _params); + + mcpd.mcpdsetec(s.innerobj, ec, _params); } /************************************************************************* - Changing search settings of KNN model. - - K and EPS parameters of KNN (AKNN) search are specified during model - construction. However, plain KNN algorithm with Euclidean distance allows - you to change them at any moment. + This function is used to add equality constraints on the elements of the + transition matrix P. - NOTE: future versions of KNN model may support advanced versions of KNN, - such as NCA or LMNN. It is possible that such algorithms won't allow - you to change search settings on the fly. If you call this function - for an algorithm which does not support on-the-fly changes, it will - throw an exception. + MCPD solver has four types of constraints which can be placed on P: + * user-specified equality constraints (optional) + * user-specified bound constraints (optional) + * user-specified general linear constraints (optional) + * basic constraints (always present): + * non-negativity: P[i,j]>=0 + * consistency: every column of P sums to 1.0 + + Final constraints which are passed to the underlying optimizer are + calculated as intersection of all present constraints. For example, you + may specify boundary constraint on P[0,0] and equality one: + 0.1<=P[0,0]<=0.9 + P[0,0]=0.5 + Such combination of constraints will be silently reduced to their + intersection, which is P[0,0]=0.5. + + This function can be used to ADD equality constraint for one element of P + without changing constraints for other elements. + + You can also use MCPDSetEC() function which allows you to specify + arbitrary set of equality constraints in one call. + + These functions (MCPDSetEC and MCPDAddEC) interact as follows: + * there is internal matrix of equality constraints which is stored in the + MCPD solver + * MCPDSetEC() replaces this matrix by another one (SET) + * MCPDAddEC() modifies one element of this matrix and leaves other ones + unchanged (ADD) + * thus MCPDAddEC() call preserves all modifications done by previous + calls, while MCPDSetEC() completely discards all changes done to the + equality constraints. INPUT PARAMETERS: - Model - KNN model - K - K>=1, neighbors count - EPS - accuracy of the EPS-approximate NN search. Set to 0.0, if - you want to perform "classic" KNN search. Specify larger - values if you need to speed-up high-dimensional KNN - queries. + S - solver + I - row index of element being constrained + J - column index of element being constrained + C - value (constraint for P[I,J]). Can be either NAN (no + constraint) or finite value from [0,1]. - OUTPUT PARAMETERS: - nothing on success, exception on failure + NOTES: + + 1. infinite values of C will lead to exception being thrown. Values less + than 0.0 or greater than 1.0 will lead to error code being returned after + call to MCPDSolve(). -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static void knnrewritekeps(knnmodel model, int k, double eps) + public static void mcpdaddec(mcpdstate s, int i, int j, double c) { - knn.knnrewritekeps(model.innerobj, k, eps, null); + mcpd.mcpdaddec(s.innerobj, i, j, c, null); } - public static void knnrewritekeps(knnmodel model, int k, double eps, alglib.xparams _params) + public static void mcpdaddec(mcpdstate s, int i, int j, double c, alglib.xparams _params) { - knn.knnrewritekeps(model.innerobj, k, eps, _params); + mcpd.mcpdaddec(s.innerobj, i, j, c, _params); } /************************************************************************* - Inference using KNN model. + This function is used to add bound constraints on the elements of the + transition matrix P. - See also knnprocess0(), knnprocessi() and knnclassify() for options with a - bit more convenient interface. + MCPD solver has four types of constraints which can be placed on P: + * user-specified equality constraints (optional) + * user-specified bound constraints (optional) + * user-specified general linear constraints (optional) + * basic constraints (always present): + * non-negativity: P[i,j]>=0 + * consistency: every column of P sums to 1.0 - IMPORTANT: this function is thread-unsafe and modifies internal structures - of the model! You can not use same model object for parallel - evaluation from several threads. + Final constraints which are passed to the underlying optimizer are + calculated as intersection of all present constraints. For example, you + may specify boundary constraint on P[0,0] and equality one: + 0.1<=P[0,0]<=0.9 + P[0,0]=0.5 + Such combination of constraints will be silently reduced to their + intersection, which is P[0,0]=0.5. - Use knntsprocess() with independent thread-local buffers, if - you need thread-safe evaluation. + This function can be used to place bound constraints on arbitrary + subset of elements of P. Set of constraints is specified by BndL/BndU + matrices, which may contain arbitrary combination of finite numbers or + infinities (like -INF=0 + * consistency: every column of P sums to 1.0 - In the former case this function returns inference result as scalar, which - is definitely more convenient that wrapping it as vector. In the latter - case it returns probability of object belonging to class #0. + Final constraints which are passed to the underlying optimizer are + calculated as intersection of all present constraints. For example, you + may specify boundary constraint on P[0,0] and equality one: + 0.1<=P[0,0]<=0.9 + P[0,0]=0.5 + Such combination of constraints will be silently reduced to their + intersection, which is P[0,0]=0.5. - If you call it for anything different from two cases above, it will work - as defined, i.e. return y[0], although it is of less use in such cases. + This function can be used to ADD bound constraint for one element of P + without changing constraints for other elements. - IMPORTANT: this function is thread-unsafe and modifies internal structures - of the model! You can not use same model object for parallel - evaluation from several threads. + You can also use MCPDSetBC() function which allows to place bound + constraints on arbitrary subset of elements of P. Set of constraints is + specified by BndL/BndU matrices, which may contain arbitrary combination + of finite numbers or infinities (like -INF=" (CT[i]>0). - INPUT PARAMETERS: - Model - KNN model - X - input vector, array[0..NVars-1]. + Your constraint may involve only some subset of P (less than N*N elements). + For example it can be something like + P[0,0] + P[0,1] = 0.5 + In this case you still should pass matrix with N*N+1 columns, but all its + elements (except for C[0,0], C[0,1] and C[0,N*N-1]) will be zero. - RESULT: - class number, -1 for regression tasks + INPUT PARAMETERS: + S - solver + C - array[K,N*N+1] - coefficients of constraints + (see above for complete description) + CT - array[K] - constraint types + (see above for complete description) + K - number of equality/inequality constraints, K>=0: + * if given, only leading K elements of C/CT are used + * if not given, automatically determined from sizes of C/CT -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static int knnclassify(knnmodel model, double[] x) + public static void mcpdsetlc(mcpdstate s, double[,] c, int[] ct, int k) { - return knn.knnclassify(model.innerobj, x, null); + mcpd.mcpdsetlc(s.innerobj, c, ct, k, null); } - public static int knnclassify(knnmodel model, double[] x, alglib.xparams _params) + public static void mcpdsetlc(mcpdstate s, double[,] c, int[] ct, int k, alglib.xparams _params) { - return knn.knnclassify(model.innerobj, x, _params); + mcpd.mcpdsetlc(s.innerobj, c, ct, k, _params); } - - /************************************************************************* - 'interactive' variant of knnprocess() for languages like Python which - support constructs like "y = knnprocessi(model,x)" and interactive mode of - the interpreter. - - This function allocates new array on each call, so it is significantly - slower than its 'non-interactive' counterpart, but it is more convenient - when you call it from command line. - - IMPORTANT: this function is thread-unsafe and may modify internal - structures of the model! You can not use same model object for - parallel evaluation from several threads. - - Use knntsprocess() with independent thread-local buffers if - you need thread-safe evaluation. - - -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey - *************************************************************************/ - public static void knnprocessi(knnmodel model, double[] x, out double[] y) + + public static void mcpdsetlc(mcpdstate s, double[,] c, int[] ct) { - y = new double[0]; - knn.knnprocessi(model.innerobj, x, ref y, null); - } + int k; + if( (ap.rows(c)!=ap.len(ct))) + throw new alglibexception("Error while calling 'mcpdsetlc': looks like one of arguments has wrong size"); - public static void knnprocessi(knnmodel model, double[] x, out double[] y, alglib.xparams _params) + k = ap.rows(c); + mcpd.mcpdsetlc(s.innerobj, c, ct, k, null); + + return; + } + + public static void mcpdsetlc(mcpdstate s, double[,] c, int[] ct, alglib.xparams _params) { - y = new double[0]; - knn.knnprocessi(model.innerobj, x, ref y, _params); + int k; + if( (ap.rows(c)!=ap.len(ct))) + throw new alglibexception("Error while calling 'mcpdsetlc': looks like one of arguments has wrong size"); + + k = ap.rows(c); + mcpd.mcpdsetlc(s.innerobj, c, ct, k, _params); + + return; } /************************************************************************* - Thread-safe procesing using external buffer for temporaries. + This function allows to tune amount of Tikhonov regularization being + applied to your problem. - This function is thread-safe (i.e . you can use same KNN model from - multiple threads) as long as you use different buffer objects for different - threads. + By default, regularizing term is equal to r*||P-prior_P||^2, where r is a + small non-zero value, P is transition matrix, prior_P is identity matrix, + ||X||^2 is a sum of squared elements of X. - INPUT PARAMETERS: - Model - KNN model - Buf - buffer object, must be allocated specifically for this - model with knncreatebuffer(). - X - input vector, array[NVars] + This function allows you to change coefficient r. You can also change + prior values with MCPDSetPrior() function. - OUTPUT PARAMETERS: - Y - result, array[NOut]. Regression estimate when solving - regression task, vector of posterior probabilities for - a classification task. + INPUT PARAMETERS: + S - solver + V - regularization coefficient, finite non-negative value. It + is not recommended to specify zero value unless you are + pretty sure that you want it. -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static void knntsprocess(knnmodel model, knnbuffer buf, double[] x, ref double[] y) + public static void mcpdsettikhonovregularizer(mcpdstate s, double v) { - knn.knntsprocess(model.innerobj, buf.innerobj, x, ref y, null); + mcpd.mcpdsettikhonovregularizer(s.innerobj, v, null); } - public static void knntsprocess(knnmodel model, knnbuffer buf, double[] x, ref double[] y, alglib.xparams _params) + public static void mcpdsettikhonovregularizer(mcpdstate s, double v, alglib.xparams _params) { - knn.knntsprocess(model.innerobj, buf.innerobj, x, ref y, _params); + mcpd.mcpdsettikhonovregularizer(s.innerobj, v, _params); } /************************************************************************* - Relative classification error on the test set + This function allows to set prior values used for regularization of your + problem. - INPUT PARAMETERS: - Model - KNN model - XY - test set - NPoints - test set size + By default, regularizing term is equal to r*||P-prior_P||^2, where r is a + small non-zero value, P is transition matrix, prior_P is identity matrix, + ||X||^2 is a sum of squared elements of X. - RESULT: - percent of incorrectly classified cases. - Zero if model solves regression task. + This function allows you to change prior values prior_P. You can also + change r with MCPDSetTikhonovRegularizer() function. - NOTE: if you need several different kinds of error metrics, it is better - to use knnallerrors() which computes all error metric with just one - pass over dataset. + INPUT PARAMETERS: + S - solver + PP - array[N,N], matrix of prior values: + 1. elements must be real numbers from [0,1] + 2. columns must sum to 1.0. + First property is checked (exception is thrown otherwise), + while second one is not checked/enforced. -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static double knnrelclserror(knnmodel model, double[,] xy, int npoints) + public static void mcpdsetprior(mcpdstate s, double[,] pp) { - return knn.knnrelclserror(model.innerobj, xy, npoints, null); + mcpd.mcpdsetprior(s.innerobj, pp, null); } - public static double knnrelclserror(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) + public static void mcpdsetprior(mcpdstate s, double[,] pp, alglib.xparams _params) { - return knn.knnrelclserror(model.innerobj, xy, npoints, _params); + mcpd.mcpdsetprior(s.innerobj, pp, _params); } /************************************************************************* - Average cross-entropy (in bits per element) on the test set - - INPUT PARAMETERS: - Model - KNN model - XY - test set - NPoints - test set size + This function is used to change prediction weights - RESULT: - CrossEntropy/NPoints. - Zero if model solves regression task. + MCPD solver scales prediction errors as follows + Error(P) = ||W*(y-P*x)||^2 + where + x is a system state at time t + y is a system state at time t+1 + P is a transition matrix + W is a diagonal scaling matrix - NOTE: the cross-entropy metric is too unstable when used to evaluate KNN - models (such models can report exactly zero probabilities), so we - do not recommend using it. + By default, weights are chosen in order to minimize relative prediction + error instead of absolute one. For example, if one component of state is + about 0.5 in magnitude and another one is about 0.05, then algorithm will + make corresponding weights equal to 2.0 and 20.0. - NOTE: if you need several different kinds of error metrics, it is better - to use knnallerrors() which computes all error metric with just one - pass over dataset. + INPUT PARAMETERS: + S - solver + PW - array[N], weights: + * must be non-negative values (exception will be thrown otherwise) + * zero values will be replaced by automatically chosen values -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static double knnavgce(knnmodel model, double[,] xy, int npoints) + public static void mcpdsetpredictionweights(mcpdstate s, double[] pw) { - return knn.knnavgce(model.innerobj, xy, npoints, null); + mcpd.mcpdsetpredictionweights(s.innerobj, pw, null); } - public static double knnavgce(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) + public static void mcpdsetpredictionweights(mcpdstate s, double[] pw, alglib.xparams _params) { - return knn.knnavgce(model.innerobj, xy, npoints, _params); + mcpd.mcpdsetpredictionweights(s.innerobj, pw, _params); } /************************************************************************* - RMS error on the test set. - - Its meaning for regression task is obvious. As for classification problems, - RMS error means error when estimating posterior probabilities. - - INPUT PARAMETERS: - Model - KNN model - XY - test set - NPoints - test set size - - RESULT: - root mean square error. + This function is used to start solution of the MCPD problem. - NOTE: if you need several different kinds of error metrics, it is better - to use knnallerrors() which computes all error metric with just one - pass over dataset. + After return from this function, you can use MCPDResults() to get solution + and completion code. -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static double knnrmserror(knnmodel model, double[,] xy, int npoints) + public static void mcpdsolve(mcpdstate s) { - return knn.knnrmserror(model.innerobj, xy, npoints, null); + mcpd.mcpdsolve(s.innerobj, null); } - public static double knnrmserror(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) + public static void mcpdsolve(mcpdstate s, alglib.xparams _params) { - return knn.knnrmserror(model.innerobj, xy, npoints, _params); + mcpd.mcpdsolve(s.innerobj, _params); } /************************************************************************* - Average error on the test set - - Its meaning for regression task is obvious. As for classification problems, - average error means error when estimating posterior probabilities. + MCPD results INPUT PARAMETERS: - Model - KNN model - XY - test set - NPoints - test set size + State - algorithm state - RESULT: - average error + OUTPUT PARAMETERS: + P - array[N,N], transition matrix + Rep - optimization report. You should check Rep.TerminationType + in order to distinguish successful termination from + unsuccessful one. Speaking short, positive values denote + success, negative ones are failures. + More information about fields of this structure can be + found in the comments on MCPDReport datatype. - NOTE: if you need several different kinds of error metrics, it is better - to use knnallerrors() which computes all error metric with just one - pass over dataset. -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 23.05.2010 by Bochkanov Sergey *************************************************************************/ - public static double knnavgerror(knnmodel model, double[,] xy, int npoints) + public static void mcpdresults(mcpdstate s, out double[,] p, out mcpdreport rep) { - - return knn.knnavgerror(model.innerobj, xy, npoints, null); + p = new double[0,0]; + rep = new mcpdreport(); + mcpd.mcpdresults(s.innerobj, ref p, rep.innerobj, null); } - public static double knnavgerror(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) + public static void mcpdresults(mcpdstate s, out double[,] p, out mcpdreport rep, alglib.xparams _params) { - - return knn.knnavgerror(model.innerobj, xy, npoints, _params); + p = new double[0,0]; + rep = new mcpdreport(); + mcpd.mcpdresults(s.innerobj, ref p, rep.innerobj, _params); } - - /************************************************************************* - Average relative error on the test set - - Its meaning for regression task is obvious. As for classification problems, - average relative error means error when estimating posterior probabilities. - - INPUT PARAMETERS: - Model - KNN model - XY - test set - NPoints - test set size - RESULT: - average relative error +} +public partial class alglib +{ - NOTE: if you need several different kinds of error metrics, it is better - to use knnallerrors() which computes all error metric with just one - pass over dataset. + + /************************************************************************* + k-means++ clusterization. + Backward compatibility function, we recommend to use CLUSTERING subpackage + as better replacement. -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 21.03.2009 by Bochkanov Sergey *************************************************************************/ - public static double knnavgrelerror(knnmodel model, double[,] xy, int npoints) + public static void kmeansgenerate(double[,] xy, int npoints, int nvars, int k, int restarts, out int info, out double[,] c, out int[] xyc) { - - return knn.knnavgrelerror(model.innerobj, xy, npoints, null); + info = 0; + c = new double[0,0]; + xyc = new int[0]; + datacomp.kmeansgenerate(xy, npoints, nvars, k, restarts, ref info, ref c, ref xyc, null); } - public static double knnavgrelerror(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) + public static void kmeansgenerate(double[,] xy, int npoints, int nvars, int k, int restarts, out int info, out double[,] c, out int[] xyc, alglib.xparams _params) { - - return knn.knnavgrelerror(model.innerobj, xy, npoints, _params); + info = 0; + c = new double[0,0]; + xyc = new int[0]; + datacomp.kmeansgenerate(xy, npoints, nvars, k, restarts, ref info, ref c, ref xyc, _params); } + +} +public partial class alglib +{ + /************************************************************************* - Calculates all kinds of errors for the model in one call. + Principal components analysis - INPUT PARAMETERS: - Model - KNN model - XY - test set: - * one row per point - * first NVars columns store independent variables - * depending on problem type: - * next column stores class number in [0,NClasses) - for - classification problems - * next NOut columns store dependent variables - for - regression problems - NPoints - test set size, NPoints>=0 + This function builds orthogonal basis where first axis corresponds to + direction with maximum variance, second axis maximizes variance in the + subspace orthogonal to first axis and so on. + + This function builds FULL basis, i.e. returns N vectors corresponding to + ALL directions, no matter how informative. If you need just a few (say, + 10 or 50) of the most important directions, you may find it faster to use + one of the reduced versions: + * pcatruncatedsubspace() - for subspace iteration based method + + It should be noted that, unlike LDA, PCA does not use class labels. + + INPUT PARAMETERS: + X - dataset, array[NPoints,NVars]. + matrix contains ONLY INDEPENDENT VARIABLES. + NPoints - dataset size, NPoints>=0 + NVars - number of independent variables, NVars>=1 OUTPUT PARAMETERS: - Rep - following fields are loaded with errors for both regression - and classification models: - * rep.rmserror - RMS error for the output - * rep.avgerror - average error - * rep.avgrelerror - average relative error - following fields are set only for classification models, - zero for regression ones: - * relclserror - relative classification error, in [0,1] - * avgce - average cross-entropy in bits per dataset entry + S2 - array[NVars]. variance values corresponding + to basis vectors. + V - array[NVars,NVars] + matrix, whose columns store basis vectors. - NOTE: the cross-entropy metric is too unstable when used to evaluate KNN - models (such models can report exactly zero probabilities), so we - do not recommend using it. + ! FREE EDITION OF ALGLIB: + ! + ! Free Edition of ALGLIB supports following important features for this + ! function: + ! * C++ version: x64 SIMD support using C++ intrinsics + ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics + ! + ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB + ! Reference Manual in order to find out how to activate SIMD support + ! in ALGLIB. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! * hardware vendor (Intel, ARM) implementations of linear algebra and + ! other primitives (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. -- ALGLIB -- - Copyright 15.02.2019 by Bochkanov Sergey + Copyright 25.08.2008 by Bochkanov Sergey *************************************************************************/ - public static void knnallerrors(knnmodel model, double[,] xy, int npoints, out knnreport rep) + public static void pcabuildbasis(double[,] x, int npoints, int nvars, out double[] s2, out double[,] v) { - rep = new knnreport(); - knn.knnallerrors(model.innerobj, xy, npoints, rep.innerobj, null); + s2 = new double[0]; + v = new double[0,0]; + pca.pcabuildbasis(x, npoints, nvars, ref s2, ref v, null); } - public static void knnallerrors(knnmodel model, double[,] xy, int npoints, out knnreport rep, alglib.xparams _params) + public static void pcabuildbasis(double[,] x, int npoints, int nvars, out double[] s2, out double[,] v, alglib.xparams _params) { - rep = new knnreport(); - knn.knnallerrors(model.innerobj, xy, npoints, rep.innerobj, _params); + s2 = new double[0]; + v = new double[0,0]; + pca.pcabuildbasis(x, npoints, nvars, ref s2, ref v, _params); } - -} -public partial class alglib -{ - - - /************************************************************************* - Training report: - * RelCLSError - fraction of misclassified cases. - * AvgCE - acerage cross-entropy - * RMSError - root-mean-square error - * AvgError - average error - * AvgRelError - average relative error - * NGrad - number of gradient calculations - * NHess - number of Hessian calculations - * NCholesky - number of Cholesky decompositions - - NOTE 1: RelCLSError/AvgCE are zero on regression problems. - - NOTE 2: on classification problems RMSError/AvgError/AvgRelError contain - errors in prediction of posterior probabilities - *************************************************************************/ - public class mlpreport : alglibobject + + public static void pcabuildbasis(double[,] x, out double[] s2, out double[,] v) { - // - // Public declarations - // - public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } - public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } - public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } - public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } - public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } - public int ngrad { get { return _innerobj.ngrad; } set { _innerobj.ngrad = value; } } - public int nhess { get { return _innerobj.nhess; } set { _innerobj.nhess = value; } } - public int ncholesky { get { return _innerobj.ncholesky; } set { _innerobj.ncholesky = value; } } + int npoints; + int nvars; - public mlpreport() - { - _innerobj = new mlptrain.mlpreport(); - } - - public override alglib.alglibobject make_copy() - { - return new mlpreport((mlptrain.mlpreport)_innerobj.make_copy()); - } + s2 = new double[0]; + v = new double[0,0]; + npoints = ap.rows(x); + nvars = ap.cols(x); + pca.pcabuildbasis(x, npoints, nvars, ref s2, ref v, null); - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private mlptrain.mlpreport _innerobj; - public mlptrain.mlpreport innerobj { get { return _innerobj; } } - public mlpreport(mlptrain.mlpreport obj) - { - _innerobj = obj; - } + return; } - - - /************************************************************************* - Cross-validation estimates of generalization error - *************************************************************************/ - public class mlpcvreport : alglibobject + + public static void pcabuildbasis(double[,] x, out double[] s2, out double[,] v, alglib.xparams _params) { - // - // Public declarations - // - public double relclserror { get { return _innerobj.relclserror; } set { _innerobj.relclserror = value; } } - public double avgce { get { return _innerobj.avgce; } set { _innerobj.avgce = value; } } - public double rmserror { get { return _innerobj.rmserror; } set { _innerobj.rmserror = value; } } - public double avgerror { get { return _innerobj.avgerror; } set { _innerobj.avgerror = value; } } - public double avgrelerror { get { return _innerobj.avgrelerror; } set { _innerobj.avgrelerror = value; } } + int npoints; + int nvars; - public mlpcvreport() - { - _innerobj = new mlptrain.mlpcvreport(); - } - - public override alglib.alglibobject make_copy() - { - return new mlpcvreport((mlptrain.mlpcvreport)_innerobj.make_copy()); - } + s2 = new double[0]; + v = new double[0,0]; + npoints = ap.rows(x); + nvars = ap.cols(x); + pca.pcabuildbasis(x, npoints, nvars, ref s2, ref v, _params); - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private mlptrain.mlpcvreport _innerobj; - public mlptrain.mlpcvreport innerobj { get { return _innerobj; } } - public mlpcvreport(mlptrain.mlpcvreport obj) - { - _innerobj = obj; - } + return; } + + /************************************************************************* + Principal components analysis + This function performs truncated PCA, i.e. returns just a few most important + directions. - /************************************************************************* - Trainer object for neural network. + Internally it uses iterative eigensolver which is very efficient when only + a minor fraction of full basis is required. Thus, if you need full basis, + it is better to use pcabuildbasis() function. - You should not try to access fields of this object directly - use ALGLIB - functions to work with this object. - *************************************************************************/ - public class mlptrainer : alglibobject - { - // - // Public declarations - // - - public mlptrainer() - { - _innerobj = new mlptrain.mlptrainer(); - } - - public override alglib.alglibobject make_copy() - { - return new mlptrainer((mlptrain.mlptrainer)_innerobj.make_copy()); - } - - // - // Although some of declarations below are public, you should not use them - // They are intended for internal use only - // - private mlptrain.mlptrainer _innerobj; - public mlptrain.mlptrainer innerobj { get { return _innerobj; } } - public mlptrainer(mlptrain.mlptrainer obj) - { - _innerobj = obj; - } - } - - /************************************************************************* - Neural network training using modified Levenberg-Marquardt with exact - Hessian calculation and regularization. Subroutine trains neural network - with restarts from random positions. Algorithm is well suited for small - and medium scale problems (hundreds of weights). + It should be noted that, unlike LDA, PCA does not use class labels. INPUT PARAMETERS: - Network - neural network with initialized geometry - XY - training set - NPoints - training set size - Decay - weight decay constant, >=0.001 - Decay term 'Decay*||Weights||^2' is added to error - function. - If you don't know what Decay to choose, use 0.001. - Restarts - number of restarts from random position, >0. - If you don't know what Restarts to choose, use 2. + X - dataset, array[0..NPoints-1,0..NVars-1]. + matrix contains ONLY INDEPENDENT VARIABLES. + NPoints - dataset size, NPoints>=0 + NVars - number of independent variables, NVars>=1 + NNeeded - number of requested components, in [1,NVars] range; + this function is efficient only for NNeeded<=0.001 - Decay term 'Decay*||Weights||^2' is added to error - function. - If you don't know what Decay to choose, use 0.001. - Restarts - number of restarts from random position, >0. - If you don't know what Restarts to choose, use 2. - WStep - stopping criterion. Algorithm stops if step size is - less than WStep. Recommended value - 0.01. Zero step - size means stopping after MaxIts iterations. - MaxIts - stopping criterion. Algorithm stops after MaxIts - iterations (NOT gradient calculations). Zero MaxIts - means stopping when step is sufficiently small. - - OUTPUT PARAMETERS: - Network - trained neural network. - Info - return code: - * -8, if both WStep=0 and MaxIts=0 - * -2, if there is a point with class number - outside of [0..NOut-1]. - * -1, if wrong parameters specified - (NPoints<0, Restarts<1). - * 2, if task has been solved. - Rep - training report - - -- ALGLIB -- - Copyright 09.12.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlptrainlbfgs(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, out int info, out mlpreport rep) + + public static void pcatruncatedsubspace(double[,] x, int nneeded, double eps, int maxits, out double[] s2, out double[,] v) { - info = 0; - rep = new mlpreport(); - mlptrain.mlptrainlbfgs(network.innerobj, xy, npoints, decay, restarts, wstep, maxits, ref info, rep.innerobj, null); - } + int npoints; + int nvars; - public static void mlptrainlbfgs(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, out int info, out mlpreport rep, alglib.xparams _params) + s2 = new double[0]; + v = new double[0,0]; + npoints = ap.rows(x); + nvars = ap.cols(x); + pca.pcatruncatedsubspace(x, npoints, nvars, nneeded, eps, maxits, ref s2, ref v, null); + + return; + } + + public static void pcatruncatedsubspace(double[,] x, int nneeded, double eps, int maxits, out double[] s2, out double[,] v, alglib.xparams _params) { - info = 0; - rep = new mlpreport(); - mlptrain.mlptrainlbfgs(network.innerobj, xy, npoints, decay, restarts, wstep, maxits, ref info, rep.innerobj, _params); + int npoints; + int nvars; + + s2 = new double[0]; + v = new double[0,0]; + npoints = ap.rows(x); + nvars = ap.cols(x); + pca.pcatruncatedsubspace(x, npoints, nvars, nneeded, eps, maxits, ref s2, ref v, _params); + + return; } /************************************************************************* - Neural network training using early stopping (base algorithm - L-BFGS with - regularization). + Sparse truncated principal components analysis + + This function performs sparse truncated PCA, i.e. returns just a few most + important principal components for a sparse input X. + + Internally it uses iterative eigensolver which is very efficient when only + a minor fraction of full basis is required. + + It should be noted that, unlike LDA, PCA does not use class labels. INPUT PARAMETERS: - Network - neural network with initialized geometry - TrnXY - training set - TrnSize - training set size, TrnSize>0 - ValXY - validation set - ValSize - validation set size, ValSize>0 - Decay - weight decay constant, >=0.001 - Decay term 'Decay*||Weights||^2' is added to error - function. - If you don't know what Decay to choose, use 0.001. - Restarts - number of restarts, either: - * strictly positive number - algorithm make specified - number of restarts from random position. - * -1, in which case algorithm makes exactly one run - from the initial state of the network (no randomization). - If you don't know what Restarts to choose, choose one - one the following: - * -1 (deterministic start) - * +1 (one random restart) - * +5 (moderate amount of random restarts) + X - sparse dataset, sparse npoints*nvars matrix. It is + recommended to use CRS sparse storage format; non-CRS + input will be internally converted to CRS. + Matrix contains ONLY INDEPENDENT VARIABLES, and must + be EXACTLY npoints*nvars. + NPoints - dataset size, NPoints>=0 + NVars - number of independent variables, NVars>=1 + NNeeded - number of requested components, in [1,NVars] range; + this function is efficient only for NNeeded<0. - restarts are counted for each partition separately, so - total number of restarts will be Restarts*FoldsCount. - WStep - stopping criterion, same as in MLPTrainLBFGS - MaxIts - stopping criterion, same as in MLPTrainLBFGS - FoldsCount - number of folds in k-fold cross-validation, - 2<=FoldsCount<=SSize. - recommended value: 10. +} +public partial class alglib +{ + public partial class bdss + { + public class cvreport : apobject + { + public double relclserror; + public double avgce; + public double rmserror; + public double avgerror; + public double avgrelerror; + public cvreport() + { + init(); + } + public override void init() + { + } + public override alglib.apobject make_copy() + { + cvreport _result = new cvreport(); + _result.relclserror = relclserror; + _result.avgce = avgce; + _result.rmserror = rmserror; + _result.avgerror = avgerror; + _result.avgrelerror = avgrelerror; + return _result; + } + }; - OUTPUT PARAMETERS: - Info - return code, same as in MLPTrainLBFGS - Rep - report, same as in MLPTrainLM/MLPTrainLBFGS - CVRep - generalization error estimates - -- ALGLIB -- - Copyright 09.12.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpkfoldcvlbfgs(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, int foldscount, out int info, out mlpreport rep, out mlpcvreport cvrep) - { - info = 0; - rep = new mlpreport(); - cvrep = new mlpcvreport(); - mlptrain.mlpkfoldcvlbfgs(network.innerobj, xy, npoints, decay, restarts, wstep, maxits, foldscount, ref info, rep.innerobj, cvrep.innerobj, null); - } - - public static void mlpkfoldcvlbfgs(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, int foldscount, out int info, out mlpreport rep, out mlpcvreport cvrep, alglib.xparams _params) - { - info = 0; - rep = new mlpreport(); - cvrep = new mlpcvreport(); - mlptrain.mlpkfoldcvlbfgs(network.innerobj, xy, npoints, decay, restarts, wstep, maxits, foldscount, ref info, rep.innerobj, cvrep.innerobj, _params); - } - - /************************************************************************* - Cross-validation estimate of generalization error. - Base algorithm - Levenberg-Marquardt. - INPUT PARAMETERS: - Network - neural network with initialized geometry. Network is - not changed during cross-validation - it is used only - as a representative of its architecture. - XY - training set. - SSize - training set size - Decay - weight decay, same as in MLPTrainLBFGS - Restarts - number of restarts, >0. - restarts are counted for each partition separately, so - total number of restarts will be Restarts*FoldsCount. - FoldsCount - number of folds in k-fold cross-validation, - 2<=FoldsCount<=SSize. - recommended value: 10. + /************************************************************************* + This set of routines (DSErrAllocate, DSErrAccumulate, DSErrFinish) + calculates different error functions (classification error, cross-entropy, + rms, avg, avg.rel errors). - OUTPUT PARAMETERS: - Info - return code, same as in MLPTrainLBFGS - Rep - report, same as in MLPTrainLM/MLPTrainLBFGS - CVRep - generalization error estimates + 1. DSErrAllocate prepares buffer. + 2. DSErrAccumulate accumulates individual errors: + * Y contains predicted output (posterior probabilities for classification) + * DesiredY contains desired output (class number for classification) + 3. DSErrFinish outputs results: + * Buf[0] contains relative classification error (zero for regression tasks) + * Buf[1] contains avg. cross-entropy (zero for regression tasks) + * Buf[2] contains rms error (regression, classification) + * Buf[3] contains average error (regression, classification) + * Buf[4] contains average relative error (regression, classification) + + NOTES(1): + "NClasses>0" means that we have classification task. + "NClasses<0" means regression task with -NClasses real outputs. - -- ALGLIB -- - Copyright 09.12.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpkfoldcvlm(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, int foldscount, out int info, out mlpreport rep, out mlpcvreport cvrep) - { - info = 0; - rep = new mlpreport(); - cvrep = new mlpcvreport(); - mlptrain.mlpkfoldcvlm(network.innerobj, xy, npoints, decay, restarts, foldscount, ref info, rep.innerobj, cvrep.innerobj, null); - } - - public static void mlpkfoldcvlm(multilayerperceptron network, double[,] xy, int npoints, double decay, int restarts, int foldscount, out int info, out mlpreport rep, out mlpcvreport cvrep, alglib.xparams _params) - { - info = 0; - rep = new mlpreport(); - cvrep = new mlpcvreport(); - mlptrain.mlpkfoldcvlm(network.innerobj, xy, npoints, decay, restarts, foldscount, ref info, rep.innerobj, cvrep.innerobj, _params); - } - - /************************************************************************* - This function estimates generalization error using cross-validation on the - current dataset with current training settings. + NOTES(2): + rms. avg, avg.rel errors for classification tasks are interpreted as + errors in posterior probabilities with respect to probabilities given + by training/test set. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + -- ALGLIB -- + Copyright 11.01.2009 by Bochkanov Sergey + *************************************************************************/ + public static void dserrallocate(int nclasses, + ref double[] buf, + alglib.xparams _params) + { + buf = new double[0]; - INPUT PARAMETERS: - S - trainer object - Network - neural network. It must have same number of inputs and - output/classes as was specified during creation of the - trainer object. Network is not changed during cross- - validation and is not trained - it is used only as - representative of its architecture. I.e., we estimate - generalization properties of ARCHITECTURE, not some - specific network. - NRestarts - number of restarts, >=0: - * NRestarts>0 means that for each cross-validation - round specified number of random restarts is - performed, with best network being chosen after - training. - * NRestarts=0 is same as NRestarts=1 - FoldsCount - number of folds in k-fold cross-validation: - * 2<=FoldsCount<=size of dataset - * recommended value: 10. - * values larger than dataset size will be silently - truncated down to dataset size + buf = new double[7+1]; + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + buf[4] = 0; + buf[5] = nclasses; + buf[6] = 0; + buf[7] = 0; + } - OUTPUT PARAMETERS: - Rep - structure which contains cross-validation estimates: - * Rep.RelCLSError - fraction of misclassified cases. - * Rep.AvgCE - acerage cross-entropy - * Rep.RMSError - root-mean-square error - * Rep.AvgError - average error - * Rep.AvgRelError - average relative error - NOTE: when no dataset was specified with MLPSetDataset/SetSparseDataset(), - or subset with only one point was given, zeros are returned as - estimates. + /************************************************************************* + See DSErrAllocate for comments on this routine. - NOTE: this method performs FoldsCount cross-validation rounds, each one - with NRestarts random starts. Thus, FoldsCount*NRestarts networks - are trained in total. + -- ALGLIB -- + Copyright 11.01.2009 by Bochkanov Sergey + *************************************************************************/ + public static void dserraccumulate(ref double[] buf, + double[] y, + double[] desiredy, + alglib.xparams _params) + { + int nclasses = 0; + int nout = 0; + int offs = 0; + int mmax = 0; + int rmax = 0; + int j = 0; + double v = 0; + double ev = 0; - NOTE: Rep.RelCLSError/Rep.AvgCE are zero on regression problems. + offs = 5; + nclasses = (int)Math.Round(buf[offs]); + if( nclasses>0 ) + { + + // + // Classification + // + rmax = (int)Math.Round(desiredy[0]); + mmax = 0; + for(j=1; j<=nclasses-1; j++) + { + if( (double)(y[j])>(double)(y[mmax]) ) + { + mmax = j; + } + } + if( mmax!=rmax ) + { + buf[0] = buf[0]+1; + } + if( (double)(y[rmax])>(double)(0) ) + { + buf[1] = buf[1]-Math.Log(y[rmax]); + } + else + { + buf[1] = buf[1]+Math.Log(math.maxrealnumber); + } + for(j=0; j<=nclasses-1; j++) + { + v = y[j]; + if( j==rmax ) + { + ev = 1; + } + else + { + ev = 0; + } + buf[2] = buf[2]+math.sqr(v-ev); + buf[3] = buf[3]+Math.Abs(v-ev); + if( (double)(ev)!=(double)(0) ) + { + buf[4] = buf[4]+Math.Abs((v-ev)/ev); + buf[offs+2] = buf[offs+2]+1; + } + } + buf[offs+1] = buf[offs+1]+1; + } + else + { + + // + // Regression + // + nout = -nclasses; + rmax = 0; + for(j=1; j<=nout-1; j++) + { + if( (double)(desiredy[j])>(double)(desiredy[rmax]) ) + { + rmax = j; + } + } + mmax = 0; + for(j=1; j<=nout-1; j++) + { + if( (double)(y[j])>(double)(y[mmax]) ) + { + mmax = j; + } + } + if( mmax!=rmax ) + { + buf[0] = buf[0]+1; + } + for(j=0; j<=nout-1; j++) + { + v = y[j]; + ev = desiredy[j]; + buf[2] = buf[2]+math.sqr(v-ev); + buf[3] = buf[3]+Math.Abs(v-ev); + if( (double)(ev)!=(double)(0) ) + { + buf[4] = buf[4]+Math.Abs((v-ev)/ev); + buf[offs+2] = buf[offs+2]+1; + } + } + buf[offs+1] = buf[offs+1]+1; + } + } - NOTE: on classification problems Rep.RMSError/Rep.AvgError/Rep.AvgRelError - contain errors in prediction of posterior probabilities. - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlpkfoldcv(mlptrainer s, multilayerperceptron network, int nrestarts, int foldscount, out mlpreport rep) - { - rep = new mlpreport(); - mlptrain.mlpkfoldcv(s.innerobj, network.innerobj, nrestarts, foldscount, rep.innerobj, null); - } - - public static void mlpkfoldcv(mlptrainer s, multilayerperceptron network, int nrestarts, int foldscount, out mlpreport rep, alglib.xparams _params) - { - rep = new mlpreport(); - mlptrain.mlpkfoldcv(s.innerobj, network.innerobj, nrestarts, foldscount, rep.innerobj, _params); - } - - /************************************************************************* - Creation of the network trainer object for regression networks + /************************************************************************* + See DSErrAllocate for comments on this routine. - INPUT PARAMETERS: - NIn - number of inputs, NIn>=1 - NOut - number of outputs, NOut>=1 + -- ALGLIB -- + Copyright 11.01.2009 by Bochkanov Sergey + *************************************************************************/ + public static void dserrfinish(ref double[] buf, + alglib.xparams _params) + { + int nout = 0; + int offs = 0; - OUTPUT PARAMETERS: - S - neural network trainer object. - This structure can be used to train any regression - network with NIn inputs and NOut outputs. + offs = 5; + nout = Math.Abs((int)Math.Round(buf[offs])); + if( (double)(buf[offs+1])!=(double)(0) ) + { + buf[0] = buf[0]/buf[offs+1]; + buf[1] = buf[1]/buf[offs+1]; + buf[2] = Math.Sqrt(buf[2]/(nout*buf[offs+1])); + buf[3] = buf[3]/(nout*buf[offs+1]); + } + if( (double)(buf[offs+2])!=(double)(0) ) + { + buf[4] = buf[4]/buf[offs+2]; + } + } - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreatetrainer(int nin, int nout, out mlptrainer s) - { - s = new mlptrainer(); - mlptrain.mlpcreatetrainer(nin, nout, s.innerobj, null); - } - - public static void mlpcreatetrainer(int nin, int nout, out mlptrainer s, alglib.xparams _params) - { - s = new mlptrainer(); - mlptrain.mlpcreatetrainer(nin, nout, s.innerobj, _params); - } - - /************************************************************************* - Creation of the network trainer object for classification networks - INPUT PARAMETERS: - NIn - number of inputs, NIn>=1 - NClasses - number of classes, NClasses>=2 + /************************************************************************* - OUTPUT PARAMETERS: - S - neural network trainer object. - This structure can be used to train any classification - network with NIn inputs and NOut outputs. + -- ALGLIB -- + Copyright 19.05.2008 by Bochkanov Sergey + *************************************************************************/ + public static void dsnormalize(ref double[,] xy, + int npoints, + int nvars, + ref int info, + ref double[] means, + ref double[] sigmas, + alglib.xparams _params) + { + int i = 0; + int j = 0; + double[] tmp = new double[0]; + double mean = 0; + double variance = 0; + double skewness = 0; + double kurtosis = 0; + int i_ = 0; - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreatetrainercls(int nin, int nclasses, out mlptrainer s) - { - s = new mlptrainer(); - mlptrain.mlpcreatetrainercls(nin, nclasses, s.innerobj, null); - } - - public static void mlpcreatetrainercls(int nin, int nclasses, out mlptrainer s, alglib.xparams _params) - { - s = new mlptrainer(); - mlptrain.mlpcreatetrainercls(nin, nclasses, s.innerobj, _params); - } - - /************************************************************************* - This function sets "current dataset" of the trainer object to one passed - by user. + info = 0; + means = new double[0]; + sigmas = new double[0]; - INPUT PARAMETERS: - S - trainer object - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. - NPoints - points count, >=0. + + // + // Test parameters + // + if( npoints<=0 || nvars<1 ) + { + info = -1; + return; + } + info = 1; + + // + // Standartization + // + means = new double[nvars-1+1]; + sigmas = new double[nvars-1+1]; + tmp = new double[npoints-1+1]; + for(j=0; j<=nvars-1; j++) + { + for(i_=0; i_<=npoints-1;i_++) + { + tmp[i_] = xy[i_,j]; + } + basestat.samplemoments(tmp, npoints, ref mean, ref variance, ref skewness, ref kurtosis, _params); + means[j] = mean; + sigmas[j] = Math.Sqrt(variance); + if( (double)(sigmas[j])==(double)(0) ) + { + sigmas[j] = 1; + } + for(i=0; i<=npoints-1; i++) + { + xy[i,j] = (xy[i,j]-means[j])/sigmas[j]; + } + } + } - DATASET FORMAT: - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + /************************************************************************* - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + -- ALGLIB -- + Copyright 19.05.2008 by Bochkanov Sergey + *************************************************************************/ + public static void dsnormalizec(double[,] xy, + int npoints, + int nvars, + ref int info, + ref double[] means, + ref double[] sigmas, + alglib.xparams _params) + { + int j = 0; + double[] tmp = new double[0]; + double mean = 0; + double variance = 0; + double skewness = 0; + double kurtosis = 0; + int i_ = 0; - For classification networks with NIn inputs and NClasses clases following - datasetformat is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). - - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlpsetdataset(mlptrainer s, double[,] xy, int npoints) - { - - mlptrain.mlpsetdataset(s.innerobj, xy, npoints, null); - } - - public static void mlpsetdataset(mlptrainer s, double[,] xy, int npoints, alglib.xparams _params) - { - - mlptrain.mlpsetdataset(s.innerobj, xy, npoints, _params); - } - - /************************************************************************* - This function sets "current dataset" of the trainer object to one passed - by user (sparse matrix is used to store dataset). - - INPUT PARAMETERS: - S - trainer object - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Any sparse storage format can be used: - Hash-table, CRS... - NPoints - points count, >=0 - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs - - For classification networks with NIn inputs and NClasses clases following - datasetformat is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). - - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlpsetsparsedataset(mlptrainer s, sparsematrix xy, int npoints) - { - - mlptrain.mlpsetsparsedataset(s.innerobj, xy.innerobj, npoints, null); - } - - public static void mlpsetsparsedataset(mlptrainer s, sparsematrix xy, int npoints, alglib.xparams _params) - { - - mlptrain.mlpsetsparsedataset(s.innerobj, xy.innerobj, npoints, _params); - } - - /************************************************************************* - This function sets weight decay coefficient which is used for training. - - INPUT PARAMETERS: - S - trainer object - Decay - weight decay coefficient, >=0. Weight decay term - 'Decay*||Weights||^2' is added to error function. If - you don't know what Decay to choose, use 1.0E-3. - Weight decay can be set to zero, in this case network - is trained without weight decay. - - NOTE: by default network uses some small nonzero value for weight decay. - - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlpsetdecay(mlptrainer s, double decay) - { - - mlptrain.mlpsetdecay(s.innerobj, decay, null); - } - - public static void mlpsetdecay(mlptrainer s, double decay, alglib.xparams _params) - { - - mlptrain.mlpsetdecay(s.innerobj, decay, _params); - } - - /************************************************************************* - This function sets stopping criteria for the optimizer. - - INPUT PARAMETERS: - S - trainer object - WStep - stopping criterion. Algorithm stops if step size is - less than WStep. Recommended value - 0.01. Zero step - size means stopping after MaxIts iterations. - WStep>=0. - MaxIts - stopping criterion. Algorithm stops after MaxIts - epochs (full passes over entire dataset). Zero MaxIts - means stopping when step is sufficiently small. - MaxIts>=0. - - NOTE: by default, WStep=0.005 and MaxIts=0 are used. These values are also - used when MLPSetCond() is called with WStep=0 and MaxIts=0. - - NOTE: these stopping criteria are used for all kinds of neural training - - from "conventional" networks to early stopping ensembles. When used - for "conventional" networks, they are used as the only stopping - criteria. When combined with early stopping, they used as ADDITIONAL - stopping criteria which can terminate early stopping algorithm. - - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlpsetcond(mlptrainer s, double wstep, int maxits) - { - - mlptrain.mlpsetcond(s.innerobj, wstep, maxits, null); - } - - public static void mlpsetcond(mlptrainer s, double wstep, int maxits, alglib.xparams _params) - { - - mlptrain.mlpsetcond(s.innerobj, wstep, maxits, _params); - } - - /************************************************************************* - This function sets training algorithm: batch training using L-BFGS will be - used. - - This algorithm: - * the most robust for small-scale problems, but may be too slow for large - scale ones. - * perfoms full pass through the dataset before performing step - * uses conditions specified by MLPSetCond() for stopping - * is default one used by trainer object - - INPUT PARAMETERS: - S - trainer object - - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlpsetalgobatch(mlptrainer s) - { - - mlptrain.mlpsetalgobatch(s.innerobj, null); - } - - public static void mlpsetalgobatch(mlptrainer s, alglib.xparams _params) - { - - mlptrain.mlpsetalgobatch(s.innerobj, _params); - } - - /************************************************************************* - This function trains neural network passed to this function, using current - dataset (one which was passed to MLPSetDataset() or MLPSetSparseDataset()) - and current training settings. Training from NRestarts random starting - positions is performed, best network is chosen. - - Training is performed using current training algorithm. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - INPUT PARAMETERS: - S - trainer object - Network - neural network. It must have same number of inputs and - output/classes as was specified during creation of the - trainer object. - NRestarts - number of restarts, >=0: - * NRestarts>0 means that specified number of random - restarts are performed, best network is chosen after - training - * NRestarts=0 means that current state of the network - is used for training. - - OUTPUT PARAMETERS: - Network - trained network - - NOTE: when no dataset was specified with MLPSetDataset/SetSparseDataset(), - network is filled by zero values. Same behavior for functions - MLPStartTraining and MLPContinueTraining. - - NOTE: this method uses sum-of-squares error function for training. - - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlptrainnetwork(mlptrainer s, multilayerperceptron network, int nrestarts, out mlpreport rep) - { - rep = new mlpreport(); - mlptrain.mlptrainnetwork(s.innerobj, network.innerobj, nrestarts, rep.innerobj, null); - } - - public static void mlptrainnetwork(mlptrainer s, multilayerperceptron network, int nrestarts, out mlpreport rep, alglib.xparams _params) - { - rep = new mlpreport(); - mlptrain.mlptrainnetwork(s.innerobj, network.innerobj, nrestarts, rep.innerobj, _params); - } - - /************************************************************************* - IMPORTANT: this is an "expert" version of the MLPTrain() function. We do - not recommend you to use it unless you are pretty sure that you - need ability to monitor training progress. - - This function performs step-by-step training of the neural network. Here - "step-by-step" means that training starts with MLPStartTraining() call, - and then user subsequently calls MLPContinueTraining() to perform one more - iteration of the training. - - After call to this function trainer object remembers network and is ready - to train it. However, no training is performed until first call to - MLPContinueTraining() function. Subsequent calls to MLPContinueTraining() - will advance training progress one iteration further. - - EXAMPLE: - > - > ...initialize network and trainer object.... - > - > MLPStartTraining(Trainer, Network, True) - > while MLPContinueTraining(Trainer, Network) do - > ...visualize training progress... - > - - INPUT PARAMETERS: - S - trainer object - Network - neural network. It must have same number of inputs and - output/classes as was specified during creation of the - trainer object. - RandomStart - randomize network before training or not: - * True means that network is randomized and its - initial state (one which was passed to the trainer - object) is lost. - * False means that training is started from the - current state of the network - - OUTPUT PARAMETERS: - Network - neural network which is ready to training (weights are - initialized, preprocessor is initialized using current - training set) - - NOTE: this method uses sum-of-squares error function for training. - - NOTE: it is expected that trainer object settings are NOT changed during - step-by-step training, i.e. no one changes stopping criteria or - training set during training. It is possible and there is no defense - against such actions, but algorithm behavior in such cases is - undefined and can be unpredictable. - - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlpstarttraining(mlptrainer s, multilayerperceptron network, bool randomstart) - { - - mlptrain.mlpstarttraining(s.innerobj, network.innerobj, randomstart, null); - } - - public static void mlpstarttraining(mlptrainer s, multilayerperceptron network, bool randomstart, alglib.xparams _params) - { - - mlptrain.mlpstarttraining(s.innerobj, network.innerobj, randomstart, _params); - } - - /************************************************************************* - IMPORTANT: this is an "expert" version of the MLPTrain() function. We do - not recommend you to use it unless you are pretty sure that you - need ability to monitor training progress. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - This function performs step-by-step training of the neural network. Here - "step-by-step" means that training starts with MLPStartTraining() call, - and then user subsequently calls MLPContinueTraining() to perform one more - iteration of the training. - - This function performs one more iteration of the training and returns - either True (training continues) or False (training stopped). In case True - was returned, Network weights are updated according to the current state - of the optimization progress. In case False was returned, no additional - updates is performed (previous update of the network weights moved us to - the final point, and no additional updates is needed). - - EXAMPLE: - > - > [initialize network and trainer object] - > - > MLPStartTraining(Trainer, Network, True) - > while MLPContinueTraining(Trainer, Network) do - > [visualize training progress] - > - - INPUT PARAMETERS: - S - trainer object - Network - neural network structure, which is used to store - current state of the training process. - - OUTPUT PARAMETERS: - Network - weights of the neural network are rewritten by the - current approximation. - - NOTE: this method uses sum-of-squares error function for training. - - NOTE: it is expected that trainer object settings are NOT changed during - step-by-step training, i.e. no one changes stopping criteria or - training set during training. It is possible and there is no defense - against such actions, but algorithm behavior in such cases is - undefined and can be unpredictable. - - NOTE: It is expected that Network is the same one which was passed to - MLPStartTraining() function. However, THIS function checks only - following: - * that number of network inputs is consistent with trainer object - settings - * that number of network outputs/classes is consistent with trainer - object settings - * that number of network weights is the same as number of weights in - the network passed to MLPStartTraining() function - Exception is thrown when these conditions are violated. - - It is also expected that you do not change state of the network on - your own - the only party who has right to change network during its - training is a trainer object. Any attempt to interfere with trainer - may lead to unpredictable results. - - - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey - *************************************************************************/ - public static bool mlpcontinuetraining(mlptrainer s, multilayerperceptron network) - { - - return mlptrain.mlpcontinuetraining(s.innerobj, network.innerobj, null); - } - - public static bool mlpcontinuetraining(mlptrainer s, multilayerperceptron network, alglib.xparams _params) - { - - return mlptrain.mlpcontinuetraining(s.innerobj, network.innerobj, _params); - } - - /************************************************************************* - Training neural networks ensemble using bootstrap aggregating (bagging). - Modified Levenberg-Marquardt algorithm is used as base training method. - - INPUT PARAMETERS: - Ensemble - model with initialized geometry - XY - training set - NPoints - training set size - Decay - weight decay coefficient, >=0.001 - Restarts - restarts, >0. - - OUTPUT PARAMETERS: - Ensemble - trained model - Info - return code: - * -2, if there is a point with class number - outside of [0..NClasses-1]. - * -1, if incorrect parameters was passed - (NPoints<0, Restarts<1). - * 2, if task has been solved. - Rep - training report. - OOBErrors - out-of-bag generalization error estimate - - -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpebagginglm(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep, out mlpcvreport ooberrors) - { - info = 0; - rep = new mlpreport(); - ooberrors = new mlpcvreport(); - mlptrain.mlpebagginglm(ensemble.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, ooberrors.innerobj, null); - } - - public static void mlpebagginglm(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep, out mlpcvreport ooberrors, alglib.xparams _params) - { - info = 0; - rep = new mlpreport(); - ooberrors = new mlpcvreport(); - mlptrain.mlpebagginglm(ensemble.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, ooberrors.innerobj, _params); - } - - /************************************************************************* - Training neural networks ensemble using bootstrap aggregating (bagging). - L-BFGS algorithm is used as base training method. - - INPUT PARAMETERS: - Ensemble - model with initialized geometry - XY - training set - NPoints - training set size - Decay - weight decay coefficient, >=0.001 - Restarts - restarts, >0. - WStep - stopping criterion, same as in MLPTrainLBFGS - MaxIts - stopping criterion, same as in MLPTrainLBFGS - - OUTPUT PARAMETERS: - Ensemble - trained model - Info - return code: - * -8, if both WStep=0 and MaxIts=0 - * -2, if there is a point with class number - outside of [0..NClasses-1]. - * -1, if incorrect parameters was passed - (NPoints<0, Restarts<1). - * 2, if task has been solved. - Rep - training report. - OOBErrors - out-of-bag generalization error estimate - - -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpebagginglbfgs(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, out int info, out mlpreport rep, out mlpcvreport ooberrors) - { - info = 0; - rep = new mlpreport(); - ooberrors = new mlpcvreport(); - mlptrain.mlpebagginglbfgs(ensemble.innerobj, xy, npoints, decay, restarts, wstep, maxits, ref info, rep.innerobj, ooberrors.innerobj, null); - } - - public static void mlpebagginglbfgs(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, double wstep, int maxits, out int info, out mlpreport rep, out mlpcvreport ooberrors, alglib.xparams _params) - { - info = 0; - rep = new mlpreport(); - ooberrors = new mlpcvreport(); - mlptrain.mlpebagginglbfgs(ensemble.innerobj, xy, npoints, decay, restarts, wstep, maxits, ref info, rep.innerobj, ooberrors.innerobj, _params); - } - - /************************************************************************* - Training neural networks ensemble using early stopping. - - INPUT PARAMETERS: - Ensemble - model with initialized geometry - XY - training set - NPoints - training set size - Decay - weight decay coefficient, >=0.001 - Restarts - restarts, >0. - - OUTPUT PARAMETERS: - Ensemble - trained model - Info - return code: - * -2, if there is a point with class number - outside of [0..NClasses-1]. - * -1, if incorrect parameters was passed - (NPoints<0, Restarts<1). - * 6, if task has been solved. - Rep - training report. - OOBErrors - out-of-bag generalization error estimate - - -- ALGLIB -- - Copyright 10.03.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpetraines(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep) - { - info = 0; - rep = new mlpreport(); - mlptrain.mlpetraines(ensemble.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, null); - } - - public static void mlpetraines(mlpensemble ensemble, double[,] xy, int npoints, double decay, int restarts, out int info, out mlpreport rep, alglib.xparams _params) - { - info = 0; - rep = new mlpreport(); - mlptrain.mlpetraines(ensemble.innerobj, xy, npoints, decay, restarts, ref info, rep.innerobj, _params); - } - - /************************************************************************* - 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). - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - INPUT PARAMETERS: - S - trainer object; - Ensemble - neural network ensemble. It must have same number of - inputs and outputs/classes as was specified during - creation of the trainer object. - NRestarts - number of restarts, >=0: - * NRestarts>0 means that specified number of random - restarts are performed during each ES round; - * NRestarts=0 is silently replaced by 1. - - OUTPUT PARAMETERS: - Ensemble - trained ensemble; - Rep - it contains all type of errors. - - NOTE: this training method uses BOTH early stopping and weight decay! So, - you should select weight decay before starting training just as you - select it before training "conventional" networks. - - NOTE: when no dataset was specified with MLPSetDataset/SetSparseDataset(), - or single-point dataset was passed, ensemble is filled by zero - values. - - NOTE: this method uses sum-of-squares error function for training. - - -- ALGLIB -- - Copyright 22.08.2012 by Bochkanov Sergey - *************************************************************************/ - public static void mlptrainensemblees(mlptrainer s, mlpensemble ensemble, int nrestarts, out mlpreport rep) - { - rep = new mlpreport(); - mlptrain.mlptrainensemblees(s.innerobj, ensemble.innerobj, nrestarts, rep.innerobj, null); - } - - public static void mlptrainensemblees(mlptrainer s, mlpensemble ensemble, int nrestarts, out mlpreport rep, alglib.xparams _params) - { - rep = new mlpreport(); - mlptrain.mlptrainensemblees(s.innerobj, ensemble.innerobj, nrestarts, rep.innerobj, _params); - } - -} -public partial class alglib -{ - - - /************************************************************************* - k-means++ clusterization. - Backward compatibility function, we recommend to use CLUSTERING subpackage - as better replacement. - - -- ALGLIB -- - Copyright 21.03.2009 by Bochkanov Sergey - *************************************************************************/ - public static void kmeansgenerate(double[,] xy, int npoints, int nvars, int k, int restarts, out int info, out double[,] c, out int[] xyc) - { - info = 0; - c = new double[0,0]; - xyc = new int[0]; - datacomp.kmeansgenerate(xy, npoints, nvars, k, restarts, ref info, ref c, ref xyc, null); - } - - public static void kmeansgenerate(double[,] xy, int npoints, int nvars, int k, int restarts, out int info, out double[,] c, out int[] xyc, alglib.xparams _params) - { - info = 0; - c = new double[0,0]; - xyc = new int[0]; - datacomp.kmeansgenerate(xy, npoints, nvars, k, restarts, ref info, ref c, ref xyc, _params); - } - -} -public partial class alglib -{ - public class pca - { - /************************************************************************* - Principal components analysis - - This function builds orthogonal basis where first axis corresponds to - direction with maximum variance, second axis maximizes variance in the - subspace orthogonal to first axis and so on. - - This function builds FULL basis, i.e. returns N vectors corresponding to - ALL directions, no matter how informative. If you need just a few (say, - 10 or 50) of the most important directions, you may find it faster to use - one of the reduced versions: - * pcatruncatedsubspace() - for subspace iteration based method - - It should be noted that, unlike LDA, PCA does not use class labels. - - INPUT PARAMETERS: - X - dataset, array[NPoints,NVars]. - matrix contains ONLY INDEPENDENT VARIABLES. - NPoints - dataset size, NPoints>=0 - NVars - number of independent variables, NVars>=1 - - OUTPUT PARAMETERS: - S2 - array[NVars]. variance values corresponding - to basis vectors. - V - array[NVars,NVars] - matrix, whose columns store basis vectors. - - ! FREE EDITION OF ALGLIB: - ! - ! Free Edition of ALGLIB supports following important features for this - ! function: - ! * C++ version: x64 SIMD support using C++ intrinsics - ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics - ! - ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB - ! Reference Manual in order to find out how to activate SIMD support - ! in ALGLIB. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! * hardware vendor (Intel, ARM) implementations of linear algebra and - ! other primitives (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - -- ALGLIB -- - Copyright 25.08.2008 by Bochkanov Sergey - *************************************************************************/ - public static void pcabuildbasis(double[,] x, - int npoints, - int nvars, - ref double[] s2, - ref double[,] v, - alglib.xparams _params) - { - double[,] a = new double[0,0]; - double[,] u = new double[0,0]; - double[,] vt = new double[0,0]; - double[] m = new double[0]; - double[] t = new double[0]; - int i = 0; - int j = 0; - double mean = 0; - double variance = 0; - double skewness = 0; - double kurtosis = 0; - int i_ = 0; - - s2 = new double[0]; - v = new double[0,0]; - - - // - // Check input data - // - alglib.ap.assert(npoints>=0, "PCABuildBasis: NPoints<0"); - alglib.ap.assert(nvars>=1, "PCABuildBasis: NVars<1"); - alglib.ap.assert(alglib.ap.rows(x)>=npoints, "PCABuildBasis: rows(X)=nvars || npoints==0, "PCABuildBasis: cols(X)=0 - NVars - number of independent variables, NVars>=1 - NNeeded - number of requested components, in [1,NVars] range; - this function is efficient only for NNeeded<=0, "PCATruncatedSubspace: npoints<0"); - alglib.ap.assert(nvars>=1, "PCATruncatedSubspace: nvars<1"); - alglib.ap.assert(nneeded>0, "PCATruncatedSubspace: nneeded<1"); - alglib.ap.assert(nneeded<=nvars, "PCATruncatedSubspace: nneeded>nvars"); - alglib.ap.assert(maxits>=0, "PCATruncatedSubspace: maxits<0"); - alglib.ap.assert(math.isfinite(eps) && (double)(eps)>=(double)(0), "PCATruncatedSubspace: eps<0 or is not finite"); - alglib.ap.assert(alglib.ap.rows(x)>=npoints, "PCATruncatedSubspace: rows(x)=nvars || npoints==0, "PCATruncatedSubspace: cols(x)=0 - NVars - number of independent variables, NVars>=1 - NNeeded - number of requested components, in [1,NVars] range; - this function is efficient only for NNeeded<=0, "PCATruncatedSubspaceSparse: npoints<0"); - alglib.ap.assert(nvars>=1, "PCATruncatedSubspaceSparse: nvars<1"); - alglib.ap.assert(nneeded>0, "PCATruncatedSubspaceSparse: nneeded<1"); - alglib.ap.assert(nneeded<=nvars, "PCATruncatedSubspaceSparse: nneeded>nvars"); - alglib.ap.assert(maxits>=0, "PCATruncatedSubspaceSparse: maxits<0"); - alglib.ap.assert(math.isfinite(eps) && (double)(eps)>=(double)(0), "PCATruncatedSubspaceSparse: eps<0 or is not finite"); - if( npoints>0 ) - { - alglib.ap.assert(sparse.sparsegetnrows(x, _params)==npoints, "PCATruncatedSubspaceSparse: rows(x)!=npoints"); - alglib.ap.assert(sparse.sparsegetncols(x, _params)==nvars, "PCATruncatedSubspaceSparse: cols(x)!=nvars"); - } - - // - // Special case: NPoints=0 - // - if( npoints==0 ) - { - s2 = new double[nneeded]; - v = new double[nvars, nneeded]; - for(i=0; i<=nvars-1; i++) - { - s2[i] = 0; - } - for(i=0; i<=nvars-1; i++) - { - for(j=0; j<=nneeded-1; j++) - { - if( i==j ) - { - v[i,j] = 1; - } - else - { - v[i,j] = 0; - } - } - } - return; - } - - // - // If input data are not in CRS format, perform conversion to CRS - // - if( !sparse.sparseiscrs(x, _params) ) - { - sparse.sparsecopytocrs(x, xcrs, _params); - pcatruncatedsubspacesparse(xcrs, npoints, nvars, nneeded, eps, maxits, ref s2, ref v, _params); - return; - } - - // - // Initialize parameters, prepare buffers - // - b1 = new double[npoints]; - z1 = new double[nvars]; - if( (double)(eps)==(double)(0) && maxits==0 ) - { - eps = 1.0E-6; - } - if( maxits==0 ) - { - maxits = 50+2*nvars; - } - - // - // Calculate mean values - // - vv = (double)1/(double)npoints; - for(i=0; i<=npoints-1; i++) - { - b1[i] = vv; - } - sparse.sparsemtv(x, b1, ref means, _params); - - // - // Find eigenvalues with subspace iteration solver - // - evd.eigsubspacecreate(nvars, nneeded, solver, _params); - evd.eigsubspacesetcond(solver, eps, maxits, _params); - evd.eigsubspaceoocstart(solver, 0, _params); - while( evd.eigsubspaceooccontinue(solver, _params) ) - { - alglib.ap.assert(solver.requesttype==0, "PCATruncatedSubspace: integrity check failed"); - for(k=0; k<=solver.requestsize-1; k++) - { - - // - // Calculate B1=(X-meansX)*Zk - // - for(i_=0; i_<=nvars-1;i_++) - { - z1[i_] = solver.x[i_,k]; - } - sparse.sparsemv(x, z1, ref b1, _params); - vv = 0.0; - for(i_=0; i_<=nvars-1;i_++) - { - vv += solver.x[i_,k]*means[i_]; - } - for(i=0; i<=npoints-1; i++) - { - b1[i] = b1[i]-vv; - } - - // - // Calculate (X-meansX)^T*B1 - // - sparse.sparsemtv(x, b1, ref c1, _params); - vv = 0; - for(i=0; i<=npoints-1; i++) - { - vv = vv+b1[i]; - } - for(j=0; j<=nvars-1; j++) - { - solver.ax[j,k] = c1[j]-vv*means[j]; - } - } - } - evd.eigsubspaceoocstop(solver, ref s2, ref v, rep, _params); - if( npoints!=1 ) - { - for(i=0; i<=nneeded-1; i++) - { - s2[i] = s2[i]/(npoints-1); - } - } - } - - - } - public class bdss - { - public class cvreport : apobject - { - public double relclserror; - public double avgce; - public double rmserror; - public double avgerror; - public double avgrelerror; - public cvreport() - { - init(); - } - public override void init() - { - } - public override alglib.apobject make_copy() - { - cvreport _result = new cvreport(); - _result.relclserror = relclserror; - _result.avgce = avgce; - _result.rmserror = rmserror; - _result.avgerror = avgerror; - _result.avgrelerror = avgrelerror; - return _result; - } - }; - - - - - /************************************************************************* - This set of routines (DSErrAllocate, DSErrAccumulate, DSErrFinish) - calculates different error functions (classification error, cross-entropy, - rms, avg, avg.rel errors). - - 1. DSErrAllocate prepares buffer. - 2. DSErrAccumulate accumulates individual errors: - * Y contains predicted output (posterior probabilities for classification) - * DesiredY contains desired output (class number for classification) - 3. DSErrFinish outputs results: - * Buf[0] contains relative classification error (zero for regression tasks) - * Buf[1] contains avg. cross-entropy (zero for regression tasks) - * Buf[2] contains rms error (regression, classification) - * Buf[3] contains average error (regression, classification) - * Buf[4] contains average relative error (regression, classification) - - NOTES(1): - "NClasses>0" means that we have classification task. - "NClasses<0" means regression task with -NClasses real outputs. - - NOTES(2): - rms. avg, avg.rel errors for classification tasks are interpreted as - errors in posterior probabilities with respect to probabilities given - by training/test set. - - -- ALGLIB -- - Copyright 11.01.2009 by Bochkanov Sergey - *************************************************************************/ - public static void dserrallocate(int nclasses, - ref double[] buf, - alglib.xparams _params) - { - buf = new double[0]; - - buf = new double[7+1]; - buf[0] = 0; - buf[1] = 0; - buf[2] = 0; - buf[3] = 0; - buf[4] = 0; - buf[5] = nclasses; - buf[6] = 0; - buf[7] = 0; - } - - - /************************************************************************* - See DSErrAllocate for comments on this routine. - - -- ALGLIB -- - Copyright 11.01.2009 by Bochkanov Sergey - *************************************************************************/ - public static void dserraccumulate(ref double[] buf, - double[] y, - double[] desiredy, - alglib.xparams _params) - { - int nclasses = 0; - int nout = 0; - int offs = 0; - int mmax = 0; - int rmax = 0; - int j = 0; - double v = 0; - double ev = 0; - - offs = 5; - nclasses = (int)Math.Round(buf[offs]); - if( nclasses>0 ) - { - - // - // Classification - // - rmax = (int)Math.Round(desiredy[0]); - mmax = 0; - for(j=1; j<=nclasses-1; j++) - { - if( (double)(y[j])>(double)(y[mmax]) ) - { - mmax = j; - } - } - if( mmax!=rmax ) - { - buf[0] = buf[0]+1; - } - if( (double)(y[rmax])>(double)(0) ) - { - buf[1] = buf[1]-Math.Log(y[rmax]); - } - else - { - buf[1] = buf[1]+Math.Log(math.maxrealnumber); - } - for(j=0; j<=nclasses-1; j++) - { - v = y[j]; - if( j==rmax ) - { - ev = 1; - } - else - { - ev = 0; - } - buf[2] = buf[2]+math.sqr(v-ev); - buf[3] = buf[3]+Math.Abs(v-ev); - if( (double)(ev)!=(double)(0) ) - { - buf[4] = buf[4]+Math.Abs((v-ev)/ev); - buf[offs+2] = buf[offs+2]+1; - } - } - buf[offs+1] = buf[offs+1]+1; - } - else - { - - // - // Regression - // - nout = -nclasses; - rmax = 0; - for(j=1; j<=nout-1; j++) - { - if( (double)(desiredy[j])>(double)(desiredy[rmax]) ) - { - rmax = j; - } - } - mmax = 0; - for(j=1; j<=nout-1; j++) - { - if( (double)(y[j])>(double)(y[mmax]) ) - { - mmax = j; - } - } - if( mmax!=rmax ) - { - buf[0] = buf[0]+1; - } - for(j=0; j<=nout-1; j++) - { - v = y[j]; - ev = desiredy[j]; - buf[2] = buf[2]+math.sqr(v-ev); - buf[3] = buf[3]+Math.Abs(v-ev); - if( (double)(ev)!=(double)(0) ) - { - buf[4] = buf[4]+Math.Abs((v-ev)/ev); - buf[offs+2] = buf[offs+2]+1; - } - } - buf[offs+1] = buf[offs+1]+1; - } - } - - - /************************************************************************* - See DSErrAllocate for comments on this routine. - - -- ALGLIB -- - Copyright 11.01.2009 by Bochkanov Sergey - *************************************************************************/ - public static void dserrfinish(ref double[] buf, - alglib.xparams _params) - { - int nout = 0; - int offs = 0; - - offs = 5; - nout = Math.Abs((int)Math.Round(buf[offs])); - if( (double)(buf[offs+1])!=(double)(0) ) - { - buf[0] = buf[0]/buf[offs+1]; - buf[1] = buf[1]/buf[offs+1]; - buf[2] = Math.Sqrt(buf[2]/(nout*buf[offs+1])); - buf[3] = buf[3]/(nout*buf[offs+1]); - } - if( (double)(buf[offs+2])!=(double)(0) ) - { - buf[4] = buf[4]/buf[offs+2]; - } - } - - - /************************************************************************* - - -- ALGLIB -- - Copyright 19.05.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dsnormalize(ref double[,] xy, - int npoints, - int nvars, - ref int info, - ref double[] means, - ref double[] sigmas, - alglib.xparams _params) - { - int i = 0; - int j = 0; - double[] tmp = new double[0]; - double mean = 0; - double variance = 0; - double skewness = 0; - double kurtosis = 0; - int i_ = 0; - - info = 0; - means = new double[0]; - sigmas = new double[0]; - - - // - // Test parameters - // - if( npoints<=0 || nvars<1 ) - { - info = -1; - return; - } - info = 1; - - // - // Standartization - // - means = new double[nvars-1+1]; - sigmas = new double[nvars-1+1]; - tmp = new double[npoints-1+1]; - for(j=0; j<=nvars-1; j++) - { - for(i_=0; i_<=npoints-1;i_++) - { - tmp[i_] = xy[i_,j]; - } - basestat.samplemoments(tmp, npoints, ref mean, ref variance, ref skewness, ref kurtosis, _params); - means[j] = mean; - sigmas[j] = Math.Sqrt(variance); - if( (double)(sigmas[j])==(double)(0) ) - { - sigmas[j] = 1; - } - for(i=0; i<=npoints-1; i++) - { - xy[i,j] = (xy[i,j]-means[j])/sigmas[j]; - } - } - } - - - /************************************************************************* - - -- ALGLIB -- - Copyright 19.05.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dsnormalizec(double[,] xy, - int npoints, - int nvars, - ref int info, - ref double[] means, - ref double[] sigmas, - alglib.xparams _params) - { - int j = 0; - double[] tmp = new double[0]; - double mean = 0; - double variance = 0; - double skewness = 0; - double kurtosis = 0; - int i_ = 0; - - info = 0; - means = new double[0]; - sigmas = new double[0]; - - - // - // Test parameters - // - if( npoints<=0 || nvars<1 ) - { - info = -1; - return; - } - info = 1; - - // - // Standartization - // - means = new double[nvars-1+1]; - sigmas = new double[nvars-1+1]; - tmp = new double[npoints-1+1]; - for(j=0; j<=nvars-1; j++) - { - for(i_=0; i_<=npoints-1;i_++) - { - tmp[i_] = xy[i_,j]; - } - basestat.samplemoments(tmp, npoints, ref mean, ref variance, ref skewness, ref kurtosis, _params); - means[j] = mean; - sigmas[j] = Math.Sqrt(variance); - if( (double)(sigmas[j])==(double)(0) ) - { - sigmas[j] = 1; - } - } - } - - - /************************************************************************* - - -- ALGLIB -- - Copyright 19.05.2008 by Bochkanov Sergey - *************************************************************************/ - public static double dsgetmeanmindistance(double[,] xy, - int npoints, - int nvars, - alglib.xparams _params) - { - double result = 0; - int i = 0; - int j = 0; - double[] tmp = new double[0]; - double[] tmp2 = new double[0]; - double v = 0; - int i_ = 0; - - - // - // Test parameters - // - if( npoints<=0 || nvars<1 ) - { - result = 0; - return result; - } - - // - // Process - // - tmp = new double[npoints-1+1]; - for(i=0; i<=npoints-1; i++) - { - tmp[i] = math.maxrealnumber; - } - tmp2 = new double[nvars-1+1]; - for(i=0; i<=npoints-1; i++) - { - for(j=i+1; j<=npoints-1; j++) - { - for(i_=0; i_<=nvars-1;i_++) - { - tmp2[i_] = xy[i,i_]; - } - for(i_=0; i_<=nvars-1;i_++) - { - tmp2[i_] = tmp2[i_] - xy[j,i_]; - } - v = 0.0; - for(i_=0; i_<=nvars-1;i_++) - { - v += tmp2[i_]*tmp2[i_]; - } - v = Math.Sqrt(v); - tmp[i] = Math.Min(tmp[i], v); - tmp[j] = Math.Min(tmp[j], v); - } - } - result = 0; - for(i=0; i<=npoints-1; i++) - { - result = result+tmp[i]/npoints; - } - return result; - } - - - /************************************************************************* - - -- ALGLIB -- - Copyright 19.05.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dstie(ref double[] a, - int n, - ref int[] ties, - ref int tiecount, - ref int[] p1, - ref int[] p2, - alglib.xparams _params) - { - int i = 0; - int k = 0; - int[] tmp = new int[0]; - - ties = new int[0]; - tiecount = 0; - p1 = new int[0]; - p2 = new int[0]; - - - // - // Special case - // - if( n<=0 ) - { - tiecount = 0; - return; - } - - // - // Sort A - // - tsort.tagsort(ref a, n, ref p1, ref p2, _params); - - // - // Process ties - // - tiecount = 1; - for(i=1; i<=n-1; i++) - { - if( (double)(a[i])!=(double)(a[i-1]) ) - { - tiecount = tiecount+1; - } - } - ties = new int[tiecount+1]; - ties[0] = 0; - k = 1; - for(i=1; i<=n-1; i++) - { - if( (double)(a[i])!=(double)(a[i-1]) ) - { - ties[k] = i; - k = k+1; - } - } - ties[tiecount] = n; - } - - - /************************************************************************* - - -- ALGLIB -- - Copyright 11.12.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dstiefasti(ref double[] a, - ref int[] b, - int n, - ref int[] ties, - ref int tiecount, - ref double[] bufr, - ref int[] bufi, - alglib.xparams _params) - { - int i = 0; - int k = 0; - int[] tmp = new int[0]; - - tiecount = 0; - - - // - // Special case - // - if( n<=0 ) - { - tiecount = 0; - return; - } - - // - // Sort A - // - tsort.tagsortfasti(ref a, ref b, ref bufr, ref bufi, n, _params); - - // - // Process ties - // - ties[0] = 0; - k = 1; - for(i=1; i<=n-1; i++) - { - if( (double)(a[i])!=(double)(a[i-1]) ) - { - ties[k] = i; - k = k+1; - } - } - ties[k] = n; - tiecount = k; - } - - - /************************************************************************* - Optimal binary classification - - Algorithms finds optimal (=with minimal cross-entropy) binary partition. - Internal subroutine. - - INPUT PARAMETERS: - A - array[0..N-1], variable - C - array[0..N-1], class numbers (0 or 1). - N - array size - - OUTPUT PARAMETERS: - Info - completetion code: - * -3, all values of A[] are same (partition is impossible) - * -2, one of C[] is incorrect (<0, >1) - * -1, incorrect pararemets were passed (N<=0). - * 1, OK - Threshold- partiton boundary. Left part contains values which are - strictly less than Threshold. Right part contains values - which are greater than or equal to Threshold. - PAL, PBL- probabilities P(0|v=Threshold) and P(1|v>=Threshold) - CVE - cross-validation estimate of cross-entropy - - -- ALGLIB -- - Copyright 22.05.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dsoptimalsplit2(double[] a, - int[] c, - int n, - ref int info, - ref double threshold, - ref double pal, - ref double pbl, - ref double par, - ref double pbr, - ref double cve, - alglib.xparams _params) - { - int i = 0; - int t = 0; - double s = 0; - int[] ties = new int[0]; - int tiecount = 0; - int[] p1 = new int[0]; - int[] p2 = new int[0]; - int k = 0; - int koptimal = 0; - double pak = 0; - double pbk = 0; - double cvoptimal = 0; - double cv = 0; - - a = (double[])a.Clone(); - c = (int[])c.Clone(); - info = 0; - threshold = 0; - pal = 0; - pbl = 0; - par = 0; - pbr = 0; - cve = 0; - - - // - // Test for errors in inputs - // - if( n<=0 ) - { - info = -1; - return; - } - for(i=0; i<=n-1; i++) - { - if( c[i]!=0 && c[i]!=1 ) - { - info = -2; - return; - } - } - info = 1; - - // - // Tie - // - dstie(ref a, n, ref ties, ref tiecount, ref p1, ref p2, _params); - for(i=0; i<=n-1; i++) - { - if( p2[i]!=i ) - { - t = c[i]; - c[i] = c[p2[i]]; - c[p2[i]] = t; - } - } - - // - // Special case: number of ties is 1. - // - // NOTE: we assume that P[i,j] equals to 0 or 1, - // intermediate values are not allowed. - // - if( tiecount==1 ) - { - info = -3; - return; - } - - // - // General case, number of ties > 1 - // - // NOTE: we assume that P[i,j] equals to 0 or 1, - // intermediate values are not allowed. - // - pal = 0; - pbl = 0; - par = 0; - pbr = 0; - for(i=0; i<=n-1; i++) - { - if( c[i]==0 ) - { - par = par+1; - } - if( c[i]==1 ) - { - pbr = pbr+1; - } - } - koptimal = -1; - cvoptimal = math.maxrealnumber; - for(k=0; k<=tiecount-2; k++) - { - - // - // first, obtain information about K-th tie which is - // moved from R-part to L-part - // - pak = 0; - pbk = 0; - for(i=ties[k]; i<=ties[k+1]-1; i++) - { - if( c[i]==0 ) - { - pak = pak+1; - } - if( c[i]==1 ) - { - pbk = pbk+1; - } - } - - // - // Calculate cross-validation CE - // - cv = 0; - cv = cv-xlny(pal+pak, (pal+pak)/(pal+pak+pbl+pbk+1), _params); - cv = cv-xlny(pbl+pbk, (pbl+pbk)/(pal+pak+1+pbl+pbk), _params); - cv = cv-xlny(par-pak, (par-pak)/(par-pak+pbr-pbk+1), _params); - cv = cv-xlny(pbr-pbk, (pbr-pbk)/(par-pak+1+pbr-pbk), _params); - - // - // Compare with best - // - if( (double)(cv)<(double)(cvoptimal) ) - { - cvoptimal = cv; - koptimal = k; - } - - // - // update - // - pal = pal+pak; - pbl = pbl+pbk; - par = par-pak; - pbr = pbr-pbk; - } - cve = cvoptimal; - threshold = 0.5*(a[ties[koptimal]]+a[ties[koptimal+1]]); - pal = 0; - pbl = 0; - par = 0; - pbr = 0; - for(i=0; i<=n-1; i++) - { - if( (double)(a[i])<(double)(threshold) ) - { - if( c[i]==0 ) - { - pal = pal+1; - } - else - { - pbl = pbl+1; - } - } - else - { - if( c[i]==0 ) - { - par = par+1; - } - else - { - pbr = pbr+1; - } - } - } - s = pal+pbl; - pal = pal/s; - pbl = pbl/s; - s = par+pbr; - par = par/s; - pbr = pbr/s; - } - - - /************************************************************************* - Optimal partition, internal subroutine. Fast version. - - Accepts: - A array[0..N-1] array of attributes array[0..N-1] - C array[0..N-1] array of class labels - TiesBuf array[0..N] temporaries (ties) - CntBuf array[0..2*NC-1] temporaries (counts) - Alpha centering factor (0<=alpha<=1, recommended value - 0.05) - BufR array[0..N-1] temporaries - BufI array[0..N-1] temporaries - - Output: - Info error code (">0"=OK, "<0"=bad) - RMS training set RMS error - CVRMS leave-one-out RMS error - - Note: - content of all arrays is changed by subroutine; - it doesn't allocate temporaries. - - -- ALGLIB -- - Copyright 11.12.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dsoptimalsplit2fast(ref double[] a, - ref int[] c, - ref int[] tiesbuf, - ref int[] cntbuf, - ref double[] bufr, - ref int[] bufi, - int n, - int nc, - double alpha, - ref int info, - ref double threshold, - ref double rms, - ref double cvrms, - alglib.xparams _params) - { - int i = 0; - int k = 0; - int cl = 0; - int tiecount = 0; - double cbest = 0; - double cc = 0; - int koptimal = 0; - int sl = 0; - int sr = 0; - double v = 0; - double w = 0; - double x = 0; - - info = 0; - threshold = 0; - rms = 0; - cvrms = 0; - - - // - // Test for errors in inputs - // - if( n<=0 || nc<2 ) - { - info = -1; - return; - } - for(i=0; i<=n-1; i++) - { - if( c[i]<0 || c[i]>=nc ) - { - info = -2; - return; - } - } - info = 1; - - // - // Tie - // - dstiefasti(ref a, ref c, n, ref tiesbuf, ref tiecount, ref bufr, ref bufi, _params); - - // - // Special case: number of ties is 1. - // - if( tiecount==1 ) - { - info = -3; - return; - } - - // - // General case, number of ties > 1 - // - for(i=0; i<=2*nc-1; i++) - { - cntbuf[i] = 0; - } - for(i=0; i<=n-1; i++) - { - cntbuf[nc+c[i]] = cntbuf[nc+c[i]]+1; - } - koptimal = -1; - threshold = a[n-1]; - cbest = math.maxrealnumber; - sl = 0; - sr = n; - for(k=0; k<=tiecount-2; k++) - { - - // - // first, move Kth tie from right to left - // - for(i=tiesbuf[k]; i<=tiesbuf[k+1]-1; i++) - { - cl = c[i]; - cntbuf[cl] = cntbuf[cl]+1; - cntbuf[nc+cl] = cntbuf[nc+cl]-1; - } - sl = sl+(tiesbuf[k+1]-tiesbuf[k]); - sr = sr-(tiesbuf[k+1]-tiesbuf[k]); - - // - // Calculate RMS error - // - v = 0; - for(i=0; i<=nc-1; i++) - { - w = cntbuf[i]; - v = v+w*math.sqr(w/sl-1); - v = v+(sl-w)*math.sqr(w/sl); - w = cntbuf[nc+i]; - v = v+w*math.sqr(w/sr-1); - v = v+(sr-w)*math.sqr(w/sr); - } - v = Math.Sqrt(v/(nc*n)); - - // - // Compare with best - // - x = (double)(2*sl)/(double)(sl+sr)-1; - cc = v*(1-alpha+alpha*math.sqr(x)); - if( (double)(cc)<(double)(cbest) ) - { - - // - // store split - // - rms = v; - koptimal = k; - cbest = cc; - - // - // calculate CVRMS error - // - cvrms = 0; - for(i=0; i<=nc-1; i++) - { - if( sl>1 ) - { - w = cntbuf[i]; - cvrms = cvrms+w*math.sqr((w-1)/(sl-1)-1); - cvrms = cvrms+(sl-w)*math.sqr(w/(sl-1)); - } - else - { - w = cntbuf[i]; - cvrms = cvrms+w*math.sqr((double)1/(double)nc-1); - cvrms = cvrms+(sl-w)*math.sqr((double)1/(double)nc); - } - if( sr>1 ) - { - w = cntbuf[nc+i]; - cvrms = cvrms+w*math.sqr((w-1)/(sr-1)-1); - cvrms = cvrms+(sr-w)*math.sqr(w/(sr-1)); - } - else - { - w = cntbuf[nc+i]; - cvrms = cvrms+w*math.sqr((double)1/(double)nc-1); - cvrms = cvrms+(sr-w)*math.sqr((double)1/(double)nc); - } - } - cvrms = Math.Sqrt(cvrms/(nc*n)); - } - } - - // - // Calculate threshold. - // Code is a bit complicated because there can be such - // numbers that 0.5(A+B) equals to A or B (if A-B=epsilon) - // - threshold = 0.5*(a[tiesbuf[koptimal]]+a[tiesbuf[koptimal+1]]); - if( (double)(threshold)<=(double)(a[tiesbuf[koptimal]]) ) - { - threshold = a[tiesbuf[koptimal+1]]; - } - } - - - /************************************************************************* - Automatic non-optimal discretization, internal subroutine. - - -- ALGLIB -- - Copyright 22.05.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dssplitk(double[] a, - int[] c, - int n, - int nc, - int kmax, - ref int info, - ref double[] thresholds, - ref int ni, - ref double cve, - alglib.xparams _params) - { - int i = 0; - int j = 0; - int j1 = 0; - int k = 0; - int[] ties = new int[0]; - int tiecount = 0; - int[] p1 = new int[0]; - int[] p2 = new int[0]; - int[] cnt = new int[0]; - double v2 = 0; - int bestk = 0; - double bestcve = 0; - int[] bestsizes = new int[0]; - double curcve = 0; - int[] cursizes = new int[0]; - - a = (double[])a.Clone(); - c = (int[])c.Clone(); - info = 0; - thresholds = new double[0]; - ni = 0; - cve = 0; - - - // - // Test for errors in inputs - // - if( (n<=0 || nc<2) || kmax<2 ) - { - info = -1; - return; - } - for(i=0; i<=n-1; i++) - { - if( c[i]<0 || c[i]>=nc ) - { - info = -2; - return; - } - } - info = 1; - - // - // Tie - // - dstie(ref a, n, ref ties, ref tiecount, ref p1, ref p2, _params); - for(i=0; i<=n-1; i++) - { - if( p2[i]!=i ) - { - k = c[i]; - c[i] = c[p2[i]]; - c[p2[i]] = k; - } - } - - // - // Special cases - // - if( tiecount==1 ) - { - info = -3; - return; - } - - // - // General case: - // 0. allocate arrays - // - kmax = Math.Min(kmax, tiecount); - bestsizes = new int[kmax-1+1]; - cursizes = new int[kmax-1+1]; - cnt = new int[nc-1+1]; - - // - // General case: - // 1. prepare "weak" solution (two subintervals, divided at median) - // - v2 = math.maxrealnumber; - j = -1; - for(i=1; i<=tiecount-1; i++) - { - if( (double)(Math.Abs(ties[i]-0.5*(n-1)))<(double)(v2) ) - { - v2 = Math.Abs(ties[i]-0.5*n); - j = i; - } - } - alglib.ap.assert(j>0, "DSSplitK: internal error #1!"); - bestk = 2; - bestsizes[0] = ties[j]; - bestsizes[1] = n-j; - bestcve = 0; - for(i=0; i<=nc-1; i++) - { - cnt[i] = 0; - } - for(i=0; i<=j-1; i++) - { - tieaddc(c, ties, i, nc, ref cnt, _params); - } - bestcve = bestcve+getcv(cnt, nc, _params); - for(i=0; i<=nc-1; i++) - { - cnt[i] = 0; - } - for(i=j; i<=tiecount-1; i++) - { - tieaddc(c, ties, i, nc, ref cnt, _params); - } - bestcve = bestcve+getcv(cnt, nc, _params); - - // - // General case: - // 2. Use greedy algorithm to find sub-optimal split in O(KMax*N) time - // - for(k=2; k<=kmax; k++) - { - - // - // Prepare greedy K-interval split - // - for(i=0; i<=k-1; i++) - { - cursizes[i] = 0; - } - i = 0; - j = 0; - while( j<=tiecount-1 && i<=k-1 ) - { - - // - // Rule: I-th bin is empty, fill it - // - if( cursizes[i]==0 ) - { - cursizes[i] = ties[j+1]-ties[j]; - j = j+1; - continue; - } - - // - // Rule: (K-1-I) bins left, (K-1-I) ties left (1 tie per bin); next bin - // - if( tiecount-j==k-1-i ) - { - i = i+1; - continue; - } - - // - // Rule: last bin, always place in current - // - if( i==k-1 ) - { - cursizes[i] = cursizes[i]+ties[j+1]-ties[j]; - j = j+1; - continue; - } - - // - // Place J-th tie in I-th bin, or leave for I+1-th bin. - // - if( (double)(Math.Abs(cursizes[i]+ties[j+1]-ties[j]-(double)n/(double)k))<(double)(Math.Abs(cursizes[i]-(double)n/(double)k)) ) - { - cursizes[i] = cursizes[i]+ties[j+1]-ties[j]; - j = j+1; - } - else - { - i = i+1; - } - } - alglib.ap.assert(cursizes[k-1]!=0 && j==tiecount, "DSSplitK: internal error #1"); - - // - // Calculate CVE - // - curcve = 0; - j = 0; - for(i=0; i<=k-1; i++) - { - for(j1=0; j1<=nc-1; j1++) - { - cnt[j1] = 0; - } - for(j1=j; j1<=j+cursizes[i]-1; j1++) - { - cnt[c[j1]] = cnt[c[j1]]+1; - } - curcve = curcve+getcv(cnt, nc, _params); - j = j+cursizes[i]; - } - - // - // Choose best variant - // - if( (double)(curcve)<(double)(bestcve) ) - { - for(i=0; i<=k-1; i++) - { - bestsizes[i] = cursizes[i]; - } - bestcve = curcve; - bestk = k; - } - } - - // - // Transform from sizes to thresholds - // - cve = bestcve; - ni = bestk; - thresholds = new double[ni-2+1]; - j = bestsizes[0]; - for(i=1; i<=bestk-1; i++) - { - thresholds[i-1] = 0.5*(a[j-1]+a[j]); - j = j+bestsizes[i]; - } - } - - - /************************************************************************* - Automatic optimal discretization, internal subroutine. - - -- ALGLIB -- - Copyright 22.05.2008 by Bochkanov Sergey - *************************************************************************/ - public static void dsoptimalsplitk(double[] a, - int[] c, - int n, - int nc, - int kmax, - ref int info, - ref double[] thresholds, - ref int ni, - ref double cve, - alglib.xparams _params) - { - int i = 0; - int j = 0; - int s = 0; - int jl = 0; - int jr = 0; - double v2 = 0; - int[] ties = new int[0]; - int tiecount = 0; - int[] p1 = new int[0]; - int[] p2 = new int[0]; - double cvtemp = 0; - int[] cnt = new int[0]; - int[] cnt2 = new int[0]; - double[,] cv = new double[0,0]; - int[,] splits = new int[0,0]; - int k = 0; - int koptimal = 0; - double cvoptimal = 0; - - a = (double[])a.Clone(); - c = (int[])c.Clone(); - info = 0; - thresholds = new double[0]; - ni = 0; - cve = 0; - - - // - // Test for errors in inputs - // - if( (n<=0 || nc<2) || kmax<2 ) - { - info = -1; - return; - } - for(i=0; i<=n-1; i++) - { - if( c[i]<0 || c[i]>=nc ) - { - info = -2; - return; - } - } - info = 1; - - // - // Tie - // - dstie(ref a, n, ref ties, ref tiecount, ref p1, ref p2, _params); - for(i=0; i<=n-1; i++) - { - if( p2[i]!=i ) - { - k = c[i]; - c[i] = c[p2[i]]; - c[p2[i]] = k; - } - } - - // - // Special cases - // - if( tiecount==1 ) - { - info = -3; - return; - } - - // - // General case - // Use dynamic programming to find best split in O(KMax*NC*TieCount^2) time - // - kmax = Math.Min(kmax, tiecount); - cv = new double[kmax-1+1, tiecount-1+1]; - splits = new int[kmax-1+1, tiecount-1+1]; - cnt = new int[nc-1+1]; - cnt2 = new int[nc-1+1]; - for(j=0; j<=nc-1; j++) - { - cnt[j] = 0; - } - for(j=0; j<=tiecount-1; j++) - { - tieaddc(c, ties, j, nc, ref cnt, _params); - splits[0,j] = 0; - cv[0,j] = getcv(cnt, nc, _params); - } - for(k=1; k<=kmax-1; k++) - { - for(j=0; j<=nc-1; j++) - { - cnt[j] = 0; - } - - // - // Subtask size J in [K..TieCount-1]: - // optimal K-splitting on ties from 0-th to J-th. - // - for(j=k; j<=tiecount-1; j++) - { - - // - // Update Cnt - let it contain classes of ties from K-th to J-th - // - tieaddc(c, ties, j, nc, ref cnt, _params); - - // - // Search for optimal split point S in [K..J] - // - for(i=0; i<=nc-1; i++) - { - cnt2[i] = cnt[i]; - } - cv[k,j] = cv[k-1,j-1]+getcv(cnt2, nc, _params); - splits[k,j] = j; - for(s=k+1; s<=j; s++) - { - - // - // Update Cnt2 - let it contain classes of ties from S-th to J-th - // - tiesubc(c, ties, s-1, nc, ref cnt2, _params); - - // - // Calculate CVE - // - cvtemp = cv[k-1,s-1]+getcv(cnt2, nc, _params); - if( (double)(cvtemp)<(double)(cv[k,j]) ) - { - cv[k,j] = cvtemp; - splits[k,j] = s; - } - } - } - } - - // - // Choose best partition, output result - // - koptimal = -1; - cvoptimal = math.maxrealnumber; - for(k=0; k<=kmax-1; k++) - { - if( (double)(cv[k,tiecount-1])<(double)(cvoptimal) ) - { - cvoptimal = cv[k,tiecount-1]; - koptimal = k; - } - } - alglib.ap.assert(koptimal>=0, "DSOptimalSplitK: internal error #1!"); - if( koptimal==0 ) - { - - // - // Special case: best partition is one big interval. - // Even 2-partition is not better. - // This is possible when dealing with "weak" predictor variables. - // - // Make binary split as close to the median as possible. - // - v2 = math.maxrealnumber; - j = -1; - for(i=1; i<=tiecount-1; i++) - { - if( (double)(Math.Abs(ties[i]-0.5*(n-1)))<(double)(v2) ) - { - v2 = Math.Abs(ties[i]-0.5*(n-1)); - j = i; - } - } - alglib.ap.assert(j>0, "DSOptimalSplitK: internal error #2!"); - thresholds = new double[0+1]; - thresholds[0] = 0.5*(a[ties[j-1]]+a[ties[j]]); - ni = 2; - cve = 0; - for(i=0; i<=nc-1; i++) - { - cnt[i] = 0; - } - for(i=0; i<=j-1; i++) - { - tieaddc(c, ties, i, nc, ref cnt, _params); - } - cve = cve+getcv(cnt, nc, _params); - for(i=0; i<=nc-1; i++) - { - cnt[i] = 0; - } - for(i=j; i<=tiecount-1; i++) - { - tieaddc(c, ties, i, nc, ref cnt, _params); - } - cve = cve+getcv(cnt, nc, _params); - } - else - { - - // - // General case: 2 or more intervals - // - // NOTE: we initialize both JL and JR (left and right bounds), - // altough algorithm needs only JL. - // - thresholds = new double[koptimal-1+1]; - ni = koptimal+1; - cve = cv[koptimal,tiecount-1]; - jl = splits[koptimal,tiecount-1]; - jr = tiecount-1; - for(k=koptimal; k>=1; k--) - { - thresholds[k-1] = 0.5*(a[ties[jl-1]]+a[ties[jl]]); - jr = jl-1; - jl = splits[k-1,jl-1]; - } - apserv.touchint(ref jr, _params); - } - } - - - /************************************************************************* - Internal function - *************************************************************************/ - private static double xlny(double x, - double y, - alglib.xparams _params) - { - double result = 0; - - if( (double)(x)==(double)(0) ) - { - result = 0; - } - else - { - result = x*Math.Log(y); - } - return result; - } - - - /************************************************************************* - Internal function, - returns number of samples of class I in Cnt[I] - *************************************************************************/ - private static double getcv(int[] cnt, - int nc, - alglib.xparams _params) - { - double result = 0; - int i = 0; - double s = 0; - - s = 0; - for(i=0; i<=nc-1; i++) - { - s = s+cnt[i]; - } - result = 0; - for(i=0; i<=nc-1; i++) - { - result = result-xlny(cnt[i], cnt[i]/(s+nc-1), _params); - } - return result; - } - - - /************************************************************************* - Internal function, adds number of samples of class I in tie NTie to Cnt[I] - *************************************************************************/ - private static void tieaddc(int[] c, - int[] ties, - int ntie, - int nc, - ref int[] cnt, - alglib.xparams _params) - { - int i = 0; - - for(i=ties[ntie]; i<=ties[ntie+1]-1; i++) - { - cnt[c[i]] = cnt[c[i]]+1; - } - } - - - /************************************************************************* - Internal function, subtracts number of samples of class I in tie NTie to Cnt[I] - *************************************************************************/ - private static void tiesubc(int[] c, - int[] ties, - int ntie, - int nc, - ref int[] cnt, - alglib.xparams _params) - { - int i = 0; - - for(i=ties[ntie]; i<=ties[ntie+1]-1; i++) - { - cnt[c[i]] = cnt[c[i]]-1; - } - } - - - } - public class mlpbase - { - /************************************************************************* - Model's errors: - * RelCLSError - fraction of misclassified cases. - * AvgCE - acerage cross-entropy - * RMSError - root-mean-square error - * AvgError - average error - * AvgRelError - average relative error - - NOTE 1: RelCLSError/AvgCE are zero on regression problems. - - NOTE 2: on classification problems RMSError/AvgError/AvgRelError contain - errors in prediction of posterior probabilities - *************************************************************************/ - public class modelerrors : apobject - { - public double relclserror; - public double avgce; - public double rmserror; - public double avgerror; - public double avgrelerror; - public modelerrors() - { - init(); - } - public override void init() - { - } - public override alglib.apobject make_copy() - { - modelerrors _result = new modelerrors(); - _result.relclserror = relclserror; - _result.avgce = avgce; - _result.rmserror = rmserror; - _result.avgerror = avgerror; - _result.avgrelerror = avgrelerror; - return _result; - } - }; - - - /************************************************************************* - This structure is used to store MLP error and gradient. - *************************************************************************/ - public class smlpgrad : apobject - { - public double f; - public double[] g; - public smlpgrad() - { - init(); - } - public override void init() - { - g = new double[0]; - } - public override alglib.apobject make_copy() - { - smlpgrad _result = new smlpgrad(); - _result.f = f; - _result.g = (double[])g.Clone(); - return _result; - } - }; - - - public class multilayerperceptron : apobject - { - public int hlnetworktype; - public int hlnormtype; - public int[] hllayersizes; - public int[] hlconnections; - public int[] hlneurons; - public int[] structinfo; - public double[] weights; - public double[] columnmeans; - public double[] columnsigmas; - public double[] neurons; - public double[] dfdnet; - public double[] derror; - public double[] x; - public double[] y; - public double[,] xy; - public double[] xyrow; - public double[] nwbuf; - public int[] integerbuf; - public modelerrors err; - public double[] rndbuf; - public alglib.smp.shared_pool buf; - public alglib.smp.shared_pool gradbuf; - public double[,] dummydxy; - public sparse.sparsematrix dummysxy; - public int[] dummyidx; - public alglib.smp.shared_pool dummypool; - public multilayerperceptron() - { - init(); - } - public override void init() - { - hllayersizes = new int[0]; - hlconnections = new int[0]; - hlneurons = new int[0]; - structinfo = new int[0]; - weights = new double[0]; - columnmeans = new double[0]; - columnsigmas = new double[0]; - neurons = new double[0]; - dfdnet = new double[0]; - derror = new double[0]; - x = new double[0]; - y = new double[0]; - xy = new double[0,0]; - xyrow = new double[0]; - nwbuf = new double[0]; - integerbuf = new int[0]; - err = new modelerrors(); - rndbuf = new double[0]; - buf = new alglib.smp.shared_pool(); - gradbuf = new alglib.smp.shared_pool(); - dummydxy = new double[0,0]; - dummysxy = new sparse.sparsematrix(); - dummyidx = new int[0]; - dummypool = new alglib.smp.shared_pool(); - } - public override alglib.apobject make_copy() - { - multilayerperceptron _result = new multilayerperceptron(); - _result.hlnetworktype = hlnetworktype; - _result.hlnormtype = hlnormtype; - _result.hllayersizes = (int[])hllayersizes.Clone(); - _result.hlconnections = (int[])hlconnections.Clone(); - _result.hlneurons = (int[])hlneurons.Clone(); - _result.structinfo = (int[])structinfo.Clone(); - _result.weights = (double[])weights.Clone(); - _result.columnmeans = (double[])columnmeans.Clone(); - _result.columnsigmas = (double[])columnsigmas.Clone(); - _result.neurons = (double[])neurons.Clone(); - _result.dfdnet = (double[])dfdnet.Clone(); - _result.derror = (double[])derror.Clone(); - _result.x = (double[])x.Clone(); - _result.y = (double[])y.Clone(); - _result.xy = (double[,])xy.Clone(); - _result.xyrow = (double[])xyrow.Clone(); - _result.nwbuf = (double[])nwbuf.Clone(); - _result.integerbuf = (int[])integerbuf.Clone(); - _result.err = err!=null ? (modelerrors)err.make_copy() : null; - _result.rndbuf = (double[])rndbuf.Clone(); - _result.buf = buf!=null ? (alglib.smp.shared_pool)buf.make_copy() : null; - _result.gradbuf = gradbuf!=null ? (alglib.smp.shared_pool)gradbuf.make_copy() : null; - _result.dummydxy = (double[,])dummydxy.Clone(); - _result.dummysxy = dummysxy!=null ? (sparse.sparsematrix)dummysxy.make_copy() : null; - _result.dummyidx = (int[])dummyidx.Clone(); - _result.dummypool = dummypool!=null ? (alglib.smp.shared_pool)dummypool.make_copy() : null; - return _result; - } - }; - - - - - public const int mlpvnum = 7; - public const int mlpfirstversion = 0; - public const int nfieldwidth = 4; - public const int hlconnfieldwidth = 5; - public const int hlnfieldwidth = 4; - public const int gradbasecasecost = 50000; - public const int microbatchsize = 64; - - - /************************************************************************* - This function returns number of weights updates which is required for - gradient calculation problem to be splitted. - *************************************************************************/ - public static int mlpgradsplitcost(alglib.xparams _params) - { - int result = 0; - - result = gradbasecasecost; - return result; - } - - - /************************************************************************* - This function returns number of elements in subset of dataset which is - required for gradient calculation problem to be splitted. - *************************************************************************/ - public static int mlpgradsplitsize(alglib.xparams _params) - { - int result = 0; - - result = microbatchsize; - return result; - } - - - /************************************************************************* - Creates neural network with NIn inputs, NOut outputs, without hidden - layers, with linear output layer. Network weights are filled with small - random values. - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreate0(int nin, - int nout, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - - layerscount = 1+3; - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(-5, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, false, network, _params); - fillhighlevelinformation(network, nin, 0, 0, nout, false, true, _params); - } - - - /************************************************************************* - Same as MLPCreate0, but with one hidden layer (NHid neurons) with - non-linear activation function. Output layer is linear. - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreate1(int nin, - int nhid, - int nout, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - - layerscount = 1+3+3; - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(-5, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, false, network, _params); - fillhighlevelinformation(network, nin, nhid, 0, nout, false, true, _params); - } - - - /************************************************************************* - Same as MLPCreate0, but with two hidden layers (NHid1 and NHid2 neurons) - with non-linear activation function. Output layer is linear. - $ALL - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreate2(int nin, - int nhid1, - int nhid2, - int nout, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - - layerscount = 1+3+3+3; - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid2, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(-5, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, false, network, _params); - fillhighlevelinformation(network, nin, nhid1, nhid2, nout, false, true, _params); - } - - - /************************************************************************* - Creates neural network with NIn inputs, NOut outputs, without hidden - layers with non-linear output layer. Network weights are filled with small - random values. - - Activation function of the output layer takes values: - - (B, +INF), if D>=0 - - or - - (-INF, B), if D<0. - - - -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreateb0(int nin, - int nout, - double b, - double d, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - int i = 0; - - layerscount = 1+3; - if( (double)(d)>=(double)(0) ) - { - d = 1; - } - else - { - d = -1; - } - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(3, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, false, network, _params); - fillhighlevelinformation(network, nin, 0, 0, nout, false, false, _params); - - // - // Turn on ouputs shift/scaling. - // - for(i=nin; i<=nin+nout-1; i++) - { - network.columnmeans[i] = b; - network.columnsigmas[i] = d; - } - } - - - /************************************************************************* - Same as MLPCreateB0 but with non-linear hidden layer. - - -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreateb1(int nin, - int nhid, - int nout, - double b, - double d, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - int i = 0; - - layerscount = 1+3+3; - if( (double)(d)>=(double)(0) ) - { - d = 1; - } - else - { - d = -1; - } - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(3, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, false, network, _params); - fillhighlevelinformation(network, nin, nhid, 0, nout, false, false, _params); - - // - // Turn on ouputs shift/scaling. - // - for(i=nin; i<=nin+nout-1; i++) - { - network.columnmeans[i] = b; - network.columnsigmas[i] = d; - } - } - - - /************************************************************************* - Same as MLPCreateB0 but with two non-linear hidden layers. - - -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreateb2(int nin, - int nhid1, - int nhid2, - int nout, - double b, - double d, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - int i = 0; - - layerscount = 1+3+3+3; - if( (double)(d)>=(double)(0) ) - { - d = 1; - } - else - { - d = -1; - } - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid2, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(3, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, false, network, _params); - fillhighlevelinformation(network, nin, nhid1, nhid2, nout, false, false, _params); - - // - // Turn on ouputs shift/scaling. - // - for(i=nin; i<=nin+nout-1; i++) - { - network.columnmeans[i] = b; - network.columnsigmas[i] = d; - } - } - - - /************************************************************************* - Creates neural network with NIn inputs, NOut outputs, without hidden - layers with non-linear output layer. Network weights are filled with small - random values. Activation function of the output layer takes values [A,B]. - - -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreater0(int nin, - int nout, - double a, - double b, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - int i = 0; - - layerscount = 1+3; - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, false, network, _params); - fillhighlevelinformation(network, nin, 0, 0, nout, false, false, _params); - - // - // Turn on outputs shift/scaling. - // - for(i=nin; i<=nin+nout-1; i++) - { - network.columnmeans[i] = 0.5*(a+b); - network.columnsigmas[i] = 0.5*(a-b); - } - } - - - /************************************************************************* - Same as MLPCreateR0, but with non-linear hidden layer. - - -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreater1(int nin, - int nhid, - int nout, - double a, - double b, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - int i = 0; - - layerscount = 1+3+3; - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, false, network, _params); - fillhighlevelinformation(network, nin, nhid, 0, nout, false, false, _params); - - // - // Turn on outputs shift/scaling. - // - for(i=nin; i<=nin+nout-1; i++) - { - network.columnmeans[i] = 0.5*(a+b); - network.columnsigmas[i] = 0.5*(a-b); - } - } - - - /************************************************************************* - Same as MLPCreateR0, but with two non-linear hidden layers. - - -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreater2(int nin, - int nhid1, - int nhid2, - int nout, - double a, - double b, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - int i = 0; - - layerscount = 1+3+3+3; - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid2, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, false, network, _params); - fillhighlevelinformation(network, nin, nhid1, nhid2, nout, false, false, _params); - - // - // Turn on outputs shift/scaling. - // - for(i=nin; i<=nin+nout-1; i++) - { - network.columnmeans[i] = 0.5*(a+b); - network.columnsigmas[i] = 0.5*(a-b); - } - } - - - /************************************************************************* - Creates classifier network with NIn inputs and NOut possible classes. - Network contains no hidden layers and linear output layer with SOFTMAX- - normalization (so outputs sums up to 1.0 and converge to posterior - probabilities). - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreatec0(int nin, - int nout, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - - alglib.ap.assert(nout>=2, "MLPCreateC0: NOut<2!"); - layerscount = 1+2+1; - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout-1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addzerolayer(ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, true, network, _params); - fillhighlevelinformation(network, nin, 0, 0, nout, true, true, _params); - } - - - /************************************************************************* - Same as MLPCreateC0, but with one non-linear hidden layer. - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreatec1(int nin, - int nhid, - int nout, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - - alglib.ap.assert(nout>=2, "MLPCreateC1: NOut<2!"); - layerscount = 1+3+2+1; - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout-1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addzerolayer(ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, true, network, _params); - fillhighlevelinformation(network, nin, nhid, 0, nout, true, true, _params); - } - - - /************************************************************************* - Same as MLPCreateC0, but with two non-linear hidden layers. - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcreatec2(int nin, - int nhid1, - int nhid2, - int nout, - multilayerperceptron network, - alglib.xparams _params) - { - int[] lsizes = new int[0]; - int[] ltypes = new int[0]; - int[] lconnfirst = new int[0]; - int[] lconnlast = new int[0]; - int layerscount = 0; - int lastproc = 0; - - alglib.ap.assert(nout>=2, "MLPCreateC2: NOut<2!"); - layerscount = 1+3+3+2+1; - - // - // Allocate arrays - // - lsizes = new int[layerscount-1+1]; - ltypes = new int[layerscount-1+1]; - lconnfirst = new int[layerscount-1+1]; - lconnlast = new int[layerscount-1+1]; - - // - // Layers - // - addinputlayer(nin, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nhid2, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addactivationlayer(1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addbiasedsummatorlayer(nout-1, ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - addzerolayer(ref lsizes, ref ltypes, ref lconnfirst, ref lconnlast, ref lastproc, _params); - - // - // Create - // - mlpcreate(nin, nout, lsizes, ltypes, lconnfirst, lconnlast, layerscount, true, network, _params); - fillhighlevelinformation(network, nin, nhid1, nhid2, nout, true, true, _params); - } - - - /************************************************************************* - Copying of neural network - - INPUT PARAMETERS: - Network1 - original - - OUTPUT PARAMETERS: - Network2 - copy - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcopy(multilayerperceptron network1, - multilayerperceptron network2, - alglib.xparams _params) - { - mlpcopyshared(network1, network2, _params); - } - - - /************************************************************************* - Copying of neural network (second parameter is passed as shared object). - - INPUT PARAMETERS: - Network1 - original - - OUTPUT PARAMETERS: - Network2 - copy - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcopyshared(multilayerperceptron network1, - multilayerperceptron network2, - alglib.xparams _params) - { - int wcount = 0; - int i = 0; - hpccores.mlpbuffers buf = new hpccores.mlpbuffers(); - smlpgrad sgrad = new smlpgrad(); - - - // - // Copy scalar and array fields - // - network2.hlnetworktype = network1.hlnetworktype; - network2.hlnormtype = network1.hlnormtype; - apserv.copyintegerarray(network1.hllayersizes, ref network2.hllayersizes, _params); - apserv.copyintegerarray(network1.hlconnections, ref network2.hlconnections, _params); - apserv.copyintegerarray(network1.hlneurons, ref network2.hlneurons, _params); - apserv.copyintegerarray(network1.structinfo, ref network2.structinfo, _params); - apserv.copyrealarray(network1.weights, ref network2.weights, _params); - apserv.copyrealarray(network1.columnmeans, ref network2.columnmeans, _params); - apserv.copyrealarray(network1.columnsigmas, ref network2.columnsigmas, _params); - apserv.copyrealarray(network1.neurons, ref network2.neurons, _params); - apserv.copyrealarray(network1.dfdnet, ref network2.dfdnet, _params); - apserv.copyrealarray(network1.derror, ref network2.derror, _params); - apserv.copyrealarray(network1.x, ref network2.x, _params); - apserv.copyrealarray(network1.y, ref network2.y, _params); - apserv.copyrealarray(network1.nwbuf, ref network2.nwbuf, _params); - apserv.copyintegerarray(network1.integerbuf, ref network2.integerbuf, _params); - - // - // copy buffers - // - wcount = mlpgetweightscount(network1, _params); - alglib.smp.ae_shared_pool_set_seed(network2.buf, buf); - sgrad.g = new double[wcount]; - sgrad.f = 0.0; - for(i=0; i<=wcount-1; i++) - { - sgrad.g[i] = 0.0; - } - alglib.smp.ae_shared_pool_set_seed(network2.gradbuf, sgrad); - } - - - /************************************************************************* - This function compares architectures of neural networks. Only geometries - are compared, weights and other parameters are not tested. - - -- ALGLIB -- - Copyright 20.06.2013 by Bochkanov Sergey - *************************************************************************/ - public static bool mlpsamearchitecture(multilayerperceptron network1, - multilayerperceptron network2, - alglib.xparams _params) - { - bool result = new bool(); - int i = 0; - int ninfo = 0; - - alglib.ap.assert(alglib.ap.len(network1.structinfo)>0 && alglib.ap.len(network1.structinfo)>=network1.structinfo[0], "MLPSameArchitecture: Network1 is uninitialized"); - alglib.ap.assert(alglib.ap.len(network2.structinfo)>0 && alglib.ap.len(network2.structinfo)>=network2.structinfo[0], "MLPSameArchitecture: Network2 is uninitialized"); - result = false; - if( network1.structinfo[0]!=network2.structinfo[0] ) - { - return result; - } - ninfo = network1.structinfo[0]; - for(i=0; i<=ninfo-1; i++) - { - if( network1.structinfo[i]!=network2.structinfo[i] ) - { - return result; - } - } - result = true; - return result; - } - - - /************************************************************************* - This function copies tunable parameters (weights/means/sigmas) from one - network to another with same architecture. It performs some rudimentary - checks that architectures are same, and throws exception if check fails. - - It is intended for fast copying of states between two network which are - known to have same geometry. - - INPUT PARAMETERS: - Network1 - source, must be correctly initialized - Network2 - target, must have same architecture - - OUTPUT PARAMETERS: - Network2 - network state is copied from source to target - - -- ALGLIB -- - Copyright 20.06.2013 by Bochkanov Sergey - *************************************************************************/ - public static void mlpcopytunableparameters(multilayerperceptron network1, - multilayerperceptron network2, - alglib.xparams _params) - { - int i = 0; - int ninfo = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - - alglib.ap.assert(alglib.ap.len(network1.structinfo)>0 && alglib.ap.len(network1.structinfo)>=network1.structinfo[0], "MLPCopyTunableParameters: Network1 is uninitialized"); - alglib.ap.assert(alglib.ap.len(network2.structinfo)>0 && alglib.ap.len(network2.structinfo)>=network2.structinfo[0], "MLPCopyTunableParameters: Network2 is uninitialized"); - alglib.ap.assert(network1.structinfo[0]==network2.structinfo[0], "MLPCopyTunableParameters: Network1 geometry differs from that of Network2"); - ninfo = network1.structinfo[0]; - for(i=0; i<=ninfo-1; i++) - { - alglib.ap.assert(network1.structinfo[i]==network2.structinfo[i], "MLPCopyTunableParameters: Network1 geometry differs from that of Network2"); - } - mlpproperties(network1, ref nin, ref nout, ref wcount, _params); - for(i=0; i<=wcount-1; i++) - { - network2.weights[i] = network1.weights[i]; - } - if( mlpissoftmax(network1, _params) ) - { - for(i=0; i<=nin-1; i++) - { - network2.columnmeans[i] = network1.columnmeans[i]; - network2.columnsigmas[i] = network1.columnsigmas[i]; - } - } - else - { - for(i=0; i<=nin+nout-1; i++) - { - network2.columnmeans[i] = network1.columnmeans[i]; - network2.columnsigmas[i] = network1.columnsigmas[i]; - } - } - } - - - /************************************************************************* - This function exports tunable parameters (weights/means/sigmas) from - network to contiguous array. Nothing is guaranteed about array format, the - only thing you can count for is that MLPImportTunableParameters() will be - able to parse it. - - It is intended for fast copying of states between network and backup array - - INPUT PARAMETERS: - Network - source, must be correctly initialized - P - array to use. If its size is enough to store data, it - is reused. - - OUTPUT PARAMETERS: - P - array which stores network parameters, resized if needed - PCount - number of parameters stored in array. - - -- ALGLIB -- - Copyright 20.06.2013 by Bochkanov Sergey - *************************************************************************/ - public static void mlpexporttunableparameters(multilayerperceptron network, - ref double[] p, - ref int pcount, - alglib.xparams _params) - { - int i = 0; - int k = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - - pcount = 0; - - alglib.ap.assert(alglib.ap.len(network.structinfo)>0 && alglib.ap.len(network.structinfo)>=network.structinfo[0], "MLPExportTunableParameters: Network is uninitialized"); - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - if( mlpissoftmax(network, _params) ) - { - pcount = wcount+2*nin; - apserv.rvectorsetlengthatleast(ref p, pcount, _params); - k = 0; - for(i=0; i<=wcount-1; i++) - { - p[k] = network.weights[i]; - k = k+1; - } - for(i=0; i<=nin-1; i++) - { - p[k] = network.columnmeans[i]; - k = k+1; - p[k] = network.columnsigmas[i]; - k = k+1; - } - } - else - { - pcount = wcount+2*(nin+nout); - apserv.rvectorsetlengthatleast(ref p, pcount, _params); - k = 0; - for(i=0; i<=wcount-1; i++) - { - p[k] = network.weights[i]; - k = k+1; - } - for(i=0; i<=nin+nout-1; i++) - { - p[k] = network.columnmeans[i]; - k = k+1; - p[k] = network.columnsigmas[i]; - k = k+1; - } - } - } - - - /************************************************************************* - This function imports tunable parameters (weights/means/sigmas) which - were exported by MLPExportTunableParameters(). - - It is intended for fast copying of states between network and backup array - - INPUT PARAMETERS: - Network - target: - * must be correctly initialized - * must have same geometry as network used to export params - P - array with parameters - - -- ALGLIB -- - Copyright 20.06.2013 by Bochkanov Sergey - *************************************************************************/ - public static void mlpimporttunableparameters(multilayerperceptron network, - double[] p, - alglib.xparams _params) - { - int i = 0; - int k = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - - alglib.ap.assert(alglib.ap.len(network.structinfo)>0 && alglib.ap.len(network.structinfo)>=network.structinfo[0], "MLPImportTunableParameters: Network is uninitialized"); - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - if( mlpissoftmax(network, _params) ) - { - k = 0; - for(i=0; i<=wcount-1; i++) - { - network.weights[i] = p[k]; - k = k+1; - } - for(i=0; i<=nin-1; i++) - { - network.columnmeans[i] = p[k]; - k = k+1; - network.columnsigmas[i] = p[k]; - k = k+1; - } - } - else - { - k = 0; - for(i=0; i<=wcount-1; i++) - { - network.weights[i] = p[k]; - k = k+1; - } - for(i=0; i<=nin+nout-1; i++) - { - network.columnmeans[i] = p[k]; - k = k+1; - network.columnsigmas[i] = p[k]; - k = k+1; - } - } - } - - - /************************************************************************* - Serialization of MultiLayerPerceptron strucure - - INPUT PARAMETERS: - Network - original - - OUTPUT PARAMETERS: - RA - array of real numbers which stores network, - array[0..RLen-1] - RLen - RA lenght - - -- ALGLIB -- - Copyright 29.03.2008 by Bochkanov Sergey - *************************************************************************/ - public static void mlpserializeold(multilayerperceptron network, - ref double[] ra, - ref int rlen, - alglib.xparams _params) - { - int i = 0; - int ssize = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int sigmalen = 0; - int offs = 0; - int i_ = 0; - int i1_ = 0; - - ra = new double[0]; - rlen = 0; + info = 0; + means = new double[0]; + sigmas = new double[0]; // - // Unload info + // Test parameters // - ssize = network.structinfo[0]; - nin = network.structinfo[1]; - nout = network.structinfo[2]; - wcount = network.structinfo[4]; - if( mlpissoftmax(network, _params) ) - { - sigmalen = nin; - } - else + if( npoints<=0 || nvars<1 ) { - sigmalen = nin+nout; + info = -1; + return; } + info = 1; // - // RA format: - // LEN DESRC. - // 1 RLen - // 1 version (MLPVNum) - // 1 StructInfo size - // SSize StructInfo - // WCount Weights - // SigmaLen ColumnMeans - // SigmaLen ColumnSigmas + // Standartization // - rlen = 3+ssize+wcount+2*sigmalen; - ra = new double[rlen-1+1]; - ra[0] = rlen; - ra[1] = mlpvnum; - ra[2] = ssize; - offs = 3; - for(i=0; i<=ssize-1; i++) - { - ra[offs+i] = network.structinfo[i]; - } - offs = offs+ssize; - i1_ = (0) - (offs); - for(i_=offs; i_<=offs+wcount-1;i_++) - { - ra[i_] = network.weights[i_+i1_]; - } - offs = offs+wcount; - i1_ = (0) - (offs); - for(i_=offs; i_<=offs+sigmalen-1;i_++) - { - ra[i_] = network.columnmeans[i_+i1_]; - } - offs = offs+sigmalen; - i1_ = (0) - (offs); - for(i_=offs; i_<=offs+sigmalen-1;i_++) + means = new double[nvars-1+1]; + sigmas = new double[nvars-1+1]; + tmp = new double[npoints-1+1]; + for(j=0; j<=nvars-1; j++) { - ra[i_] = network.columnsigmas[i_+i1_]; + for(i_=0; i_<=npoints-1;i_++) + { + tmp[i_] = xy[i_,j]; + } + basestat.samplemoments(tmp, npoints, ref mean, ref variance, ref skewness, ref kurtosis, _params); + means[j] = mean; + sigmas[j] = Math.Sqrt(variance); + if( (double)(sigmas[j])==(double)(0) ) + { + sigmas[j] = 1; + } } - offs = offs+sigmalen; } /************************************************************************* - Unserialization of MultiLayerPerceptron strucure - - INPUT PARAMETERS: - RA - real array which stores network - - OUTPUT PARAMETERS: - Network - restored network -- ALGLIB -- - Copyright 29.03.2008 by Bochkanov Sergey + Copyright 19.05.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpunserializeold(double[] ra, - multilayerperceptron network, + public static double dsgetmeanmindistance(double[,] xy, + int npoints, + int nvars, alglib.xparams _params) { + double result = 0; int i = 0; - int ssize = 0; - int ntotal = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int sigmalen = 0; - int offs = 0; + int j = 0; + double[] tmp = new double[0]; + double[] tmp2 = new double[0]; + double v = 0; int i_ = 0; - int i1_ = 0; - alglib.ap.assert((int)Math.Round(ra[1])==mlpvnum, "MLPUnserialize: incorrect array!"); - - // - // Unload StructInfo from IA - // - offs = 3; - ssize = (int)Math.Round(ra[2]); - network.structinfo = new int[ssize-1+1]; - for(i=0; i<=ssize-1; i++) - { - network.structinfo[i] = (int)Math.Round(ra[offs+i]); - } - offs = offs+ssize; // - // Unload info from StructInfo + // Test parameters // - ssize = network.structinfo[0]; - nin = network.structinfo[1]; - nout = network.structinfo[2]; - ntotal = network.structinfo[3]; - wcount = network.structinfo[4]; - if( network.structinfo[6]==0 ) - { - sigmalen = nin+nout; - } - else + if( npoints<=0 || nvars<1 ) { - sigmalen = nin; + result = 0; + return result; } // - // Allocate space for other fields - // - network.weights = new double[wcount-1+1]; - network.columnmeans = new double[sigmalen-1+1]; - network.columnsigmas = new double[sigmalen-1+1]; - network.neurons = new double[ntotal-1+1]; - network.nwbuf = new double[Math.Max(wcount, 2*nout)-1+1]; - network.dfdnet = new double[ntotal-1+1]; - network.x = new double[nin-1+1]; - network.y = new double[nout-1+1]; - network.derror = new double[ntotal-1+1]; - - // - // Copy parameters from RA + // Process // - i1_ = (offs) - (0); - for(i_=0; i_<=wcount-1;i_++) + tmp = new double[npoints-1+1]; + for(i=0; i<=npoints-1; i++) { - network.weights[i_] = ra[i_+i1_]; + tmp[i] = math.maxrealnumber; } - offs = offs+wcount; - i1_ = (offs) - (0); - for(i_=0; i_<=sigmalen-1;i_++) + tmp2 = new double[nvars-1+1]; + for(i=0; i<=npoints-1; i++) { - network.columnmeans[i_] = ra[i_+i1_]; + for(j=i+1; j<=npoints-1; j++) + { + for(i_=0; i_<=nvars-1;i_++) + { + tmp2[i_] = xy[i,i_]; + } + for(i_=0; i_<=nvars-1;i_++) + { + tmp2[i_] = tmp2[i_] - xy[j,i_]; + } + v = 0.0; + for(i_=0; i_<=nvars-1;i_++) + { + v += tmp2[i_]*tmp2[i_]; + } + v = Math.Sqrt(v); + tmp[i] = Math.Min(tmp[i], v); + tmp[j] = Math.Min(tmp[j], v); + } } - offs = offs+sigmalen; - i1_ = (offs) - (0); - for(i_=0; i_<=sigmalen-1;i_++) + result = 0; + for(i=0; i<=npoints-1; i++) { - network.columnsigmas[i_] = ra[i_+i1_]; + result = result+tmp[i]/npoints; } - offs = offs+sigmalen; + return result; } /************************************************************************* - Randomization of neural network weights -- ALGLIB -- - Copyright 06.11.2007 by Bochkanov Sergey + Copyright 19.05.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlprandomize(multilayerperceptron network, + public static void dstie(ref double[] a, + int n, + ref int[] ties, + ref int tiecount, + ref int[] p1, + ref int[] p2, alglib.xparams _params) { - int nin = 0; - int nout = 0; - int wcount = 0; - int ntotal = 0; - int istart = 0; - hqrnd.hqrndstate r = new hqrnd.hqrndstate(); - int entrysize = 0; - int entryoffs = 0; - int neuronidx = 0; - int neurontype = 0; - double vmean = 0; - double vvar = 0; int i = 0; - int n1 = 0; - int n2 = 0; - double desiredsigma = 0; - int montecarlocnt = 0; - double ef = 0; - double ef2 = 0; - double v = 0; - double wscale = 0; + int k = 0; + int[] tmp = new int[0]; + + ties = new int[0]; + tiecount = 0; + p1 = new int[0]; + p2 = new int[0]; - hqrnd.hqrndrandomize(r, _params); - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; - desiredsigma = 0.5; - montecarlocnt = 20; // - // Stage 1: - // * Network.Weights is filled by standard deviation of weights - // * default values: sigma=1 + // Special case // - for(i=0; i<=wcount-1; i++) + if( n<=0 ) { - network.weights[i] = 1.0; + tiecount = 0; + return; } // - // Stage 2: - // * assume that input neurons have zero mean and unit standard deviation - // * assume that constant neurons have zero standard deviation - // * perform forward pass along neurons - // * for each non-input non-constant neuron: - // * calculate mean and standard deviation of neuron's output - // assuming that we know means/deviations of neurons which feed it - // and assuming that weights has unit variance and zero mean. - // * for each nonlinear neuron additionally we perform backward pass: - // * scale variances of weights which feed it in such way that neuron's - // input has unit standard deviation - // - // NOTE: this algorithm assumes that each connection feeds at most one - // non-linear neuron. This assumption can be incorrect in upcoming - // architectures with strong neurons. However, algorithm should - // work smoothly even in this case. - // - // During this stage we use Network.RndBuf, which is grouped into NTotal - // entries, each of them having following format: + // Sort A // - // Buf[Offset+0] mean value of neuron's output - // Buf[Offset+1] standard deviation of neuron's output - // + tsort.tagsort(ref a, n, ref p1, ref p2, _params); + // + // Process ties // - entrysize = 2; - apserv.rvectorsetlengthatleast(ref network.rndbuf, entrysize*ntotal, _params); - for(neuronidx=0; neuronidx<=ntotal-1; neuronidx++) + tiecount = 1; + for(i=1; i<=n-1; i++) { - neurontype = network.structinfo[istart+neuronidx*nfieldwidth+0]; - entryoffs = entrysize*neuronidx; - if( neurontype==-2 ) - { - - // - // Input neuron: zero mean, unit variance. - // - network.rndbuf[entryoffs+0] = 0.0; - network.rndbuf[entryoffs+1] = 1.0; - continue; - } - if( neurontype==-3 ) - { - - // - // "-1" neuron: mean=-1, zero variance. - // - network.rndbuf[entryoffs+0] = -1.0; - network.rndbuf[entryoffs+1] = 0.0; - continue; - } - if( neurontype==-4 ) - { - - // - // "0" neuron: mean=0, zero variance. - // - network.rndbuf[entryoffs+0] = 0.0; - network.rndbuf[entryoffs+1] = 0.0; - continue; - } - if( neurontype==0 ) - { - - // - // Adaptive summator neuron: - // * calculate its mean and variance. - // * we assume that weights of this neuron have unit variance and zero mean. - // * thus, neuron's output is always have zero mean - // * as for variance, it is a bit more interesting: - // * let n[i] is i-th input neuron - // * let w[i] is i-th weight - // * we assume that n[i] and w[i] are independently distributed - // * Var(n0*w0+n1*w1+...) = Var(n0*w0)+Var(n1*w1)+... - // * Var(X*Y) = mean(X)^2*Var(Y) + mean(Y)^2*Var(X) + Var(X)*Var(Y) - // * mean(w[i])=0, var(w[i])=1 - // * Var(n[i]*w[i]) = mean(n[i])^2 + Var(n[i]) - // - n1 = network.structinfo[istart+neuronidx*nfieldwidth+2]; - n2 = n1+network.structinfo[istart+neuronidx*nfieldwidth+1]-1; - vmean = 0.0; - vvar = 0.0; - for(i=n1; i<=n2; i++) - { - vvar = vvar+math.sqr(network.rndbuf[entrysize*i+0])+math.sqr(network.rndbuf[entrysize*i+1]); - } - network.rndbuf[entryoffs+0] = vmean; - network.rndbuf[entryoffs+1] = Math.Sqrt(vvar); - continue; - } - if( neurontype==-5 ) - { - - // - // Linear activation function - // - i = network.structinfo[istart+neuronidx*nfieldwidth+2]; - vmean = network.rndbuf[entrysize*i+0]; - vvar = math.sqr(network.rndbuf[entrysize*i+1]); - if( (double)(vvar)>(double)(0) ) - { - wscale = desiredsigma/Math.Sqrt(vvar); - } - else - { - wscale = 1.0; - } - randomizebackwardpass(network, i, wscale, _params); - network.rndbuf[entryoffs+0] = vmean*wscale; - network.rndbuf[entryoffs+1] = desiredsigma; - continue; - } - if( neurontype>0 ) + if( (double)(a[i])!=(double)(a[i-1]) ) { - - // - // Nonlinear activation function: - // * scale its inputs - // * estimate mean/sigma of its output using Monte-Carlo method - // (we simulate different inputs with unit deviation and - // sample activation function output on such inputs) - // - i = network.structinfo[istart+neuronidx*nfieldwidth+2]; - vmean = network.rndbuf[entrysize*i+0]; - vvar = math.sqr(network.rndbuf[entrysize*i+1]); - if( (double)(vvar)>(double)(0) ) - { - wscale = desiredsigma/Math.Sqrt(vvar); - } - else - { - wscale = 1.0; - } - randomizebackwardpass(network, i, wscale, _params); - ef = 0.0; - ef2 = 0.0; - vmean = vmean*wscale; - for(i=0; i<=montecarlocnt-1; i++) - { - v = vmean+desiredsigma*hqrnd.hqrndnormal(r, _params); - ef = ef+v; - ef2 = ef2+v*v; - } - ef = ef/montecarlocnt; - ef2 = ef2/montecarlocnt; - network.rndbuf[entryoffs+0] = ef; - network.rndbuf[entryoffs+1] = Math.Max(ef2-ef*ef, 0.0); - continue; + tiecount = tiecount+1; } - alglib.ap.assert(false, "MLPRandomize: unexpected neuron type"); } - - // - // Stage 3: generate weights. - // - for(i=0; i<=wcount-1; i++) + ties = new int[tiecount+1]; + ties[0] = 0; + k = 1; + for(i=1; i<=n-1; i++) { - network.weights[i] = network.weights[i]*hqrnd.hqrndnormal(r, _params); + if( (double)(a[i])!=(double)(a[i-1]) ) + { + ties[k] = i; + k = k+1; + } } + ties[tiecount] = n; } /************************************************************************* - Randomization of neural network weights and standartisator -- ALGLIB -- - Copyright 10.03.2008 by Bochkanov Sergey + Copyright 11.12.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlprandomizefull(multilayerperceptron network, + public static void dstiefasti(ref double[] a, + ref int[] b, + int n, + ref int[] ties, + ref int tiecount, + ref double[] bufr, + ref int[] bufi, alglib.xparams _params) { int i = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int ntotal = 0; - int istart = 0; - int offs = 0; - int ntype = 0; + int k = 0; + int[] tmp = new int[0]; + + tiecount = 0; - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; // - // Process network + // Special case // - mlprandomize(network, _params); - for(i=0; i<=nin-1; i++) + if( n<=0 ) { - network.columnmeans[i] = math.randomreal()-0.5; - network.columnsigmas[i] = math.randomreal()+0.5; + tiecount = 0; + return; } - if( !mlpissoftmax(network, _params) ) + + // + // Sort A + // + tsort.tagsortfasti(ref a, ref b, ref bufr, ref bufi, n, _params); + + // + // Process ties + // + ties[0] = 0; + k = 1; + for(i=1; i<=n-1; i++) { - for(i=0; i<=nout-1; i++) + if( (double)(a[i])!=(double)(a[i-1]) ) { - offs = istart+(ntotal-nout+i)*nfieldwidth; - ntype = network.structinfo[offs+0]; - if( ntype==0 ) - { - - // - // Shifts are changed only for linear outputs neurons - // - network.columnmeans[nin+i] = 2*math.randomreal()-1; - } - if( ntype==0 || ntype==3 ) - { - - // - // Scales are changed only for linear or bounded outputs neurons. - // Note that scale randomization preserves sign. - // - network.columnsigmas[nin+i] = Math.Sign(network.columnsigmas[nin+i])*(1.5*math.randomreal()+0.5); - } + ties[k] = i; + k = k+1; } } + ties[k] = n; + tiecount = k; } /************************************************************************* + Optimal binary classification + + Algorithms finds optimal (=with minimal cross-entropy) binary partition. Internal subroutine. + INPUT PARAMETERS: + A - array[0..N-1], variable + C - array[0..N-1], class numbers (0 or 1). + N - array size + + OUTPUT PARAMETERS: + Info - completetion code: + * -3, all values of A[] are same (partition is impossible) + * -2, one of C[] is incorrect (<0, >1) + * -1, incorrect pararemets were passed (N<=0). + * 1, OK + Threshold- partiton boundary. Left part contains values which are + strictly less than Threshold. Right part contains values + which are greater than or equal to Threshold. + PAL, PBL- probabilities P(0|v=Threshold) and P(1|v>=Threshold) + CVE - cross-validation estimate of cross-entropy + -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey + Copyright 22.05.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpinitpreprocessor(multilayerperceptron network, - double[,] xy, - int ssize, + public static void dsoptimalsplit2(double[] a, + int[] c, + int n, + ref int info, + ref double threshold, + ref double pal, + ref double pbl, + ref double par, + ref double pbr, + ref double cve, alglib.xparams _params) { int i = 0; - int j = 0; - int jmax = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int ntotal = 0; - int istart = 0; - int offs = 0; - int ntype = 0; - double[] means = new double[0]; - double[] sigmas = new double[0]; + int t = 0; double s = 0; + int[] ties = new int[0]; + int tiecount = 0; + int[] p1 = new int[0]; + int[] p2 = new int[0]; + int k = 0; + int koptimal = 0; + double pak = 0; + double pbk = 0; + double cvoptimal = 0; + double cv = 0; + + a = (double[])a.Clone(); + c = (int[])c.Clone(); + info = 0; + threshold = 0; + pal = 0; + pbl = 0; + par = 0; + pbr = 0; + cve = 0; - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; // - // Means/Sigmas + // Test for errors in inputs // - if( mlpissoftmax(network, _params) ) - { - jmax = nin-1; - } - else - { - jmax = nin+nout-1; - } - means = new double[jmax+1]; - sigmas = new double[jmax+1]; - for(i=0; i<=jmax; i++) + if( n<=0 ) { - means[i] = 0; - sigmas[i] = 0; + info = -1; + return; } - for(i=0; i<=ssize-1; i++) + for(i=0; i<=n-1; i++) { - for(j=0; j<=jmax; j++) + if( c[i]!=0 && c[i]!=1 ) { - means[j] = means[j]+xy[i,j]; + info = -2; + return; } } - for(i=0; i<=jmax; i++) - { - means[i] = means[i]/ssize; - } - for(i=0; i<=ssize-1; i++) + info = 1; + + // + // Tie + // + dstie(ref a, n, ref ties, ref tiecount, ref p1, ref p2, _params); + for(i=0; i<=n-1; i++) { - for(j=0; j<=jmax; j++) + if( p2[i]!=i ) { - sigmas[j] = sigmas[j]+math.sqr(xy[i,j]-means[j]); + t = c[i]; + c[i] = c[p2[i]]; + c[p2[i]] = t; } } - for(i=0; i<=jmax; i++) + + // + // Special case: number of ties is 1. + // + // NOTE: we assume that P[i,j] equals to 0 or 1, + // intermediate values are not allowed. + // + if( tiecount==1 ) { - sigmas[i] = Math.Sqrt(sigmas[i]/ssize); + info = -3; + return; } // - // Inputs + // General case, number of ties > 1 // - for(i=0; i<=nin-1; i++) + // NOTE: we assume that P[i,j] equals to 0 or 1, + // intermediate values are not allowed. + // + pal = 0; + pbl = 0; + par = 0; + pbr = 0; + for(i=0; i<=n-1; i++) { - network.columnmeans[i] = means[i]; - network.columnsigmas[i] = sigmas[i]; - if( (double)(network.columnsigmas[i])==(double)(0) ) + if( c[i]==0 ) { - network.columnsigmas[i] = 1; + par = par+1; + } + if( c[i]==1 ) + { + pbr = pbr+1; } } - - // - // Outputs - // - if( !mlpissoftmax(network, _params) ) + koptimal = -1; + cvoptimal = math.maxrealnumber; + for(k=0; k<=tiecount-2; k++) { - for(i=0; i<=nout-1; i++) + + // + // first, obtain information about K-th tie which is + // moved from R-part to L-part + // + pak = 0; + pbk = 0; + for(i=ties[k]; i<=ties[k+1]-1; i++) { - offs = istart+(ntotal-nout+i)*nfieldwidth; - ntype = network.structinfo[offs+0]; - - // - // Linear outputs - // - if( ntype==0 ) + if( c[i]==0 ) { - network.columnmeans[nin+i] = means[nin+i]; - network.columnsigmas[nin+i] = sigmas[nin+i]; - if( (double)(network.columnsigmas[nin+i])==(double)(0) ) - { - network.columnsigmas[nin+i] = 1; - } + pak = pak+1; } - - // - // Bounded outputs (half-interval) - // - if( ntype==3 ) + if( c[i]==1 ) { - s = means[nin+i]-network.columnmeans[nin+i]; - if( (double)(s)==(double)(0) ) - { - s = Math.Sign(network.columnsigmas[nin+i]); - } - if( (double)(s)==(double)(0) ) - { - s = 1.0; - } - network.columnsigmas[nin+i] = Math.Sign(network.columnsigmas[nin+i])*Math.Abs(s); - if( (double)(network.columnsigmas[nin+i])==(double)(0) ) - { - network.columnsigmas[nin+i] = 1; - } + pbk = pbk+1; + } + } + + // + // Calculate cross-validation CE + // + cv = 0; + cv = cv-xlny(pal+pak, (pal+pak)/(pal+pak+pbl+pbk+1), _params); + cv = cv-xlny(pbl+pbk, (pbl+pbk)/(pal+pak+1+pbl+pbk), _params); + cv = cv-xlny(par-pak, (par-pak)/(par-pak+pbr-pbk+1), _params); + cv = cv-xlny(pbr-pbk, (pbr-pbk)/(par-pak+1+pbr-pbk), _params); + + // + // Compare with best + // + if( (double)(cv)<(double)(cvoptimal) ) + { + cvoptimal = cv; + koptimal = k; + } + + // + // update + // + pal = pal+pak; + pbl = pbl+pbk; + par = par-pak; + pbr = pbr-pbk; + } + cve = cvoptimal; + threshold = 0.5*(a[ties[koptimal]]+a[ties[koptimal+1]]); + pal = 0; + pbl = 0; + par = 0; + pbr = 0; + for(i=0; i<=n-1; i++) + { + if( (double)(a[i])<(double)(threshold) ) + { + if( c[i]==0 ) + { + pal = pal+1; + } + else + { + pbl = pbl+1; + } + } + else + { + if( c[i]==0 ) + { + par = par+1; + } + else + { + pbr = pbr+1; } } } + s = pal+pbl; + pal = pal/s; + pbl = pbl/s; + s = par+pbr; + par = par/s; + pbr = pbr/s; } /************************************************************************* - Internal subroutine. - Initialization for preprocessor based on a sample. + Optimal partition, internal subroutine. Fast version. - INPUT - Network - initialized neural network; - XY - sample, given by sparse matrix; - SSize - sample size. + Accepts: + A array[0..N-1] array of attributes array[0..N-1] + C array[0..N-1] array of class labels + TiesBuf array[0..N] temporaries (ties) + CntBuf array[0..2*NC-1] temporaries (counts) + Alpha centering factor (0<=alpha<=1, recommended value - 0.05) + BufR array[0..N-1] temporaries + BufI array[0..N-1] temporaries - OUTPUT - Network - neural network with initialised preprocessor. + Output: + Info error code (">0"=OK, "<0"=bad) + RMS training set RMS error + CVRMS leave-one-out RMS error + + Note: + content of all arrays is changed by subroutine; + it doesn't allocate temporaries. -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 11.12.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpinitpreprocessorsparse(multilayerperceptron network, - sparse.sparsematrix xy, - int ssize, + public static void dsoptimalsplit2fast(ref double[] a, + ref int[] c, + ref int[] tiesbuf, + ref int[] cntbuf, + ref double[] bufr, + ref int[] bufi, + int n, + int nc, + double alpha, + ref int info, + ref double threshold, + ref double rms, + ref double cvrms, alglib.xparams _params) { - int jmax = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int ntotal = 0; - int istart = 0; - int offs = 0; - int ntype = 0; - double[] means = new double[0]; - double[] sigmas = new double[0]; - double s = 0; int i = 0; - int j = 0; + int k = 0; + int cl = 0; + int tiecount = 0; + double cbest = 0; + double cc = 0; + int koptimal = 0; + int sl = 0; + int sr = 0; + double v = 0; + double w = 0; + double x = 0; + + info = 0; + threshold = 0; + rms = 0; + cvrms = 0; - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; // - // Means/Sigmas + // Test for errors in inputs // - if( mlpissoftmax(network, _params) ) - { - jmax = nin-1; - } - else - { - jmax = nin+nout-1; - } - means = new double[jmax+1]; - sigmas = new double[jmax+1]; - for(i=0; i<=jmax; i++) - { - means[i] = 0; - sigmas[i] = 0; - } - for(i=0; i<=ssize-1; i++) - { - sparse.sparsegetrow(xy, i, ref network.xyrow, _params); - for(j=0; j<=jmax; j++) - { - means[j] = means[j]+network.xyrow[j]; - } - } - for(i=0; i<=jmax; i++) + if( n<=0 || nc<2 ) { - means[i] = means[i]/ssize; + info = -1; + return; } - for(i=0; i<=ssize-1; i++) + for(i=0; i<=n-1; i++) { - sparse.sparsegetrow(xy, i, ref network.xyrow, _params); - for(j=0; j<=jmax; j++) + if( c[i]<0 || c[i]>=nc ) { - sigmas[j] = sigmas[j]+math.sqr(network.xyrow[j]-means[j]); + info = -2; + return; } } - for(i=0; i<=jmax; i++) - { - sigmas[i] = Math.Sqrt(sigmas[i]/ssize); - } + info = 1; // - // Inputs + // Tie // - for(i=0; i<=nin-1; i++) + dstiefasti(ref a, ref c, n, ref tiesbuf, ref tiecount, ref bufr, ref bufi, _params); + + // + // Special case: number of ties is 1. + // + if( tiecount==1 ) { - network.columnmeans[i] = means[i]; - network.columnsigmas[i] = sigmas[i]; - if( (double)(network.columnsigmas[i])==(double)(0) ) - { - network.columnsigmas[i] = 1; - } + info = -3; + return; } // - // Outputs + // General case, number of ties > 1 // - if( !mlpissoftmax(network, _params) ) + for(i=0; i<=2*nc-1; i++) { - for(i=0; i<=nout-1; i++) + cntbuf[i] = 0; + } + for(i=0; i<=n-1; i++) + { + cntbuf[nc+c[i]] = cntbuf[nc+c[i]]+1; + } + koptimal = -1; + threshold = a[n-1]; + cbest = math.maxrealnumber; + sl = 0; + sr = n; + for(k=0; k<=tiecount-2; k++) + { + + // + // first, move Kth tie from right to left + // + for(i=tiesbuf[k]; i<=tiesbuf[k+1]-1; i++) + { + cl = c[i]; + cntbuf[cl] = cntbuf[cl]+1; + cntbuf[nc+cl] = cntbuf[nc+cl]-1; + } + sl = sl+(tiesbuf[k+1]-tiesbuf[k]); + sr = sr-(tiesbuf[k+1]-tiesbuf[k]); + + // + // Calculate RMS error + // + v = 0; + for(i=0; i<=nc-1; i++) + { + w = cntbuf[i]; + v = v+w*math.sqr(w/sl-1); + v = v+(sl-w)*math.sqr(w/sl); + w = cntbuf[nc+i]; + v = v+w*math.sqr(w/sr-1); + v = v+(sr-w)*math.sqr(w/sr); + } + v = Math.Sqrt(v/(nc*n)); + + // + // Compare with best + // + x = (double)(2*sl)/(double)(sl+sr)-1; + cc = v*(1-alpha+alpha*math.sqr(x)); + if( (double)(cc)<(double)(cbest) ) { - offs = istart+(ntotal-nout+i)*nfieldwidth; - ntype = network.structinfo[offs+0]; // - // Linear outputs + // store split // - if( ntype==0 ) - { - network.columnmeans[nin+i] = means[nin+i]; - network.columnsigmas[nin+i] = sigmas[nin+i]; - if( (double)(network.columnsigmas[nin+i])==(double)(0) ) - { - network.columnsigmas[nin+i] = 1; - } - } + rms = v; + koptimal = k; + cbest = cc; // - // Bounded outputs (half-interval) + // calculate CVRMS error // - if( ntype==3 ) + cvrms = 0; + for(i=0; i<=nc-1; i++) { - s = means[nin+i]-network.columnmeans[nin+i]; - if( (double)(s)==(double)(0) ) + if( sl>1 ) { - s = Math.Sign(network.columnsigmas[nin+i]); + w = cntbuf[i]; + cvrms = cvrms+w*math.sqr((w-1)/(sl-1)-1); + cvrms = cvrms+(sl-w)*math.sqr(w/(sl-1)); } - if( (double)(s)==(double)(0) ) + else { - s = 1.0; + w = cntbuf[i]; + cvrms = cvrms+w*math.sqr((double)1/(double)nc-1); + cvrms = cvrms+(sl-w)*math.sqr((double)1/(double)nc); } - network.columnsigmas[nin+i] = Math.Sign(network.columnsigmas[nin+i])*Math.Abs(s); - if( (double)(network.columnsigmas[nin+i])==(double)(0) ) + if( sr>1 ) { - network.columnsigmas[nin+i] = 1; + w = cntbuf[nc+i]; + cvrms = cvrms+w*math.sqr((w-1)/(sr-1)-1); + cvrms = cvrms+(sr-w)*math.sqr(w/(sr-1)); + } + else + { + w = cntbuf[nc+i]; + cvrms = cvrms+w*math.sqr((double)1/(double)nc-1); + cvrms = cvrms+(sr-w)*math.sqr((double)1/(double)nc); } } + cvrms = Math.Sqrt(cvrms/(nc*n)); } } + + // + // Calculate threshold. + // Code is a bit complicated because there can be such + // numbers that 0.5(A+B) equals to A or B (if A-B=epsilon) + // + threshold = 0.5*(a[tiesbuf[koptimal]]+a[tiesbuf[koptimal+1]]); + if( (double)(threshold)<=(double)(a[tiesbuf[koptimal]]) ) + { + threshold = a[tiesbuf[koptimal+1]]; + } } /************************************************************************* - Internal subroutine. - Initialization for preprocessor based on a subsample. - - INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset; one sample = one row; - first NIn columns contain inputs, - next NOut columns - desired outputs. - SetSize - real size of XY, SetSize>=0; - Idx - subset of SubsetSize elements, array[SubsetSize]: - * Idx[I] stores row index in the original dataset which is - given by XY. Gradient is calculated with respect to rows - whose indexes are stored in Idx[]. - * Idx[] must store correct indexes; this function throws - an exception in case incorrect index (less than 0 or - larger than rows(XY)) is given - * Idx[] may store indexes in any order and even with - repetitions. - SubsetSize- number of elements in Idx[] array. - - OUTPUT: - Network - neural network with initialised preprocessor. - - NOTE: when SubsetSize<0 is used full dataset by call MLPInitPreprocessor - function. + Automatic non-optimal discretization, internal subroutine. -- ALGLIB -- - Copyright 23.08.2012 by Bochkanov Sergey + Copyright 22.05.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpinitpreprocessorsubset(multilayerperceptron network, - double[,] xy, - int setsize, - int[] idx, - int subsetsize, + public static void dssplitk(double[] a, + int[] c, + int n, + int nc, + int kmax, + ref int info, + ref double[] thresholds, + ref int ni, + ref double cve, alglib.xparams _params) { - int jmax = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int ntotal = 0; - int istart = 0; - int offs = 0; - int ntype = 0; - double[] means = new double[0]; - double[] sigmas = new double[0]; - double s = 0; - int npoints = 0; int i = 0; int j = 0; + int j1 = 0; + int k = 0; + int[] ties = new int[0]; + int tiecount = 0; + int[] p1 = new int[0]; + int[] p2 = new int[0]; + int[] cnt = new int[0]; + double v2 = 0; + int bestk = 0; + double bestcve = 0; + int[] bestsizes = new int[0]; + double curcve = 0; + int[] cursizes = new int[0]; - alglib.ap.assert(setsize>=0, "MLPInitPreprocessorSubset: SetSize<0"); - if( subsetsize<0 ) + a = (double[])a.Clone(); + c = (int[])c.Clone(); + info = 0; + thresholds = new double[0]; + ni = 0; + cve = 0; + + + // + // Test for errors in inputs + // + if( (n<=0 || nc<2) || kmax<2 ) { - mlpinitpreprocessor(network, xy, setsize, _params); + info = -1; return; } - alglib.ap.assert(subsetsize<=alglib.ap.len(idx), "MLPInitPreprocessorSubset: SubsetSize>Length(Idx)"); - npoints = setsize; - for(i=0; i<=subsetsize-1; i++) + for(i=0; i<=n-1; i++) { - alglib.ap.assert(idx[i]>=0, "MLPInitPreprocessorSubset: incorrect index of XY row(Idx[I]<0)"); - alglib.ap.assert(idx[i]<=npoints-1, "MLPInitPreprocessorSubset: incorrect index of XY row(Idx[I]>Rows(XY)-1)"); + if( c[i]<0 || c[i]>=nc ) + { + info = -2; + return; + } } - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; + info = 1; // - // Means/Sigmas + // Tie // - if( mlpissoftmax(network, _params) ) - { - jmax = nin-1; - } - else + dstie(ref a, n, ref ties, ref tiecount, ref p1, ref p2, _params); + for(i=0; i<=n-1; i++) { - jmax = nin+nout-1; + if( p2[i]!=i ) + { + k = c[i]; + c[i] = c[p2[i]]; + c[p2[i]] = k; + } } - means = new double[jmax+1]; - sigmas = new double[jmax+1]; - for(i=0; i<=jmax; i++) + + // + // Special cases + // + if( tiecount==1 ) { - means[i] = 0; - sigmas[i] = 0; + info = -3; + return; } - for(i=0; i<=subsetsize-1; i++) + + // + // General case: + // 0. allocate arrays + // + kmax = Math.Min(kmax, tiecount); + bestsizes = new int[kmax-1+1]; + cursizes = new int[kmax-1+1]; + cnt = new int[nc-1+1]; + + // + // General case: + // 1. prepare "weak" solution (two subintervals, divided at median) + // + v2 = math.maxrealnumber; + j = -1; + for(i=1; i<=tiecount-1; i++) { - for(j=0; j<=jmax; j++) + if( (double)(Math.Abs(ties[i]-0.5*(n-1)))<(double)(v2) ) { - means[j] = means[j]+xy[idx[i],j]; + v2 = Math.Abs(ties[i]-0.5*n); + j = i; } } - for(i=0; i<=jmax; i++) + alglib.ap.assert(j>0, "DSSplitK: internal error #1!"); + bestk = 2; + bestsizes[0] = ties[j]; + bestsizes[1] = n-j; + bestcve = 0; + for(i=0; i<=nc-1; i++) { - means[i] = means[i]/subsetsize; + cnt[i] = 0; } - for(i=0; i<=subsetsize-1; i++) + for(i=0; i<=j-1; i++) { - for(j=0; j<=jmax; j++) - { - sigmas[j] = sigmas[j]+math.sqr(xy[idx[i],j]-means[j]); - } + tieaddc(c, ties, i, nc, ref cnt, _params); } - for(i=0; i<=jmax; i++) + bestcve = bestcve+getcv(cnt, nc, _params); + for(i=0; i<=nc-1; i++) { - sigmas[i] = Math.Sqrt(sigmas[i]/subsetsize); + cnt[i] = 0; } - - // - // Inputs - // - for(i=0; i<=nin-1; i++) + for(i=j; i<=tiecount-1; i++) { - network.columnmeans[i] = means[i]; - network.columnsigmas[i] = sigmas[i]; - if( (double)(network.columnsigmas[i])==(double)(0) ) - { - network.columnsigmas[i] = 1; - } + tieaddc(c, ties, i, nc, ref cnt, _params); } + bestcve = bestcve+getcv(cnt, nc, _params); // - // Outputs + // General case: + // 2. Use greedy algorithm to find sub-optimal split in O(KMax*N) time // - if( !mlpissoftmax(network, _params) ) + for(k=2; k<=kmax; k++) { - for(i=0; i<=nout-1; i++) + + // + // Prepare greedy K-interval split + // + for(i=0; i<=k-1; i++) + { + cursizes[i] = 0; + } + i = 0; + j = 0; + while( j<=tiecount-1 && i<=k-1 ) { - offs = istart+(ntotal-nout+i)*nfieldwidth; - ntype = network.structinfo[offs+0]; // - // Linear outputs + // Rule: I-th bin is empty, fill it // - if( ntype==0 ) + if( cursizes[i]==0 ) { - network.columnmeans[nin+i] = means[nin+i]; - network.columnsigmas[nin+i] = sigmas[nin+i]; - if( (double)(network.columnsigmas[nin+i])==(double)(0) ) - { - network.columnsigmas[nin+i] = 1; - } + cursizes[i] = ties[j+1]-ties[j]; + j = j+1; + continue; } // - // Bounded outputs (half-interval) + // Rule: (K-1-I) bins left, (K-1-I) ties left (1 tie per bin); next bin // - if( ntype==3 ) + if( tiecount-j==k-1-i ) { - s = means[nin+i]-network.columnmeans[nin+i]; - if( (double)(s)==(double)(0) ) - { - s = Math.Sign(network.columnsigmas[nin+i]); - } - if( (double)(s)==(double)(0) ) - { - s = 1.0; - } - network.columnsigmas[nin+i] = Math.Sign(network.columnsigmas[nin+i])*Math.Abs(s); - if( (double)(network.columnsigmas[nin+i])==(double)(0) ) - { - network.columnsigmas[nin+i] = 1; - } + i = i+1; + continue; + } + + // + // Rule: last bin, always place in current + // + if( i==k-1 ) + { + cursizes[i] = cursizes[i]+ties[j+1]-ties[j]; + j = j+1; + continue; + } + + // + // Place J-th tie in I-th bin, or leave for I+1-th bin. + // + if( (double)(Math.Abs(cursizes[i]+ties[j+1]-ties[j]-(double)n/(double)k))<(double)(Math.Abs(cursizes[i]-(double)n/(double)k)) ) + { + cursizes[i] = cursizes[i]+ties[j+1]-ties[j]; + j = j+1; + } + else + { + i = i+1; + } + } + alglib.ap.assert(cursizes[k-1]!=0 && j==tiecount, "DSSplitK: internal error #1"); + + // + // Calculate CVE + // + curcve = 0; + j = 0; + for(i=0; i<=k-1; i++) + { + for(j1=0; j1<=nc-1; j1++) + { + cnt[j1] = 0; + } + for(j1=j; j1<=j+cursizes[i]-1; j1++) + { + cnt[c[j1]] = cnt[c[j1]]+1; + } + curcve = curcve+getcv(cnt, nc, _params); + j = j+cursizes[i]; + } + + // + // Choose best variant + // + if( (double)(curcve)<(double)(bestcve) ) + { + for(i=0; i<=k-1; i++) + { + bestsizes[i] = cursizes[i]; } + bestcve = curcve; + bestk = k; } } + + // + // Transform from sizes to thresholds + // + cve = bestcve; + ni = bestk; + thresholds = new double[ni-2+1]; + j = bestsizes[0]; + for(i=1; i<=bestk-1; i++) + { + thresholds[i-1] = 0.5*(a[j-1]+a[j]); + j = j+bestsizes[i]; + } } /************************************************************************* - Internal subroutine. - Initialization for preprocessor based on a subsample. + Automatic optimal discretization, internal subroutine. - INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset, given by sparse matrix; - one sample = one row; - first NIn columns contain inputs, - next NOut columns - desired outputs. - SetSize - real size of XY, SetSize>=0; - Idx - subset of SubsetSize elements, array[SubsetSize]: - * Idx[I] stores row index in the original dataset which is - given by XY. Gradient is calculated with respect to rows - whose indexes are stored in Idx[]. - * Idx[] must store correct indexes; this function throws - an exception in case incorrect index (less than 0 or - larger than rows(XY)) is given - * Idx[] may store indexes in any order and even with - repetitions. - SubsetSize- number of elements in Idx[] array. - - OUTPUT: - Network - neural network with initialised preprocessor. - - NOTE: when SubsetSize<0 is used full dataset by call - MLPInitPreprocessorSparse function. - -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 22.05.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpinitpreprocessorsparsesubset(multilayerperceptron network, - sparse.sparsematrix xy, - int setsize, - int[] idx, - int subsetsize, + public static void dsoptimalsplitk(double[] a, + int[] c, + int n, + int nc, + int kmax, + ref int info, + ref double[] thresholds, + ref int ni, + ref double cve, alglib.xparams _params) { - int jmax = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int ntotal = 0; - int istart = 0; - int offs = 0; - int ntype = 0; - double[] means = new double[0]; - double[] sigmas = new double[0]; - double s = 0; - int npoints = 0; int i = 0; int j = 0; + int s = 0; + int jl = 0; + int jr = 0; + double v2 = 0; + int[] ties = new int[0]; + int tiecount = 0; + int[] p1 = new int[0]; + int[] p2 = new int[0]; + double cvtemp = 0; + int[] cnt = new int[0]; + int[] cnt2 = new int[0]; + double[,] cv = new double[0,0]; + int[,] splits = new int[0,0]; + int k = 0; + int koptimal = 0; + double cvoptimal = 0; + + a = (double[])a.Clone(); + c = (int[])c.Clone(); + info = 0; + thresholds = new double[0]; + ni = 0; + cve = 0; - alglib.ap.assert(setsize>=0, "MLPInitPreprocessorSparseSubset: SetSize<0"); - if( subsetsize<0 ) - { - mlpinitpreprocessorsparse(network, xy, setsize, _params); - return; - } - alglib.ap.assert(subsetsize<=alglib.ap.len(idx), "MLPInitPreprocessorSparseSubset: SubsetSize>Length(Idx)"); - npoints = setsize; - for(i=0; i<=subsetsize-1; i++) - { - alglib.ap.assert(idx[i]>=0, "MLPInitPreprocessorSparseSubset: incorrect index of XY row(Idx[I]<0)"); - alglib.ap.assert(idx[i]<=npoints-1, "MLPInitPreprocessorSparseSubset: incorrect index of XY row(Idx[I]>Rows(XY)-1)"); - } - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; // - // Means/Sigmas + // Test for errors in inputs // - if( mlpissoftmax(network, _params) ) - { - jmax = nin-1; - } - else - { - jmax = nin+nout-1; - } - means = new double[jmax+1]; - sigmas = new double[jmax+1]; - for(i=0; i<=jmax; i++) + if( (n<=0 || nc<2) || kmax<2 ) { - means[i] = 0; - sigmas[i] = 0; + info = -1; + return; } - for(i=0; i<=subsetsize-1; i++) + for(i=0; i<=n-1; i++) { - sparse.sparsegetrow(xy, idx[i], ref network.xyrow, _params); - for(j=0; j<=jmax; j++) + if( c[i]<0 || c[i]>=nc ) { - means[j] = means[j]+network.xyrow[j]; + info = -2; + return; } } - for(i=0; i<=jmax; i++) - { - means[i] = means[i]/subsetsize; - } - for(i=0; i<=subsetsize-1; i++) + info = 1; + + // + // Tie + // + dstie(ref a, n, ref ties, ref tiecount, ref p1, ref p2, _params); + for(i=0; i<=n-1; i++) { - sparse.sparsegetrow(xy, idx[i], ref network.xyrow, _params); - for(j=0; j<=jmax; j++) + if( p2[i]!=i ) { - sigmas[j] = sigmas[j]+math.sqr(network.xyrow[j]-means[j]); + k = c[i]; + c[i] = c[p2[i]]; + c[p2[i]] = k; } } - for(i=0; i<=jmax; i++) - { - sigmas[i] = Math.Sqrt(sigmas[i]/subsetsize); - } // - // Inputs + // Special cases // - for(i=0; i<=nin-1; i++) + if( tiecount==1 ) { - network.columnmeans[i] = means[i]; - network.columnsigmas[i] = sigmas[i]; - if( (double)(network.columnsigmas[i])==(double)(0) ) - { - network.columnsigmas[i] = 1; - } + info = -3; + return; } // - // Outputs + // General case + // Use dynamic programming to find best split in O(KMax*NC*TieCount^2) time // - if( !mlpissoftmax(network, _params) ) + kmax = Math.Min(kmax, tiecount); + cv = new double[kmax-1+1, tiecount-1+1]; + splits = new int[kmax-1+1, tiecount-1+1]; + cnt = new int[nc-1+1]; + cnt2 = new int[nc-1+1]; + for(j=0; j<=nc-1; j++) { - for(i=0; i<=nout-1; i++) + cnt[j] = 0; + } + for(j=0; j<=tiecount-1; j++) + { + tieaddc(c, ties, j, nc, ref cnt, _params); + splits[0,j] = 0; + cv[0,j] = getcv(cnt, nc, _params); + } + for(k=1; k<=kmax-1; k++) + { + for(j=0; j<=nc-1; j++) + { + cnt[j] = 0; + } + + // + // Subtask size J in [K..TieCount-1]: + // optimal K-splitting on ties from 0-th to J-th. + // + for(j=k; j<=tiecount-1; j++) { - offs = istart+(ntotal-nout+i)*nfieldwidth; - ntype = network.structinfo[offs+0]; // - // Linear outputs + // Update Cnt - let it contain classes of ties from K-th to J-th // - if( ntype==0 ) - { - network.columnmeans[nin+i] = means[nin+i]; - network.columnsigmas[nin+i] = sigmas[nin+i]; - if( (double)(network.columnsigmas[nin+i])==(double)(0) ) - { - network.columnsigmas[nin+i] = 1; - } - } + tieaddc(c, ties, j, nc, ref cnt, _params); // - // Bounded outputs (half-interval) + // Search for optimal split point S in [K..J] // - if( ntype==3 ) + for(i=0; i<=nc-1; i++) { - s = means[nin+i]-network.columnmeans[nin+i]; - if( (double)(s)==(double)(0) ) - { - s = Math.Sign(network.columnsigmas[nin+i]); - } - if( (double)(s)==(double)(0) ) - { - s = 1.0; - } - network.columnsigmas[nin+i] = Math.Sign(network.columnsigmas[nin+i])*Math.Abs(s); - if( (double)(network.columnsigmas[nin+i])==(double)(0) ) + cnt2[i] = cnt[i]; + } + cv[k,j] = cv[k-1,j-1]+getcv(cnt2, nc, _params); + splits[k,j] = j; + for(s=k+1; s<=j; s++) + { + + // + // Update Cnt2 - let it contain classes of ties from S-th to J-th + // + tiesubc(c, ties, s-1, nc, ref cnt2, _params); + + // + // Calculate CVE + // + cvtemp = cv[k-1,s-1]+getcv(cnt2, nc, _params); + if( (double)(cvtemp)<(double)(cv[k,j]) ) { - network.columnsigmas[nin+i] = 1; + cv[k,j] = cvtemp; + splits[k,j] = s; } } } } + + // + // Choose best partition, output result + // + koptimal = -1; + cvoptimal = math.maxrealnumber; + for(k=0; k<=kmax-1; k++) + { + if( (double)(cv[k,tiecount-1])<(double)(cvoptimal) ) + { + cvoptimal = cv[k,tiecount-1]; + koptimal = k; + } + } + alglib.ap.assert(koptimal>=0, "DSOptimalSplitK: internal error #1!"); + if( koptimal==0 ) + { + + // + // Special case: best partition is one big interval. + // Even 2-partition is not better. + // This is possible when dealing with "weak" predictor variables. + // + // Make binary split as close to the median as possible. + // + v2 = math.maxrealnumber; + j = -1; + for(i=1; i<=tiecount-1; i++) + { + if( (double)(Math.Abs(ties[i]-0.5*(n-1)))<(double)(v2) ) + { + v2 = Math.Abs(ties[i]-0.5*(n-1)); + j = i; + } + } + alglib.ap.assert(j>0, "DSOptimalSplitK: internal error #2!"); + thresholds = new double[0+1]; + thresholds[0] = 0.5*(a[ties[j-1]]+a[ties[j]]); + ni = 2; + cve = 0; + for(i=0; i<=nc-1; i++) + { + cnt[i] = 0; + } + for(i=0; i<=j-1; i++) + { + tieaddc(c, ties, i, nc, ref cnt, _params); + } + cve = cve+getcv(cnt, nc, _params); + for(i=0; i<=nc-1; i++) + { + cnt[i] = 0; + } + for(i=j; i<=tiecount-1; i++) + { + tieaddc(c, ties, i, nc, ref cnt, _params); + } + cve = cve+getcv(cnt, nc, _params); + } + else + { + + // + // General case: 2 or more intervals + // + // NOTE: we initialize both JL and JR (left and right bounds), + // altough algorithm needs only JL. + // + thresholds = new double[koptimal-1+1]; + ni = koptimal+1; + cve = cv[koptimal,tiecount-1]; + jl = splits[koptimal,tiecount-1]; + jr = tiecount-1; + for(k=koptimal; k>=1; k--) + { + thresholds[k-1] = 0.5*(a[ties[jl-1]]+a[ties[jl]]); + jr = jl-1; + jl = splits[k-1,jl-1]; + } + apserv.touchint(ref jr, _params); + } } /************************************************************************* - Returns information about initialized network: number of inputs, outputs, - weights. - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static void mlpproperties(multilayerperceptron network, - ref int nin, - ref int nout, - ref int wcount, - alglib.xparams _params) - { - nin = 0; - nout = 0; - wcount = 0; - - nin = network.structinfo[1]; - nout = network.structinfo[2]; - wcount = network.structinfo[4]; - } - - - /************************************************************************* - Returns number of "internal", low-level neurons in the network (one which - is stored in StructInfo). - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey - *************************************************************************/ - public static int mlpntotal(multilayerperceptron network, - alglib.xparams _params) - { - int result = 0; - - result = network.structinfo[3]; - return result; - } - - - /************************************************************************* - Returns number of inputs. - - -- ALGLIB -- - Copyright 19.10.2011 by Bochkanov Sergey + Internal function *************************************************************************/ - public static int mlpgetinputscount(multilayerperceptron network, + private static double xlny(double x, + double y, alglib.xparams _params) { - int result = 0; + double result = 0; - result = network.structinfo[1]; + if( (double)(x)==(double)(0) ) + { + result = 0; + } + else + { + result = x*Math.Log(y); + } return result; } /************************************************************************* - Returns number of outputs. - - -- ALGLIB -- - Copyright 19.10.2011 by Bochkanov Sergey + Internal function, + returns number of samples of class I in Cnt[I] *************************************************************************/ - public static int mlpgetoutputscount(multilayerperceptron network, + private static double getcv(int[] cnt, + int nc, alglib.xparams _params) { - int result = 0; + double result = 0; + int i = 0; + double s = 0; - result = network.structinfo[2]; + s = 0; + for(i=0; i<=nc-1; i++) + { + s = s+cnt[i]; + } + result = 0; + for(i=0; i<=nc-1; i++) + { + result = result-xlny(cnt[i], cnt[i]/(s+nc-1), _params); + } return result; } /************************************************************************* - Returns number of weights. - - -- ALGLIB -- - Copyright 19.10.2011 by Bochkanov Sergey + Internal function, adds number of samples of class I in tie NTie to Cnt[I] *************************************************************************/ - public static int mlpgetweightscount(multilayerperceptron network, + private static void tieaddc(int[] c, + int[] ties, + int ntie, + int nc, + ref int[] cnt, alglib.xparams _params) { - int result = 0; + int i = 0; - result = network.structinfo[4]; - return result; + for(i=ties[ntie]; i<=ties[ntie+1]-1; i++) + { + cnt[c[i]] = cnt[c[i]]+1; + } } /************************************************************************* - Tells whether network is SOFTMAX-normalized (i.e. classifier) or not. - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Internal function, subtracts number of samples of class I in tie NTie to Cnt[I] *************************************************************************/ - public static bool mlpissoftmax(multilayerperceptron network, + private static void tiesubc(int[] c, + int[] ties, + int ntie, + int nc, + ref int[] cnt, alglib.xparams _params) { - bool result = new bool(); + int i = 0; - result = network.structinfo[6]==1; - return result; + for(i=ties[ntie]; i<=ties[ntie+1]-1; i++) + { + cnt[c[i]] = cnt[c[i]]-1; + } } + } + public partial class lda + { /************************************************************************* - This function returns total number of layers (including input, hidden and - output layers). + Multiclass Fisher LDA - -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey - *************************************************************************/ - public static int mlpgetlayerscount(multilayerperceptron network, - alglib.xparams _params) - { - int result = 0; + The function finds coefficients of a linear combination which optimally + separates training set. Most suited for 2-class problems, see fisherldan() + for an variant that returns N-dimensional basis. - result = alglib.ap.len(network.hllayersizes); - return result; - } + INPUT PARAMETERS: + XY - training set, array[NPoints,NVars+1]. + First NVars columns store values of independent + variables, the next column stores class index (from 0 + to NClasses-1) which dataset element belongs to. + Fractional values are rounded to the nearest integer. + The class index must be in the [0,NClasses-1] range, + an exception is generated otherwise. + NPoints - training set size, NPoints>=0 + NVars - number of independent variables, NVars>=1 + NClasses - number of classes, NClasses>=2 - /************************************************************************* - This function returns size of K-th layer. + OUTPUT PARAMETERS: + W - linear combination coefficients, array[NVars] - K=0 corresponds to input layer, K=CNT-1 corresponds to output layer. + ! FREE EDITION OF ALGLIB: + ! + ! Free Edition of ALGLIB supports following important features for this + ! function: + ! * C++ version: x64 SIMD support using C++ intrinsics + ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics + ! + ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB + ! Reference Manual in order to find out how to activate SIMD support + ! in ALGLIB. - Size of the output layer is always equal to the number of outputs, although - when we have softmax-normalized network, last neuron doesn't have any - connections - it is just zero. + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! * hardware vendor (Intel, ARM) implementations of linear algebra and + ! other primitives (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 31.05.2008 by Bochkanov Sergey *************************************************************************/ - public static int mlpgetlayersize(multilayerperceptron network, - int k, + public static void fisherlda(double[,] xy, + int npoints, + int nvars, + int nclasses, + ref double[] w, alglib.xparams _params) { - int result = 0; + double[,] w2 = new double[0,0]; + int i_ = 0; - alglib.ap.assert(k>=0 && k=0 + NVars - number of independent variables, NVars>=1 + NClasses - number of classes, NClasses>=2 + OUTPUT PARAMETERS: - Mean - mean term - Sigma - sigma term, guaranteed to be nonzero. + W - basis, array[NVars,NVars] + columns of matrix stores basis vectors, sorted by + quality of training set separation (in descending order) - I-th input is passed through linear transformation - IN[i] = (IN[i]-Mean)/Sigma - before feeding to the network + ! FREE EDITION OF ALGLIB: + ! + ! Free Edition of ALGLIB supports following important features for this + ! function: + ! * C++ version: x64 SIMD support using C++ intrinsics + ! * C# version: x64 SIMD support using NET5/NetCore hardware intrinsics + ! + ! We recommend you to read 'Compiling ALGLIB' section of the ALGLIB + ! Reference Manual in order to find out how to activate SIMD support + ! in ALGLIB. + + ! COMMERCIAL EDITION OF ALGLIB: + ! + ! Commercial Edition of ALGLIB includes following important improvements + ! of this function: + ! * high-performance native backend with same C# interface (C# version) + ! * multithreading support (C++ and C# versions) + ! * hardware vendor (Intel, ARM) implementations of linear algebra and + ! other primitives (C++ and C# versions) + ! + ! We recommend you to read 'Working with commercial version' section of + ! ALGLIB Reference Manual in order to find out how to use performance- + ! related features provided by commercial edition of ALGLIB. -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 31.05.2008 by Bochkanov Sergey *************************************************************************/ - public static void mlpgetinputscaling(multilayerperceptron network, - int i, - ref double mean, - ref double sigma, + public static void fisherldan(double[,] xy, + int npoints, + int nvars, + int nclasses, + ref double[,] w, alglib.xparams _params) { - mean = 0; - sigma = 0; + int i = 0; + int j = 0; + int k = 0; + int m = 0; + double v = 0; + int[] c = new int[0]; + double[] mu = new double[0]; + double[,] muc = new double[0,0]; + int[] nc = new int[0]; + double[,] sw = new double[0,0]; + double[,] st = new double[0,0]; + double[,] z = new double[0,0]; + double[,] z2 = new double[0,0]; + double[,] tm = new double[0,0]; + double[,] sbroot = new double[0,0]; + double[,] a = new double[0,0]; + double[,] xyc = new double[0,0]; + double[,] xyproj = new double[0,0]; + double[,] wproj = new double[0,0]; + double[] tf = new double[0]; + double[] d = new double[0]; + double[] d2 = new double[0]; + double[] work = new double[0]; + int i_ = 0; - alglib.ap.assert(i>=0 && i=nclasses ) + { + alglib.ap.assert(false, "FisherLDAN: class index is <0 or >NClasses-1"); + } + } + + // + // Special case: NPoints<=1 + // Degenerate task. + // + if( npoints<=1 ) + { + w = new double[nvars, nvars]; + for(i=0; i<=nvars-1; i++) + { + for(j=0; j<=nvars-1; j++) + { + if( i==j ) + { + w[i,j] = 1; + } + else + { + w[i,j] = 0; + } + } + } + return; + } + + // + // Prepare temporaries + // + tf = new double[nvars]; + work = new double[Math.Max(nvars, npoints)+1]; + xyc = new double[npoints, nvars]; + + // + // Convert class labels from reals to integers (just for convenience) + // + c = new int[npoints]; + for(i=0; i<=npoints-1; i++) + { + c[i] = (int)Math.Round(xy[i,nvars]); + } + + // + // Calculate class sizes, class means + // + mu = new double[nvars]; + muc = new double[nclasses, nvars]; + nc = new int[nclasses]; + for(j=0; j<=nvars-1; j++) + { + mu[j] = 0; + } + for(i=0; i<=nclasses-1; i++) + { + nc[i] = 0; + for(j=0; j<=nvars-1; j++) + { + muc[i,j] = 0; + } + } + for(i=0; i<=npoints-1; i++) + { + for(i_=0; i_<=nvars-1;i_++) + { + mu[i_] = mu[i_] + xy[i,i_]; + } + for(i_=0; i_<=nvars-1;i_++) + { + muc[c[i],i_] = muc[c[i],i_] + xy[i,i_]; + } + nc[c[i]] = nc[c[i]]+1; + } + for(i=0; i<=nclasses-1; i++) + { + v = (double)1/(double)nc[i]; + for(i_=0; i_<=nvars-1;i_++) + { + muc[i,i_] = v*muc[i,i_]; + } + } + v = (double)1/(double)npoints; + for(i_=0; i_<=nvars-1;i_++) + { + mu[i_] = v*mu[i_]; + } + + // + // Create ST matrix + // + st = new double[nvars, nvars]; + for(i=0; i<=nvars-1; i++) + { + for(j=0; j<=nvars-1; j++) + { + st[i,j] = 0; + } + } + for(k=0; k<=npoints-1; k++) + { + for(i_=0; i_<=nvars-1;i_++) + { + xyc[k,i_] = xy[k,i_]; + } + for(i_=0; i_<=nvars-1;i_++) + { + xyc[k,i_] = xyc[k,i_] - mu[i_]; + } + } + ablas.rmatrixgemm(nvars, nvars, npoints, 1.0, xyc, 0, 0, 1, xyc, 0, 0, 0, 0.0, st, 0, 0, _params); + + // + // Create SW matrix + // + sw = new double[nvars, nvars]; + for(i=0; i<=nvars-1; i++) + { + for(j=0; j<=nvars-1; j++) + { + sw[i,j] = 0; + } + } + for(k=0; k<=npoints-1; k++) + { + for(i_=0; i_<=nvars-1;i_++) + { + xyc[k,i_] = xy[k,i_]; + } + for(i_=0; i_<=nvars-1;i_++) + { + xyc[k,i_] = xyc[k,i_] - muc[c[k],i_]; + } + } + ablas.rmatrixgemm(nvars, nvars, npoints, 1.0, xyc, 0, 0, 1, xyc, 0, 0, 0, 0.0, sw, 0, 0, _params); + + // + // Maximize ratio J=(w'*ST*w)/(w'*SW*w). + // + // First, make transition from w to v such that w'*ST*w becomes v'*v: + // v = root(ST)*w = R*w + // R = root(D)*Z' + // w = (root(ST)^-1)*v = RI*v + // RI = Z*inv(root(D)) + // J = (v'*v)/(v'*(RI'*SW*RI)*v) + // ST = Z*D*Z' + // + // so we have + // + // J = (v'*v) / (v'*(inv(root(D))*Z'*SW*Z*inv(root(D)))*v) = + // = (v'*v) / (v'*A*v) + // + if( !evd.smatrixevd(st, nvars, 1, true, ref d, ref z, _params) ) { - sigma = 1; + alglib.ap.assert(false, "FisherLDAN: EVD solver failure"); } - } - - - /************************************************************************* - This function returns offset/scaling coefficients for I-th output of the - network. - - INPUT PARAMETERS: - Network - network - I - input index - - OUTPUT PARAMETERS: - Mean - mean term - Sigma - sigma term, guaranteed to be nonzero. - - I-th output is passed through linear transformation - OUT[i] = OUT[i]*Sigma+Mean - before returning it to user. In case we have SOFTMAX-normalized network, - we return (Mean,Sigma)=(0.0,1.0). - - -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey - *************************************************************************/ - public static void mlpgetoutputscaling(multilayerperceptron network, - int i, - ref double mean, - ref double sigma, - alglib.xparams _params) - { - mean = 0; - sigma = 0; - - alglib.ap.assert(i>=0 && i=0 && i=0 && i=0, "MLPSetNeuronInfo: incorrect (nonexistent) layer or neuron index"); + OUTPUT PARAMETERS + Buf - external buffer. - // - // activation function - // - if( network.hlneurons[highlevelidx*hlnfieldwidth+2]>=0 ) - { - activationoffset = istart+network.hlneurons[highlevelidx*hlnfieldwidth+2]*nfieldwidth; - network.structinfo[activationoffset+0] = fkind; - } - else - { - alglib.ap.assert(fkind==0, "MLPSetNeuronInfo: you try to set activation function for neuron which can not have one"); - } - // - // Threshold - // - if( network.hlneurons[highlevelidx*hlnfieldwidth+3]>=0 ) - { - network.weights[network.hlneurons[highlevelidx*hlnfieldwidth+3]] = threshold; - } - else + IMPORTANT: buffer object should be used only with model which was used to + initialize buffer. Any attempt to use buffer with different + object is dangerous - you may get integrity check failure + (exception) because sizes of internal arrays do not fit to + dimensions of the model structure. + + -- ALGLIB -- + Copyright 15.02.2019 by Bochkanov Sergey + *************************************************************************/ + public static void knncreatebuffer(knnmodel model, + knnbuffer buf, + alglib.xparams _params) + { + if( !model.isdummy ) { - alglib.ap.assert((double)(threshold)==(double)(0), "MLPSetNeuronInfo: you try to set non-zero threshold for neuron which can not have one"); + nearestneighbor.kdtreecreaterequestbuffer(model.tree, buf.treebuf, _params); } + buf.x = new double[model.nvars]; + buf.y = new double[model.nout]; } /************************************************************************* - This function modifies information about connection from I0-th neuron of - K0-th layer to I1-th neuron of K1-th layer. + This subroutine creates KNNBuilder object which is used to train KNN models. + + By default, new builder stores empty dataset and some reasonable default + settings. At the very least, you should specify dataset prior to building + KNN model. You can also tweak settings of the model construction algorithm + (recommended, although default settings should work well). + + Following actions are mandatory: + * calling knnbuildersetdataset() to specify dataset + * calling knnbuilderbuildknnmodel() to build KNN model using current + dataset and default settings + + Additionally, you may call: + * knnbuildersetnorm() to change norm being used INPUT PARAMETERS: - Network - network - K0 - layer index - I0 - neuron index (within layer) - K1 - layer index - I1 - neuron index (within layer) - W - connection weight (must be zero for non-existent - connections) + none - This function: - 1. throws exception if layer or neuron with given index do not exists. - 2. throws exception if you try to set non-zero weight for non-existent - connection + OUTPUT PARAMETERS: + S - KNN builder -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpsetweight(multilayerperceptron network, - int k0, - int i0, - int k1, - int i1, - double w, + public static void knnbuildercreate(knnbuilder s, alglib.xparams _params) { - int ccnt = 0; - int highlevelidx = 0; - - ccnt = alglib.ap.len(network.hlconnections)/hlconnfieldwidth; // - // check params + // Empty dataset // - alglib.ap.assert(k0>=0 && k0=0 && i0=0 && k1=0 && i1=0 ) - { - network.weights[network.hlconnections[highlevelidx*hlconnfieldwidth+4]] = w; - } - else - { - alglib.ap.assert((double)(w)==(double)(0), "MLPSetWeight: you try to set non-zero weight for non-existent connection"); - } + s.knnnrm = 2; } /************************************************************************* - Neural network activation function + Specifies regression problem (one or more continuous output variables are + predicted). There also exists "classification" version of this function. + + This subroutine adds dense dataset to the internal storage of the builder + object. Specifying your dataset in the dense format means that the dense + version of the KNN construction algorithm will be invoked. INPUT PARAMETERS: - NET - neuron input - K - function index (zero for linear function) + S - KNN builder object + XY - array[NPoints,NVars+NOut] (note: actual size can be + larger, only leading part is used anyway), dataset: + * first NVars elements of each row store values of the + independent variables + * next NOut elements store values of the dependent + variables + NPoints - number of rows in the dataset, NPoints>=1 + NVars - number of independent variables, NVars>=1 + NOut - number of dependent variables, NOut>=1 OUTPUT PARAMETERS: - F - function - DF - its derivative - D2F - its second derivative + S - KNN builder -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpactivationfunction(double net, - int k, - ref double f, - ref double df, - ref double d2f, + public static void knnbuildersetdatasetreg(knnbuilder s, + double[,] xy, + int npoints, + int nvars, + int nout, alglib.xparams _params) { - double net2 = 0; - double arg = 0; - double root = 0; - double r = 0; - - f = 0; - df = 0; - d2f = 0; + int i = 0; + int j = 0; - if( k==0 || k==-5 ) - { - f = net; - df = 1; - d2f = 0; - return; - } - if( k==1 ) + + // + // Check parameters + // + alglib.ap.assert(npoints>=1, "knnbuildersetdatasetreg: npoints<1"); + alglib.ap.assert(nvars>=1, "knnbuildersetdatasetreg: nvars<1"); + alglib.ap.assert(nout>=1, "knnbuildersetdatasetreg: nout<1"); + alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "knnbuildersetdatasetreg: rows(xy)=nvars+nout, "knnbuildersetdatasetreg: cols(xy)=(double)(0) ) - { - net2 = net*net; - arg = net2+1; - root = Math.Sqrt(arg); - f = net+root; - r = net/root; - df = 1+r; - d2f = (root-net*r)/arg; - } - else + for(j=0; j<=nout-1; j++) { - f = Math.Exp(net); - df = f; - d2f = f; + s.dsrval[i*nout+j] = xy[i,nvars+j]; } - return; - } - if( k==2 ) - { - f = Math.Exp(-math.sqr(net)); - df = -(2*net*f); - d2f = -(2*(f+df*net)); - return; } - f = 0; - df = 0; - d2f = 0; } /************************************************************************* - Procesing + Specifies classification problem (two or more classes are predicted). + There also exists "regression" version of this function. + + This subroutine adds dense dataset to the internal storage of the builder + object. Specifying your dataset in the dense format means that the dense + version of the KNN construction algorithm will be invoked. INPUT PARAMETERS: - Network - neural network - X - input vector, array[0..NIn-1]. + S - KNN builder object + XY - array[NPoints,NVars+1] (note: actual size can be + larger, only leading part is used anyway), dataset: + * first NVars elements of each row store values of the + independent variables + * next element stores class index, in [0,NClasses) + NPoints - number of rows in the dataset, NPoints>=1 + NVars - number of independent variables, NVars>=1 + NClasses - number of classes, NClasses>=2 OUTPUT PARAMETERS: - Y - result. Regression estimate when solving regression task, - vector of posterior probabilities for classification task. - - See also MLPProcessI + S - KNN builder -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpprocess(multilayerperceptron network, - double[] x, - ref double[] y, + public static void knnbuildersetdatasetcls(knnbuilder s, + double[,] xy, + int npoints, + int nvars, + int nclasses, alglib.xparams _params) { - if( alglib.ap.len(y)=1, "knnbuildersetdatasetcls: npoints<1"); + alglib.ap.assert(nvars>=1, "knnbuildersetdatasetcls: nvars<1"); + alglib.ap.assert(nclasses>=2, "knnbuildersetdatasetcls: nclasses<2"); + alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "knnbuildersetdatasetcls: rows(xy)=nvars+1, "knnbuildersetdatasetcls: cols(xy)=0 && j1, Eps=0 corresponding to the "K nearest neighbors algorithm" + * K>=1, Eps>0 corresponding to "approximate nearest neighbors algorithm" + + An approximate KNN is a good option for high-dimensional datasets (exact + KNN works slowly when dimensions count grows). + + An ALGLIB implementation of kd-trees is used to perform k-nn searches. ! COMMERCIAL EDITION OF ALGLIB: ! @@ -16539,2337 +13929,2070 @@ Error of the neural network on dataset. ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. - - RESULT: - sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + S - KNN builder object + K - number of neighbors to search for, K>=1 + Eps - approximation factor: + * Eps=0 means that exact kNN search is performed + * Eps>0 means that (1+Eps)-approximate search is performed - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + OUTPUT PARAMETERS: + Model - KNN model + Rep - report -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlperror(multilayerperceptron network, - double[,] xy, - int npoints, + public static void knnbuilderbuildknnmodel(knnbuilder s, + int k, + double eps, + knnmodel model, + knnreport rep, alglib.xparams _params) { - double result = 0; + int i = 0; + int j = 0; + int nvars = 0; + int nout = 0; + int npoints = 0; + bool iscls = new bool(); + double[,] xy = new double[0,0]; + int[] tags = new int[0]; - alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "MLPError: XY has less than NPoints rows"); - if( npoints>0 ) + npoints = s.npoints; + nvars = s.nvars; + nout = s.nout; + iscls = s.iscls; + + // + // Check settings + // + alglib.ap.assert(k>=1, "knnbuilderbuildknnmodel: k<1"); + alglib.ap.assert(math.isfinite(eps) && (double)(eps)>=(double)(0), "knnbuilderbuildknnmodel: eps<0"); + + // + // Prepare output + // + clearreport(rep, _params); + model.nvars = nvars; + model.nout = nout; + model.iscls = iscls; + model.k = k; + model.eps = eps; + model.isdummy = false; + + // + // Quick exit for empty dataset + // + if( s.dstype==-1 ) { - if( mlpissoftmax(network, _params) ) + model.isdummy = true; + return; + } + + // + // Build kd-tree + // + if( iscls ) + { + xy = new double[npoints, nvars+1]; + tags = new int[npoints]; + for(i=0; i<=npoints-1; i++) { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+1, "MLPError: XY has less than NIn+1 columns"); + for(j=0; j<=nvars-1; j++) + { + xy[i,j] = s.dsdata[i,j]; + } + xy[i,nvars] = s.dsival[i]; + tags[i] = s.dsival[i]; } - else + nearestneighbor.kdtreebuildtagged(xy, tags, npoints, nvars, 0, s.knnnrm, model.tree, _params); + } + else + { + xy = new double[npoints, nvars+nout]; + for(i=0; i<=npoints-1; i++) { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPError: XY has less than NIn+NOut columns"); + for(j=0; j<=nvars-1; j++) + { + xy[i,j] = s.dsdata[i,j]; + } + for(j=0; j<=nout-1; j++) + { + xy[i,nvars+j] = s.dsrval[i*nout+j]; + } } + nearestneighbor.kdtreebuild(xy, npoints, nvars, nout, s.knnnrm, model.tree, _params); } - mlpallerrorsx(network, xy, network.dummysxy, npoints, 0, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = math.sqr(network.err.rmserror)*npoints*mlpgetoutputscount(network, _params)/2; - return result; + + // + // Build buffer + // + knncreatebuffer(model, model.buffer, _params); + + // + // Report + // + knnallerrors(model, xy, npoints, rep, _params); } /************************************************************************* - Error of the neural network on dataset given by sparse matrix. + Changing search settings of KNN model. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + K and EPS parameters of KNN (AKNN) search are specified during model + construction. However, plain KNN algorithm with Euclidean distance allows + you to change them at any moment. + + NOTE: future versions of KNN model may support advanced versions of KNN, + such as NCA or LMNN. It is possible that such algorithms won't allow + you to change search settings on the fly. If you call this function + for an algorithm which does not support on-the-fly changes, it will + throw an exception. INPUT PARAMETERS: - Network - neural network - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0 + Model - KNN model + K - K>=1, neighbors count + EPS - accuracy of the EPS-approximate NN search. Set to 0.0, if + you want to perform "classic" KNN search. Specify larger + values if you need to speed-up high-dimensional KNN + queries. - RESULT: - sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) + OUTPUT PARAMETERS: + nothing on success, exception on failure - DATASET FORMAT: + -- ALGLIB -- + Copyright 15.02.2019 by Bochkanov Sergey + *************************************************************************/ + public static void knnrewritekeps(knnmodel model, + int k, + double eps, + alglib.xparams _params) + { + alglib.ap.assert(k>=1, "knnrewritekeps: k<1"); + alglib.ap.assert(math.isfinite(eps) && (double)(eps)>=(double)(0), "knnrewritekeps: eps<0"); + model.k = k; + model.eps = eps; + } + + + /************************************************************************* + Inference using KNN model. + + See also knnprocess0(), knnprocessi() and knnclassify() for options with a + bit more convenient interface. + + IMPORTANT: this function is thread-unsafe and modifies internal structures + of the model! You can not use same model object for parallel + evaluation from several threads. + + Use knntsprocess() with independent thread-local buffers, if + you need thread-safe evaluation. - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + INPUT PARAMETERS: + Model - KNN model + X - input vector, array[0..NVars-1]. + Y - possible preallocated buffer. Reused if long enough. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + OUTPUT PARAMETERS: + Y - result. Regression estimate when solving regression task, + vector of posterior probabilities for classification task. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). - -- ALGLIB -- - Copyright 23.07.2012 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlperrorsparse(multilayerperceptron network, - sparse.sparsematrix xy, - int npoints, + public static void knnprocess(knnmodel model, + double[] x, + ref double[] y, alglib.xparams _params) { - double result = 0; - - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPErrorSparse: XY is not in CRS format."); - alglib.ap.assert(sparse.sparsegetnrows(xy, _params)>=npoints, "MLPErrorSparse: XY has less than NPoints rows"); - if( npoints>0 ) - { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+1, "MLPErrorSparse: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPErrorSparse: XY has less than NIn+NOut columns"); - } - } - mlpallerrorsx(network, network.dummydxy, xy, npoints, 1, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = math.sqr(network.err.rmserror)*npoints*mlpgetoutputscount(network, _params)/2; - return result; + knntsprocess(model, model.buffer, x, ref y, _params); } /************************************************************************* - Natural error function for neural network, internal subroutine. + This function returns first component of the inferred vector (i.e. one + with index #0). - NOTE: this function is single-threaded. Unlike other error function, it - receives no speed-up from being executed in SMP mode. + It is a convenience wrapper for knnprocess() intended for either: + * 1-dimensional regression problems + * 2-class classification problems + + In the former case this function returns inference result as scalar, which + is definitely more convenient that wrapping it as vector. In the latter + case it returns probability of object belonging to class #0. + + If you call it for anything different from two cases above, it will work + as defined, i.e. return y[0], although it is of less use in such cases. + + IMPORTANT: this function is thread-unsafe and modifies internal structures + of the model! You can not use same model object for parallel + evaluation from several threads. + + Use knntsprocess() with independent thread-local buffers, if + you need thread-safe evaluation. + + INPUT PARAMETERS: + Model - KNN model + X - input vector, array[0..NVars-1]. + + RESULT: + Y[0] -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlperrorn(multilayerperceptron network, - double[,] xy, - int ssize, + public static double knnprocess0(knnmodel model, + double[] x, alglib.xparams _params) { double result = 0; int i = 0; - int k = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - double e = 0; - int i_ = 0; - int i1_ = 0; + int nvars = 0; - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - result = 0; - for(i=0; i<=ssize-1; i++) + nvars = model.nvars; + for(i=0; i<=nvars-1; i++) { - - // - // Process vector - // - for(i_=0; i_<=nin-1;i_++) - { - network.x[i_] = xy[i,i_]; - } - mlpprocess(network, network.x, ref network.y, _params); - - // - // Update error function - // - if( network.structinfo[6]==0 ) - { - - // - // Least squares error function - // - i1_ = (nin) - (0); - for(i_=0; i_<=nout-1;i_++) - { - network.y[i_] = network.y[i_] - xy[i,i_+i1_]; - } - e = 0.0; - for(i_=0; i_<=nout-1;i_++) - { - e += network.y[i_]*network.y[i_]; - } - result = result+e/2; - } - else - { - - // - // Cross-entropy error function - // - k = (int)Math.Round(xy[i,nin]); - if( k>=0 && k=npoints, "MLPClsError: XY has less than NPoints rows"); - if( npoints>0 ) + if( !model.iscls ) { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+1, "MLPClsError: XY has less than NIn+1 columns"); - } - else + result = -1; + return result; + } + nvars = model.nvars; + nout = model.nout; + for(i=0; i<=nvars-1; i++) + { + model.buffer.x[i] = x[i]; + } + processinternal(model, model.buffer, _params); + result = 0; + for(i=1; i<=nout-1; i++) + { + if( model.buffer.y[i]>model.buffer.y[result] ) { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPClsError: XY has less than NIn+NOut columns"); + result = i; } } - mlpallerrorsx(network, xy, network.dummysxy, npoints, 0, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = (int)Math.Round(npoints*network.err.relclserror); return result; } /************************************************************************* - Relative classification error on the test set. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. - - RESULT: - Percent of incorrectly classified cases. Works both for classifier - networks and general purpose networks used as classifiers. - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + 'interactive' variant of knnprocess() for languages like Python which + support constructs like "y = knnprocessi(model,x)" and interactive mode of + the interpreter. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + This function allocates new array on each call, so it is significantly + slower than its 'non-interactive' counterpart, but it is more convenient + when you call it from command line. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + IMPORTANT: this function is thread-unsafe and may modify internal + structures of the model! You can not use same model object for + parallel evaluation from several threads. + + Use knntsprocess() with independent thread-local buffers if + you need thread-safe evaluation. -- ALGLIB -- - Copyright 25.12.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlprelclserror(multilayerperceptron network, - double[,] xy, - int npoints, + public static void knnprocessi(knnmodel model, + double[] x, + ref double[] y, alglib.xparams _params) { - double result = 0; + y = new double[0]; - alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "MLPRelClsError: XY has less than NPoints rows"); - if( npoints>0 ) - { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+1, "MLPRelClsError: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPRelClsError: XY has less than NIn+NOut columns"); - } - } - if( npoints>0 ) - { - result = (double)mlpclserror(network, xy, npoints, _params)/(double)npoints; - } - else - { - result = 0.0; - } - return result; + knnprocess(model, x, ref y, _params); } /************************************************************************* - Relative classification error on the test set given by sparse matrix. + Thread-safe procesing using external buffer for temporaries. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + This function is thread-safe (i.e . you can use same KNN model from + multiple threads) as long as you use different buffer objects for different + threads. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. Sparse matrix must use CRS format - for storage. - NPoints - points count, >=0. - - RESULT: - Percent of incorrectly classified cases. Works both for classifier - networks and general purpose networks used as classifiers. - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + Model - KNN model + Buf - buffer object, must be allocated specifically for this + model with knncreatebuffer(). + X - input vector, array[NVars] - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + OUTPUT PARAMETERS: + Y - result, array[NOut]. Regression estimate when solving + regression task, vector of posterior probabilities for + a classification task. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). - -- ALGLIB -- - Copyright 09.08.2012 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlprelclserrorsparse(multilayerperceptron network, - sparse.sparsematrix xy, - int npoints, + public static void knntsprocess(knnmodel model, + knnbuffer buf, + double[] x, + ref double[] y, alglib.xparams _params) { - double result = 0; + int i = 0; + int nvars = 0; + int nout = 0; - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPRelClsErrorSparse: sparse matrix XY is not in CRS format."); - alglib.ap.assert(sparse.sparsegetnrows(xy, _params)>=npoints, "MLPRelClsErrorSparse: sparse matrix XY has less than NPoints rows"); - if( npoints>0 ) + nvars = model.nvars; + nout = model.nout; + for(i=0; i<=nvars-1; i++) { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+1, "MLPRelClsErrorSparse: sparse matrix XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPRelClsErrorSparse: sparse matrix XY has less than NIn+NOut columns"); - } + buf.x[i] = x[i]; + } + processinternal(model, buf, _params); + if( alglib.ap.len(y)=npoints, "MLPAvgCE: XY has less than NPoints rows"); - if( npoints>0 ) - { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+1, "MLPAvgCE: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPAvgCE: XY has less than NIn+NOut columns"); - } - } - mlpallerrorsx(network, xy, network.dummysxy, npoints, 0, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = network.err.avgce; + knnallerrors(model, xy, npoints, rep, _params); + result = rep.relclserror; return result; } /************************************************************************* - Average cross-entropy (in bits per element) on the test set given by - sparse matrix. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Average cross-entropy (in bits per element) on the test set INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0. + Model - KNN model + XY - test set + NPoints - test set size RESULT: - CrossEntropy/(NPoints*LN(2)). - Zero if network solves regression task. - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + CrossEntropy/NPoints. + Zero if model solves regression task. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + NOTE: the cross-entropy metric is too unstable when used to evaluate KNN + models (such models can report exactly zero probabilities), so we + do not recommend using it. + + NOTE: if you need several different kinds of error metrics, it is better + to use knnallerrors() which computes all error metric with just one + pass over dataset. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). - -- ALGLIB -- - Copyright 9.08.2012 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgcesparse(multilayerperceptron network, - sparse.sparsematrix xy, + public static double knnavgce(knnmodel model, + double[,] xy, int npoints, alglib.xparams _params) { double result = 0; + knnreport rep = new knnreport(); - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPAvgCESparse: sparse matrix XY is not in CRS format."); - alglib.ap.assert(sparse.sparsegetnrows(xy, _params)>=npoints, "MLPAvgCESparse: sparse matrix XY has less than NPoints rows"); - if( npoints>0 ) - { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+1, "MLPAvgCESparse: sparse matrix XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPAvgCESparse: sparse matrix XY has less than NIn+NOut columns"); - } - } - mlpallerrorsx(network, network.dummydxy, xy, npoints, 1, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = network.err.avgce; + knnallerrors(model, xy, npoints, rep, _params); + result = rep.avgce; return result; } /************************************************************************* - RMS error on the test set given. + RMS error on the test set. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Its meaning for regression task is obvious. As for classification problems, + RMS error means error when estimating posterior probabilities. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. + Model - KNN model + XY - test set + NPoints - test set size RESULT: - Root mean square error. Its meaning for regression task is obvious. As for - classification task, RMS error means error when estimating posterior - probabilities. - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs - - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + root mean square error. + + NOTE: if you need several different kinds of error metrics, it is better + to use knnallerrors() which computes all error metric with just one + pass over dataset. -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlprmserror(multilayerperceptron network, + public static double knnrmserror(knnmodel model, double[,] xy, int npoints, alglib.xparams _params) { double result = 0; + knnreport rep = new knnreport(); - alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "MLPRMSError: XY has less than NPoints rows"); - if( npoints>0 ) - { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+1, "MLPRMSError: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPRMSError: XY has less than NIn+NOut columns"); - } - } - mlpallerrorsx(network, xy, network.dummysxy, npoints, 0, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = network.err.rmserror; + knnallerrors(model, xy, npoints, rep, _params); + result = rep.rmserror; return result; } /************************************************************************* - RMS error on the test set given by sparse matrix. + Average error on the test set - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Its meaning for regression task is obvious. As for classification problems, + average error means error when estimating posterior probabilities. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0. + Model - KNN model + XY - test set + NPoints - test set size RESULT: - Root mean square error. Its meaning for regression task is obvious. As for - classification task, RMS error means error when estimating posterior - probabilities. - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + average error + + NOTE: if you need several different kinds of error metrics, it is better + to use knnallerrors() which computes all error metric with just one + pass over dataset. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). - -- ALGLIB -- - Copyright 09.08.2012 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlprmserrorsparse(multilayerperceptron network, - sparse.sparsematrix xy, + public static double knnavgerror(knnmodel model, + double[,] xy, int npoints, alglib.xparams _params) { double result = 0; + knnreport rep = new knnreport(); - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPRMSErrorSparse: sparse matrix XY is not in CRS format."); - alglib.ap.assert(sparse.sparsegetnrows(xy, _params)>=npoints, "MLPRMSErrorSparse: sparse matrix XY has less than NPoints rows"); - if( npoints>0 ) - { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+1, "MLPRMSErrorSparse: sparse matrix XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPRMSErrorSparse: sparse matrix XY has less than NIn+NOut columns"); - } - } - mlpallerrorsx(network, network.dummydxy, xy, npoints, 1, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = network.err.rmserror; + knnallerrors(model, xy, npoints, rep, _params); + result = rep.avgerror; return result; } /************************************************************************* - Average absolute error on the test set. + Average relative error on the test set - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + Its meaning for regression task is obvious. As for classification problems, + average relative error means error when estimating posterior probabilities. INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. + Model - KNN model + XY - test set + NPoints - test set size RESULT: - Its meaning for regression task is obvious. As for classification task, it - means average error when estimating posterior probabilities. + average relative error + + NOTE: if you need several different kinds of error metrics, it is better + to use knnallerrors() which computes all error metric with just one + pass over dataset. - DATASET FORMAT: + -- ALGLIB -- + Copyright 15.02.2019 by Bochkanov Sergey + *************************************************************************/ + public static double knnavgrelerror(knnmodel model, + double[,] xy, + int npoints, + alglib.xparams _params) + { + double result = 0; + knnreport rep = new knnreport(); - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + knnallerrors(model, xy, npoints, rep, _params); + result = rep.avgrelerror; + return result; + } - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + /************************************************************************* + Calculates all kinds of errors for the model in one call. + + INPUT PARAMETERS: + Model - KNN model + XY - test set: + * one row per point + * first NVars columns store independent variables + * depending on problem type: + * next column stores class number in [0,NClasses) - for + classification problems + * next NOut columns store dependent variables - for + regression problems + NPoints - test set size, NPoints>=0 + + OUTPUT PARAMETERS: + Rep - following fields are loaded with errors for both regression + and classification models: + * rep.rmserror - RMS error for the output + * rep.avgerror - average error + * rep.avgrelerror - average relative error + following fields are set only for classification models, + zero for regression ones: + * relclserror - relative classification error, in [0,1] + * avgce - average cross-entropy in bits per dataset entry + + NOTE: the cross-entropy metric is too unstable when used to evaluate KNN + models (such models can report exactly zero probabilities), so we + do not recommend using it. -- ALGLIB -- - Copyright 11.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgerror(multilayerperceptron network, + public static void knnallerrors(knnmodel model, double[,] xy, int npoints, + knnreport rep, alglib.xparams _params) { - double result = 0; + knnbuffer buf = new knnbuffer(); + double[] desiredy = new double[0]; + double[] errbuf = new double[0]; + int nvars = 0; + int nout = 0; + int ny = 0; + bool iscls = new bool(); + int i = 0; + int j = 0; - alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "MLPAvgError: XY has less than NPoints rows"); - if( npoints>0 ) + nvars = model.nvars; + nout = model.nout; + iscls = model.iscls; + if( iscls ) { - if( mlpissoftmax(network, _params) ) + ny = 1; + } + else + { + ny = nout; + } + + // + // Check input + // + alglib.ap.assert(npoints>=0, "knnallerrors: npoints<0"); + alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "knnallerrors: rows(xy)=nvars+ny, "knnallerrors: cols(xy)=mlpgetinputscount(network, _params)+1, "MLPAvgError: XY has less than NIn+1 columns"); + buf.x[j] = xy[i,j]; + } + if( iscls ) + { + j = (int)Math.Round(xy[i,nvars]); + alglib.ap.assert(j>=0 && j=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPAvgError: XY has less than NIn+NOut columns"); + for(j=0; j<=nout-1; j++) + { + desiredy[j] = xy[i,nvars+j]; + } } + processinternal(model, buf, _params); + bdss.dserraccumulate(ref errbuf, buf.y, desiredy, _params); } - mlpallerrorsx(network, xy, network.dummysxy, npoints, 0, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = network.err.avgerror; - return result; + bdss.dserrfinish(ref errbuf, _params); + + // + // Extract results + // + if( iscls ) + { + rep.relclserror = errbuf[0]; + rep.avgce = errbuf[1]; + } + rep.rmserror = errbuf[2]; + rep.avgerror = errbuf[3]; + rep.avgrelerror = errbuf[4]; } /************************************************************************* - Average absolute error on the test set given by sparse matrix. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0. - - RESULT: - Its meaning for regression task is obvious. As for classification task, it - means average error when estimating posterior probabilities. + Serializer: allocation - DATASET FORMAT: + -- ALGLIB -- + Copyright 15.02.2019 by Bochkanov Sergey + *************************************************************************/ + public static void knnalloc(alglib.serializer s, + knnmodel model, + alglib.xparams _params) + { + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + if( !model.isdummy ) + { + nearestneighbor.kdtreealloc(s, model.tree, _params); + } + } - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + /************************************************************************* + Serializer: serialization - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). - -- ALGLIB -- - Copyright 09.08.2012 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgerrorsparse(multilayerperceptron network, - sparse.sparsematrix xy, - int npoints, + public static void knnserialize(alglib.serializer s, + knnmodel model, alglib.xparams _params) { - double result = 0; - - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPAvgErrorSparse: XY is not in CRS format."); - alglib.ap.assert(sparse.sparsegetnrows(xy, _params)>=npoints, "MLPAvgErrorSparse: XY has less than NPoints rows"); - if( npoints>0 ) + s.serialize_int(scodes.getknnserializationcode(_params)); + s.serialize_int(knnfirstversion); + s.serialize_int(model.nvars); + s.serialize_int(model.nout); + s.serialize_int(model.k); + s.serialize_double(model.eps); + s.serialize_bool(model.iscls); + s.serialize_bool(model.isdummy); + if( !model.isdummy ) { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+1, "MLPAvgErrorSparse: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPAvgErrorSparse: XY has less than NIn+NOut columns"); - } + nearestneighbor.kdtreeserialize(s, model.tree, _params); } - mlpallerrorsx(network, network.dummydxy, xy, npoints, 1, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = network.err.avgerror; - return result; } /************************************************************************* - Average relative error on the test set. + Serializer: unserialization - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + -- ALGLIB -- + Copyright 15.02.2019 by Bochkanov Sergey + *************************************************************************/ + public static void knnunserialize(alglib.serializer s, + knnmodel model, + alglib.xparams _params) + { + int i0 = 0; + int i1 = 0; - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - NPoints - points count. + + // + // check correctness of header + // + i0 = s.unserialize_int(); + alglib.ap.assert(i0==scodes.getknnserializationcode(_params), "KNNUnserialize: stream header corrupted"); + i1 = s.unserialize_int(); + alglib.ap.assert(i1==knnfirstversion, "KNNUnserialize: stream header corrupted"); + + // + // Unserialize data + // + model.nvars = s.unserialize_int(); + model.nout = s.unserialize_int(); + model.k = s.unserialize_int(); + model.eps = s.unserialize_double(); + model.iscls = s.unserialize_bool(); + model.isdummy = s.unserialize_bool(); + if( !model.isdummy ) + { + nearestneighbor.kdtreeunserialize(s, model.tree, _params); + } + + // + // Prepare local buffer + // + knncreatebuffer(model, model.buffer, _params); + } - RESULT: - Its meaning for regression task is obvious. As for classification task, it - means average relative error when estimating posterior probability of - belonging to the correct class. - DATASET FORMAT: + /************************************************************************* + Sets report fields to their default values - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + -- ALGLIB -- + Copyright 15.02.2019 by Bochkanov Sergey + *************************************************************************/ + private static void clearreport(knnreport rep, + alglib.xparams _params) + { + rep.relclserror = 0; + rep.avgce = 0; + rep.rmserror = 0; + rep.avgerror = 0; + rep.avgrelerror = 0; + } - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + /************************************************************************* + This function processes buf.X and stores result to buf.Y + + INPUT PARAMETERS + Model - KNN model + Buf - processing buffer. + + + IMPORTANT: buffer object should be used only with model which was used to + initialize buffer. Any attempt to use buffer with different + object is dangerous - you may get integrity check failure + (exception) because sizes of internal arrays do not fit to + dimensions of the model structure. -- ALGLIB -- - Copyright 11.03.2008 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpavgrelerror(multilayerperceptron network, - double[,] xy, - int npoints, + private static void processinternal(knnmodel model, + knnbuffer buf, alglib.xparams _params) { - double result = 0; + int nvars = 0; + int nout = 0; + bool iscls = new bool(); + int nncnt = 0; + int i = 0; + int j = 0; + double v = 0; - alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "MLPAvgRelError: XY has less than NPoints rows"); - if( npoints>0 ) + nvars = model.nvars; + nout = model.nout; + iscls = model.iscls; + + // + // Quick exit if needed + // + if( model.isdummy ) { - if( mlpissoftmax(network, _params) ) + for(i=0; i<=nout-1; i++) { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+1, "MLPAvgRelError: XY has less than NIn+1 columns"); + buf.y[i] = 0; } - else + return; + } + + // + // Perform request, average results + // + for(i=0; i<=nout-1; i++) + { + buf.y[i] = 0; + } + nncnt = nearestneighbor.kdtreetsqueryaknn(model.tree, buf.treebuf, buf.x, model.k, true, model.eps, _params); + v = 1/apserv.coalesce(nncnt, 1, _params); + if( iscls ) + { + nearestneighbor.kdtreetsqueryresultstags(model.tree, buf.treebuf, ref buf.tags, _params); + for(i=0; i<=nncnt-1; i++) { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPAvgRelError: XY has less than NIn+NOut columns"); + j = buf.tags[i]; + buf.y[j] = buf.y[j]+v; + } + } + else + { + nearestneighbor.kdtreetsqueryresultsxy(model.tree, buf.treebuf, ref buf.xy, _params); + for(i=0; i<=nncnt-1; i++) + { + for(j=0; j<=nout-1; j++) + { + buf.y[j] = buf.y[j]+v*buf.xy[i,nvars+j]; + } } } - mlpallerrorsx(network, xy, network.dummysxy, npoints, 0, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = network.err.avgrelerror; - return result; } + } + public partial class dforest + { /************************************************************************* - Average relative error on the test set given by sparse matrix. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - NPoints - points count, >=0. + A random forest (decision forest) builder object. - RESULT: - Its meaning for regression task is obvious. As for classification task, it - means average relative error when estimating posterior probability of - belonging to the correct class. + Used to store dataset and specify decision forest training algorithm settings. + *************************************************************************/ + public class decisionforestbuilder : apobject + { + public int dstype; + public int npoints; + public int nvars; + public int nclasses; + public double[] dsdata; + public double[] dsrval; + public int[] dsival; + public int rdfalgo; + public double rdfratio; + public double rdfvars; + public int rdfglobalseed; + public int rdfsplitstrength; + public int rdfimportance; + public double[] dsmin; + public double[] dsmax; + public bool[] dsbinary; + public double dsravg; + public int[] dsctotals; + public int rdfprogress; + public int rdftotal; + public alglib.smp.shared_pool workpool; + public alglib.smp.shared_pool votepool; + public alglib.smp.shared_pool treepool; + public alglib.smp.shared_pool treefactory; + public bool neediobmatrix; + public bool[,] iobmatrix; + public int[] varimpshuffle2; + public decisionforestbuilder() + { + init(); + } + public override void init() + { + dsdata = new double[0]; + dsrval = new double[0]; + dsival = new int[0]; + dsmin = new double[0]; + dsmax = new double[0]; + dsbinary = new bool[0]; + dsctotals = new int[0]; + workpool = new alglib.smp.shared_pool(); + votepool = new alglib.smp.shared_pool(); + treepool = new alglib.smp.shared_pool(); + treefactory = new alglib.smp.shared_pool(); + iobmatrix = new bool[0,0]; + varimpshuffle2 = new int[0]; + } + public override alglib.apobject make_copy() + { + decisionforestbuilder _result = new decisionforestbuilder(); + _result.dstype = dstype; + _result.npoints = npoints; + _result.nvars = nvars; + _result.nclasses = nclasses; + _result.dsdata = (double[])dsdata.Clone(); + _result.dsrval = (double[])dsrval.Clone(); + _result.dsival = (int[])dsival.Clone(); + _result.rdfalgo = rdfalgo; + _result.rdfratio = rdfratio; + _result.rdfvars = rdfvars; + _result.rdfglobalseed = rdfglobalseed; + _result.rdfsplitstrength = rdfsplitstrength; + _result.rdfimportance = rdfimportance; + _result.dsmin = (double[])dsmin.Clone(); + _result.dsmax = (double[])dsmax.Clone(); + _result.dsbinary = (bool[])dsbinary.Clone(); + _result.dsravg = dsravg; + _result.dsctotals = (int[])dsctotals.Clone(); + _result.rdfprogress = rdfprogress; + _result.rdftotal = rdftotal; + _result.workpool = workpool!=null ? (alglib.smp.shared_pool)workpool.make_copy() : null; + _result.votepool = votepool!=null ? (alglib.smp.shared_pool)votepool.make_copy() : null; + _result.treepool = treepool!=null ? (alglib.smp.shared_pool)treepool.make_copy() : null; + _result.treefactory = treefactory!=null ? (alglib.smp.shared_pool)treefactory.make_copy() : null; + _result.neediobmatrix = neediobmatrix; + _result.iobmatrix = (bool[,])iobmatrix.Clone(); + _result.varimpshuffle2 = (int[])varimpshuffle2.Clone(); + return _result; + } + }; - DATASET FORMAT: - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + public class dfworkbuf : apobject + { + public int[] classpriors; + public int[] varpool; + public int varpoolsize; + public int[] trnset; + public int trnsize; + public double[] trnlabelsr; + public int[] trnlabelsi; + public int[] oobset; + public int oobsize; + public double[] ooblabelsr; + public int[] ooblabelsi; + public double[] treebuf; + public double[] curvals; + public double[] bestvals; + public int[] tmp0i; + public int[] tmp1i; + public double[] tmp0r; + public double[] tmp1r; + public double[] tmp2r; + public double[] tmp3r; + public int[] tmpnrms2; + public int[] classtotals0; + public int[] classtotals1; + public int[] classtotals01; + public dfworkbuf() + { + init(); + } + public override void init() + { + classpriors = new int[0]; + varpool = new int[0]; + trnset = new int[0]; + trnlabelsr = new double[0]; + trnlabelsi = new int[0]; + oobset = new int[0]; + ooblabelsr = new double[0]; + ooblabelsi = new int[0]; + treebuf = new double[0]; + curvals = new double[0]; + bestvals = new double[0]; + tmp0i = new int[0]; + tmp1i = new int[0]; + tmp0r = new double[0]; + tmp1r = new double[0]; + tmp2r = new double[0]; + tmp3r = new double[0]; + tmpnrms2 = new int[0]; + classtotals0 = new int[0]; + classtotals1 = new int[0]; + classtotals01 = new int[0]; + } + public override alglib.apobject make_copy() + { + dfworkbuf _result = new dfworkbuf(); + _result.classpriors = (int[])classpriors.Clone(); + _result.varpool = (int[])varpool.Clone(); + _result.varpoolsize = varpoolsize; + _result.trnset = (int[])trnset.Clone(); + _result.trnsize = trnsize; + _result.trnlabelsr = (double[])trnlabelsr.Clone(); + _result.trnlabelsi = (int[])trnlabelsi.Clone(); + _result.oobset = (int[])oobset.Clone(); + _result.oobsize = oobsize; + _result.ooblabelsr = (double[])ooblabelsr.Clone(); + _result.ooblabelsi = (int[])ooblabelsi.Clone(); + _result.treebuf = (double[])treebuf.Clone(); + _result.curvals = (double[])curvals.Clone(); + _result.bestvals = (double[])bestvals.Clone(); + _result.tmp0i = (int[])tmp0i.Clone(); + _result.tmp1i = (int[])tmp1i.Clone(); + _result.tmp0r = (double[])tmp0r.Clone(); + _result.tmp1r = (double[])tmp1r.Clone(); + _result.tmp2r = (double[])tmp2r.Clone(); + _result.tmp3r = (double[])tmp3r.Clone(); + _result.tmpnrms2 = (int[])tmpnrms2.Clone(); + _result.classtotals0 = (int[])classtotals0.Clone(); + _result.classtotals1 = (int[])classtotals1.Clone(); + _result.classtotals01 = (int[])classtotals01.Clone(); + return _result; + } + }; - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). - - -- ALGLIB -- - Copyright 09.08.2012 by Bochkanov Sergey - *************************************************************************/ - public static double mlpavgrelerrorsparse(multilayerperceptron network, - sparse.sparsematrix xy, - int npoints, - alglib.xparams _params) + public class dfvotebuf : apobject { - double result = 0; - - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPAvgRelErrorSparse: XY is not in CRS format."); - alglib.ap.assert(sparse.sparsegetnrows(xy, _params)>=npoints, "MLPAvgRelErrorSparse: XY has less than NPoints rows"); - if( npoints>0 ) + public double[] trntotals; + public double[] oobtotals; + public int[] trncounts; + public int[] oobcounts; + public double[] giniimportances; + public dfvotebuf() { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+1, "MLPAvgRelErrorSparse: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPAvgRelErrorSparse: XY has less than NIn+NOut columns"); - } + init(); } - mlpallerrorsx(network, network.dummydxy, xy, npoints, 1, network.dummyidx, 0, npoints, 0, network.buf, network.err, _params); - result = network.err.avgrelerror; - return result; - } + public override void init() + { + trntotals = new double[0]; + oobtotals = new double[0]; + trncounts = new int[0]; + oobcounts = new int[0]; + giniimportances = new double[0]; + } + public override alglib.apobject make_copy() + { + dfvotebuf _result = new dfvotebuf(); + _result.trntotals = (double[])trntotals.Clone(); + _result.oobtotals = (double[])oobtotals.Clone(); + _result.trncounts = (int[])trncounts.Clone(); + _result.oobcounts = (int[])oobcounts.Clone(); + _result.giniimportances = (double[])giniimportances.Clone(); + return _result; + } + }; /************************************************************************* - Gradient calculation - - INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - X - input vector, length of array must be at least NIn - DesiredY- desired outputs, length of array must be at least NOut - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + Permutation importance buffer object, stores permutation-related losses + for some subset of the dataset + some temporaries - OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, array[WCount] - - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Losses - array[NVars+2], stores sum of squared residuals for each + permutation type: + * Losses[0..NVars-1] stores losses for permutation in J-th variable + * Losses[NVars] stores loss for all variables being randomly perturbed + * Losses[NVars+1] stores loss for unperturbed dataset *************************************************************************/ - public static void mlpgrad(multilayerperceptron network, - double[] x, - double[] desiredy, - ref double e, - ref double[] grad, - alglib.xparams _params) + public class dfpermimpbuf : apobject { - int i = 0; - int nout = 0; - int ntotal = 0; + public double[] losses; + public double[] xraw; + public double[] xdist; + public double[] xcur; + public double[] y; + public double[] yv; + public double[] targety; + public int[] startnodes; + public dfpermimpbuf() + { + init(); + } + public override void init() + { + losses = new double[0]; + xraw = new double[0]; + xdist = new double[0]; + xcur = new double[0]; + y = new double[0]; + yv = new double[0]; + targety = new double[0]; + startnodes = new int[0]; + } + public override alglib.apobject make_copy() + { + dfpermimpbuf _result = new dfpermimpbuf(); + _result.losses = (double[])losses.Clone(); + _result.xraw = (double[])xraw.Clone(); + _result.xdist = (double[])xdist.Clone(); + _result.xcur = (double[])xcur.Clone(); + _result.y = (double[])y.Clone(); + _result.yv = (double[])yv.Clone(); + _result.targety = (double[])targety.Clone(); + _result.startnodes = (int[])startnodes.Clone(); + return _result; + } + }; - e = 0; - - // - // Alloc - // - apserv.rvectorsetlengthatleast(ref grad, network.structinfo[4], _params); - - // - // Prepare dError/dOut, internal structures - // - mlpprocess(network, x, ref network.y, _params); - nout = network.structinfo[2]; - ntotal = network.structinfo[3]; - e = 0; - for(i=0; i<=ntotal-1; i++) + public class dftreebuf : apobject + { + public double[] treebuf; + public int treeidx; + public dftreebuf() { - network.derror[i] = 0; + init(); } - for(i=0; i<=nout-1; i++) + public override void init() { - network.derror[ntotal-nout+i] = network.y[i]-desiredy[i]; - e = e+math.sqr(network.y[i]-desiredy[i])/2; + treebuf = new double[0]; } - - // - // gradient - // - mlpinternalcalculategradient(network, network.neurons, network.weights, ref network.derror, ref grad, false, _params); - } + public override alglib.apobject make_copy() + { + dftreebuf _result = new dftreebuf(); + _result.treebuf = (double[])treebuf.Clone(); + _result.treeidx = treeidx; + return _result; + } + }; /************************************************************************* - Gradient calculation (natural error function is used) + Buffer object which is used to perform various requests (usually model + inference) in the multithreaded mode (multiple threads working with same + DF object). - INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - X - input vector, length of array must be at least NIn - DesiredY- desired outputs, length of array must be at least NOut - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + This object should be created with DFCreateBuffer(). + *************************************************************************/ + public class decisionforestbuffer : apobject + { + public double[] x; + public double[] y; + public decisionforestbuffer() + { + init(); + } + public override void init() + { + x = new double[0]; + y = new double[0]; + } + public override alglib.apobject make_copy() + { + decisionforestbuffer _result = new decisionforestbuffer(); + _result.x = (double[])x.Clone(); + _result.y = (double[])y.Clone(); + return _result; + } + }; - OUTPUT PARAMETERS: - E - error function, sum-of-squares for regression networks, - cross-entropy for classification networks. - Grad - gradient of E with respect to weights of network, array[WCount] - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + /************************************************************************* + Decision forest (random forest) model. *************************************************************************/ - public static void mlpgradn(multilayerperceptron network, - double[] x, - double[] desiredy, - ref double e, - ref double[] grad, - alglib.xparams _params) + public class decisionforest : apobject { - double s = 0; - int i = 0; - int nout = 0; - int ntotal = 0; - - e = 0; - - - // - // Alloc - // - apserv.rvectorsetlengthatleast(ref grad, network.structinfo[4], _params); - - // - // Prepare dError/dOut, internal structures - // - mlpprocess(network, x, ref network.y, _params); - nout = network.structinfo[2]; - ntotal = network.structinfo[3]; - for(i=0; i<=ntotal-1; i++) + public int forestformat; + public bool usemantissa8; + public int nvars; + public int nclasses; + public int ntrees; + public int bufsize; + public double[] trees; + public decisionforestbuffer buffer; + public byte[] trees8; + public decisionforest() { - network.derror[i] = 0; + init(); } - e = 0; - if( network.structinfo[6]==0 ) + public override void init() { - - // - // Regression network, least squares - // - for(i=0; i<=nout-1; i++) - { - network.derror[ntotal-nout+i] = network.y[i]-desiredy[i]; - e = e+math.sqr(network.y[i]-desiredy[i])/2; - } + trees = new double[0]; + buffer = new decisionforestbuffer(); + trees8 = new byte[0]; } - else + public override alglib.apobject make_copy() { - - // - // Classification network, cross-entropy - // - s = 0; - for(i=0; i<=nout-1; i++) - { - s = s+desiredy[i]; - } - for(i=0; i<=nout-1; i++) - { - network.derror[ntotal-nout+i] = s*network.y[i]-desiredy[i]; - e = e+safecrossentropy(desiredy[i], network.y[i], _params); - } + decisionforest _result = new decisionforest(); + _result.forestformat = forestformat; + _result.usemantissa8 = usemantissa8; + _result.nvars = nvars; + _result.nclasses = nclasses; + _result.ntrees = ntrees; + _result.bufsize = bufsize; + _result.trees = (double[])trees.Clone(); + _result.buffer = buffer!=null ? (decisionforestbuffer)buffer.make_copy() : null; + _result.trees8 = (byte[])trees8.Clone(); + return _result; } - - // - // gradient - // - mlpinternalcalculategradient(network, network.neurons, network.weights, ref network.derror, ref grad, true, _params); - } + }; /************************************************************************* - Batch gradient calculation for a set of inputs/outputs + Decision forest training report. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + === training/oob errors ================================================== - INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset in dense format; one sample = one row: - * first NIn columns contain inputs, - * for regression problem, next NOut columns store - desired outputs. - * for classification problem, next column (just one!) - stores class number. - SSize - number of elements in XY - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + Following fields store training set errors: + * relclserror - fraction of misclassified cases, [0,1] + * avgce - average cross-entropy in bits per symbol + * rmserror - root-mean-square error + * avgerror - average error + * avgrelerror - average relative error - OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, array[WCount] + Out-of-bag estimates are stored in fields with same names, but "oob" prefix. - -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + For classification problems: + * RMS, AVG and AVGREL errors are calculated for posterior probabilities + + For regression problems: + * RELCLS and AVGCE errors are zero + + === variable importance ================================================== + + Following fields are used to store variable importance information: + + * topvars - variables ordered from the most important to + less important ones (according to current + choice of importance raiting). + For example, topvars[0] contains index of the + most important variable, and topvars[0:2] are + indexes of 3 most important ones and so on. + + * varimportances - array[nvars], ratings (the larger, the more + important the variable is, always in [0,1] + range). + By default, filled by zeros (no importance + ratings are provided unless you explicitly + request them). + Zero rating means that variable is not important, + however you will rarely encounter such a thing, + in many cases unimportant variables produce + nearly-zero (but nonzero) ratings. + + Variable importance report must be EXPLICITLY requested by calling: + * dfbuildersetimportancegini() function, if you need out-of-bag Gini-based + importance rating also known as MDI (fast to calculate, resistant to + overfitting issues, but has some bias towards continuous and + high-cardinality categorical variables) + * dfbuildersetimportancetrngini() function, if you need training set Gini- + -based importance rating (what other packages typically report). + * dfbuildersetimportancepermutation() function, if you need permutation- + based importance rating also known as MDA (slower to calculate, but less + biased) + * dfbuildersetimportancenone() function, if you do not need importance + ratings - ratings will be zero, topvars[] will be [0,1,2,...] + + Different importance ratings (Gini or permutation) produce non-comparable + values. Although in all cases rating values lie in [0,1] range, there are + exist differences: + * informally speaking, Gini importance rating tends to divide "unit amount + of importance" between several important variables, i.e. it produces + estimates which roughly sum to 1.0 (or less than 1.0, if your task can + not be solved exactly). If all variables are equally important, they + will have same rating, roughly 1/NVars, even if every variable is + critically important. + * from the other side, permutation importance tells us what percentage of + the model predictive power will be ruined by permuting this specific + variable. It does not produce estimates which sum to one. Critically + important variable will have rating close to 1.0, and you may have + multiple variables with such a rating. + + More information on variable importance ratings can be found in comments + on the dfbuildersetimportancegini() and dfbuildersetimportancepermutation() + functions. *************************************************************************/ - public static void mlpgradbatch(multilayerperceptron network, - double[,] xy, - int ssize, - ref double e, - ref double[] grad, - alglib.xparams _params) + public class dfreport : apobject { - int i = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int subset0 = 0; - int subset1 = 0; - int subsettype = 0; - smlpgrad sgrad = null; + public double relclserror; + public double avgce; + public double rmserror; + public double avgerror; + public double avgrelerror; + public double oobrelclserror; + public double oobavgce; + public double oobrmserror; + public double oobavgerror; + public double oobavgrelerror; + public int[] topvars; + public double[] varimportances; + public dfreport() + { + init(); + } + public override void init() + { + topvars = new int[0]; + varimportances = new double[0]; + } + public override alglib.apobject make_copy() + { + dfreport _result = new dfreport(); + _result.relclserror = relclserror; + _result.avgce = avgce; + _result.rmserror = rmserror; + _result.avgerror = avgerror; + _result.avgrelerror = avgrelerror; + _result.oobrelclserror = oobrelclserror; + _result.oobavgce = oobavgce; + _result.oobrmserror = oobrmserror; + _result.oobavgerror = oobavgerror; + _result.oobavgrelerror = oobavgrelerror; + _result.topvars = (int[])topvars.Clone(); + _result.varimportances = (double[])varimportances.Clone(); + return _result; + } + }; - e = 0; - alglib.ap.assert(ssize>=0, "MLPGradBatchSparse: SSize<0"); - subset0 = 0; - subset1 = ssize; - subsettype = 0; - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - apserv.rvectorsetlengthatleast(ref grad, wcount, _params); - alglib.smp.ae_shared_pool_first_recycled(network.gradbuf, ref sgrad); - while( sgrad!=null ) + public class dfinternalbuffers : apobject + { + public double[] treebuf; + public int[] idxbuf; + public double[] tmpbufr; + public double[] tmpbufr2; + public int[] tmpbufi; + public int[] classibuf; + public double[] sortrbuf; + public double[] sortrbuf2; + public int[] sortibuf; + public int[] varpool; + public bool[] evsbin; + public double[] evssplits; + public dfinternalbuffers() { - sgrad.f = 0.0; - for(i=0; i<=wcount-1; i++) - { - sgrad.g[i] = 0.0; - } - alglib.smp.ae_shared_pool_next_recycled(network.gradbuf, ref sgrad); + init(); } - mlpgradbatchx(network, xy, network.dummysxy, ssize, 0, network.dummyidx, subset0, subset1, subsettype, network.buf, network.gradbuf, _params); - e = 0.0; - for(i=0; i<=wcount-1; i++) + public override void init() { - grad[i] = 0.0; + treebuf = new double[0]; + idxbuf = new int[0]; + tmpbufr = new double[0]; + tmpbufr2 = new double[0]; + tmpbufi = new int[0]; + classibuf = new int[0]; + sortrbuf = new double[0]; + sortrbuf2 = new double[0]; + sortibuf = new int[0]; + varpool = new int[0]; + evsbin = new bool[0]; + evssplits = new double[0]; } - alglib.smp.ae_shared_pool_first_recycled(network.gradbuf, ref sgrad); - while( sgrad!=null ) + public override alglib.apobject make_copy() { - e = e+sgrad.f; - for(i=0; i<=wcount-1; i++) - { - grad[i] = grad[i]+sgrad.g[i]; - } - alglib.smp.ae_shared_pool_next_recycled(network.gradbuf, ref sgrad); + dfinternalbuffers _result = new dfinternalbuffers(); + _result.treebuf = (double[])treebuf.Clone(); + _result.idxbuf = (int[])idxbuf.Clone(); + _result.tmpbufr = (double[])tmpbufr.Clone(); + _result.tmpbufr2 = (double[])tmpbufr2.Clone(); + _result.tmpbufi = (int[])tmpbufi.Clone(); + _result.classibuf = (int[])classibuf.Clone(); + _result.sortrbuf = (double[])sortrbuf.Clone(); + _result.sortrbuf2 = (double[])sortrbuf2.Clone(); + _result.sortibuf = (int[])sortibuf.Clone(); + _result.varpool = (int[])varpool.Clone(); + _result.evsbin = (bool[])evsbin.Clone(); + _result.evssplits = (double[])evssplits.Clone(); + return _result; } - } + }; + + + + + public const int innernodewidth = 3; + public const int leafnodewidth = 2; + public const int dfusestrongsplits = 1; + public const int dfuseevs = 2; + public const int dfuncompressedv0 = 0; + public const int dfcompressedv0 = 1; + public const int needtrngini = 1; + public const int needoobgini = 2; + public const int needpermutation = 3; + public const int permutationimportancebatchsize = 512; /************************************************************************* - Batch gradient calculation for a set of inputs/outputs given by sparse - matrices + This function creates buffer structure which can be used to perform + parallel inference requests. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + DF subpackage provides two sets of computing functions - ones which use + internal buffer of DF model (these functions are single-threaded because + they use same buffer, which can not shared between threads), and ones + which use external buffer. - INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset in sparse format; one sample = one row: - * MATRIX MUST BE STORED IN CRS FORMAT - * first NIn columns contain inputs. - * for regression problem, next NOut columns store - desired outputs. - * for classification problem, next column (just one!) - stores class number. - SSize - number of elements in XY - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + This function is used to initialize external buffer. - OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, array[WCount] + INPUT PARAMETERS + Model - DF model which is associated with newly created buffer + + OUTPUT PARAMETERS + Buf - external buffer. + + + IMPORTANT: buffer object should be used only with model which was used to + initialize buffer. Any attempt to use buffer with different + object is dangerous - you may get integrity check failure + (exception) because sizes of internal arrays do not fit to + dimensions of the model structure. -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradbatchsparse(multilayerperceptron network, - sparse.sparsematrix xy, - int ssize, - ref double e, - ref double[] grad, + public static void dfcreatebuffer(decisionforest model, + decisionforestbuffer buf, alglib.xparams _params) { - int i = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int subset0 = 0; - int subset1 = 0; - int subsettype = 0; - smlpgrad sgrad = null; - - e = 0; - - alglib.ap.assert(ssize>=0, "MLPGradBatchSparse: SSize<0"); - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPGradBatchSparse: sparse matrix XY must be in CRS format."); - subset0 = 0; - subset1 = ssize; - subsettype = 0; - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - apserv.rvectorsetlengthatleast(ref grad, wcount, _params); - alglib.smp.ae_shared_pool_first_recycled(network.gradbuf, ref sgrad); - while( sgrad!=null ) - { - sgrad.f = 0.0; - for(i=0; i<=wcount-1; i++) - { - sgrad.g[i] = 0.0; - } - alglib.smp.ae_shared_pool_next_recycled(network.gradbuf, ref sgrad); - } - mlpgradbatchx(network, network.dummydxy, xy, ssize, 1, network.dummyidx, subset0, subset1, subsettype, network.buf, network.gradbuf, _params); - e = 0.0; - for(i=0; i<=wcount-1; i++) - { - grad[i] = 0.0; - } - alglib.smp.ae_shared_pool_first_recycled(network.gradbuf, ref sgrad); - while( sgrad!=null ) - { - e = e+sgrad.f; - for(i=0; i<=wcount-1; i++) - { - grad[i] = grad[i]+sgrad.g[i]; - } - alglib.smp.ae_shared_pool_next_recycled(network.gradbuf, ref sgrad); - } + buf.x = new double[model.nvars]; + buf.y = new double[model.nclasses]; } /************************************************************************* - Batch gradient calculation for a subset of dataset + This subroutine creates DecisionForestBuilder object which is used to + train decision forests. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + By default, new builder stores empty dataset and some reasonable default + settings. At the very least, you should specify dataset prior to building + decision forest. You can also tweak settings of the forest construction + algorithm (recommended, although default setting should work well). + + Following actions are mandatory: + * calling dfbuildersetdataset() to specify dataset + * calling dfbuilderbuildrandomforest() to build decision forest using + current dataset and default settings + + Additionally, you may call: + * dfbuildersetrndvars() or dfbuildersetrndvarsratio() to specify number of + variables randomly chosen for each split + * dfbuildersetsubsampleratio() to specify fraction of the dataset randomly + subsampled to build each tree + * dfbuildersetseed() to control random seed chosen for tree construction INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset in dense format; one sample = one row: - * first NIn columns contain inputs, - * for regression problem, next NOut columns store - desired outputs. - * for classification problem, next column (just one!) - stores class number. - SetSize - real size of XY, SetSize>=0; - Idx - subset of SubsetSize elements, array[SubsetSize]: - * Idx[I] stores row index in the original dataset which is - given by XY. Gradient is calculated with respect to rows - whose indexes are stored in Idx[]. - * Idx[] must store correct indexes; this function throws - an exception in case incorrect index (less than 0 or - larger than rows(XY)) is given - * Idx[] may store indexes in any order and even with - repetitions. - SubsetSize- number of elements in Idx[] array: - * positive value means that subset given by Idx[] is processed - * zero value results in zero gradient - * negative value means that full dataset is processed - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + none + + OUTPUT PARAMETERS: + S - decision forest builder + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + public static void dfbuildercreate(decisionforestbuilder s, + alglib.xparams _params) + { + + // + // Empty dataset + // + s.dstype = -1; + s.npoints = 0; + s.nvars = 0; + s.nclasses = 1; + + // + // Default training settings + // + s.rdfalgo = 0; + s.rdfratio = 0.5; + s.rdfvars = 0.0; + s.rdfglobalseed = 0; + s.rdfsplitstrength = 2; + s.rdfimportance = 0; + + // + // Other fields + // + s.rdfprogress = 0; + s.rdftotal = 1; + } + + + /************************************************************************* + This subroutine adds dense dataset to the internal storage of the builder + object. Specifying your dataset in the dense format means that the dense + version of the forest construction algorithm will be invoked. + + INPUT PARAMETERS: + S - decision forest builder object + XY - array[NPoints,NVars+1] (minimum size; actual size can + be larger, only leading part is used anyway), dataset: + * first NVars elements of each row store values of the + independent variables + * last column store class number (in 0...NClasses-1) + or real value of the dependent variable + NPoints - number of rows in the dataset, NPoints>=1 + NVars - number of independent variables, NVars>=1 + NClasses - indicates type of the problem being solved: + * NClasses>=2 means that classification problem is + solved (last column of the dataset stores class + number) + * NClasses=1 means that regression problem is solved + (last column of the dataset stores variable value) OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, - array[WCount] + S - decision forest builder -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradbatchsubset(multilayerperceptron network, + public static void dfbuildersetdataset(decisionforestbuilder s, double[,] xy, - int setsize, - int[] idx, - int subsetsize, - ref double e, - ref double[] grad, + int npoints, + int nvars, + int nclasses, alglib.xparams _params) { int i = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int npoints = 0; - int subset0 = 0; - int subset1 = 0; - int subsettype = 0; - smlpgrad sgrad = null; - - e = 0; + int j = 0; - alglib.ap.assert(setsize>=0, "MLPGradBatchSubset: SetSize<0"); - alglib.ap.assert(subsetsize<=alglib.ap.len(idx), "MLPGradBatchSubset: SubsetSize>Length(Idx)"); - npoints = setsize; - if( subsetsize<0 ) - { - subset0 = 0; - subset1 = setsize; - subsettype = 0; - } - else + + // + // Check parameters + // + alglib.ap.assert(npoints>=1, "dfbuildersetdataset: npoints<1"); + alglib.ap.assert(nvars>=1, "dfbuildersetdataset: nvars<1"); + alglib.ap.assert(nclasses>=1, "dfbuildersetdataset: nclasses<1"); + alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "dfbuildersetdataset: rows(xy)=nvars+1, "dfbuildersetdataset: cols(xy)1 ) { - subset0 = 0; - subset1 = subsetsize; - subsettype = 1; - for(i=0; i<=subsetsize-1; i++) + for(i=0; i<=npoints-1; i++) { - alglib.ap.assert(idx[i]>=0, "MLPGradBatchSubset: incorrect index of XY row(Idx[I]<0)"); - alglib.ap.assert(idx[i]<=npoints-1, "MLPGradBatchSubset: incorrect index of XY row(Idx[I]>Rows(XY)-1)"); + j = (int)Math.Round(xy[i,nvars]); + alglib.ap.assert(j>=0 && j1 ) { - grad[i] = 0.0; + apserv.ivectorsetlengthatleast(ref s.dsival, npoints, _params); + for(i=0; i<=npoints-1; i++) + { + s.dsival[i] = (int)Math.Round(xy[i,nvars]); + } } - alglib.smp.ae_shared_pool_first_recycled(network.gradbuf, ref sgrad); - while( sgrad!=null ) + else { - e = e+sgrad.f; - for(i=0; i<=wcount-1; i++) + apserv.rvectorsetlengthatleast(ref s.dsrval, npoints, _params); + for(i=0; i<=npoints-1; i++) { - grad[i] = grad[i]+sgrad.g[i]; + s.dsrval[i] = xy[i,nvars]; } - alglib.smp.ae_shared_pool_next_recycled(network.gradbuf, ref sgrad); } } /************************************************************************* - Batch gradient calculation for a set of inputs/outputs for a subset of - dataset given by set of indexes. + This function sets number of variables (in [1,NVars] range) used by + decision forest construction algorithm. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + The default option is to use roughly sqrt(NVars) variables. INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset in sparse format; one sample = one row: - * MATRIX MUST BE STORED IN CRS FORMAT - * first NIn columns contain inputs, - * for regression problem, next NOut columns store - desired outputs. - * for classification problem, next column (just one!) - stores class number. - SetSize - real size of XY, SetSize>=0; - Idx - subset of SubsetSize elements, array[SubsetSize]: - * Idx[I] stores row index in the original dataset which is - given by XY. Gradient is calculated with respect to rows - whose indexes are stored in Idx[]. - * Idx[] must store correct indexes; this function throws - an exception in case incorrect index (less than 0 or - larger than rows(XY)) is given - * Idx[] may store indexes in any order and even with - repetitions. - SubsetSize- number of elements in Idx[] array: - * positive value means that subset given by Idx[] is processed - * zero value results in zero gradient - * negative value means that full dataset is processed - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + S - decision forest builder object + RndVars - number of randomly selected variables; values outside + of [1,NVars] range are silently clipped. OUTPUT PARAMETERS: - E - error function, SUM(sqr(y[i]-desiredy[i])/2,i) - Grad - gradient of E with respect to weights of network, - array[WCount] + S - decision forest builder - NOTE: when SubsetSize<0 is used full dataset by call MLPGradBatchSparse - function. - -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradbatchsparsesubset(multilayerperceptron network, - sparse.sparsematrix xy, - int setsize, - int[] idx, - int subsetsize, - ref double e, - ref double[] grad, + public static void dfbuildersetrndvars(decisionforestbuilder s, + int rndvars, alglib.xparams _params) { - int i = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - int npoints = 0; - int subset0 = 0; - int subset1 = 0; - int subsettype = 0; - smlpgrad sgrad = null; + s.rdfvars = Math.Max(rndvars, 1); + } - e = 0; - alglib.ap.assert(setsize>=0, "MLPGradBatchSparseSubset: SetSize<0"); - alglib.ap.assert(subsetsize<=alglib.ap.len(idx), "MLPGradBatchSparseSubset: SubsetSize>Length(Idx)"); - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPGradBatchSparseSubset: sparse matrix XY must be in CRS format."); - npoints = setsize; - if( subsetsize<0 ) - { - subset0 = 0; - subset1 = setsize; - subsettype = 0; - } - else - { - subset0 = 0; - subset1 = subsetsize; - subsettype = 1; - for(i=0; i<=subsetsize-1; i++) - { - alglib.ap.assert(idx[i]>=0, "MLPGradBatchSparseSubset: incorrect index of XY row(Idx[I]<0)"); - alglib.ap.assert(idx[i]<=npoints-1, "MLPGradBatchSparseSubset: incorrect index of XY row(Idx[I]>Rows(XY)-1)"); - } - } - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - apserv.rvectorsetlengthatleast(ref grad, wcount, _params); - alglib.smp.ae_shared_pool_first_recycled(network.gradbuf, ref sgrad); - while( sgrad!=null ) - { - sgrad.f = 0.0; - for(i=0; i<=wcount-1; i++) - { - sgrad.g[i] = 0.0; - } - alglib.smp.ae_shared_pool_next_recycled(network.gradbuf, ref sgrad); - } - mlpgradbatchx(network, network.dummydxy, xy, setsize, 1, idx, subset0, subset1, subsettype, network.buf, network.gradbuf, _params); - e = 0.0; - for(i=0; i<=wcount-1; i++) - { - grad[i] = 0.0; - } - alglib.smp.ae_shared_pool_first_recycled(network.gradbuf, ref sgrad); - while( sgrad!=null ) - { - e = e+sgrad.f; - for(i=0; i<=wcount-1; i++) - { - grad[i] = grad[i]+sgrad.g[i]; - } - alglib.smp.ae_shared_pool_next_recycled(network.gradbuf, ref sgrad); - } + /************************************************************************* + This function sets number of variables used by decision forest construction + algorithm as a fraction of total variable count (0,1) range. + + The default option is to use roughly sqrt(NVars) variables. + + INPUT PARAMETERS: + S - decision forest builder object + F - round(NVars*F) variables are selected + + OUTPUT PARAMETERS: + S - decision forest builder + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + public static void dfbuildersetrndvarsratio(decisionforestbuilder s, + double f, + alglib.xparams _params) + { + alglib.ap.assert(math.isfinite(f), "dfbuildersetrndvarsratio: F is INF or NAN"); + s.rdfvars = -Math.Max(f, math.machineepsilon); } /************************************************************************* - Internal function which actually calculates batch gradient for a subset or - full dataset, which can be represented in different formats. + This function tells decision forest builder to automatically choose number + of variables used by decision forest construction algorithm. Roughly + sqrt(NVars) variables will be used. - THIS FUNCTION IS NOT INTENDED TO BE USED BY ALGLIB USERS! + INPUT PARAMETERS: + S - decision forest builder object + + OUTPUT PARAMETERS: + S - decision forest builder -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradbatchx(multilayerperceptron network, - double[,] densexy, - sparse.sparsematrix sparsexy, - int datasetsize, - int datasettype, - int[] idx, - int subset0, - int subset1, - int subsettype, - alglib.smp.shared_pool buf, - alglib.smp.shared_pool gradbuf, + public static void dfbuildersetrndvarsauto(decisionforestbuilder s, alglib.xparams _params) { - int nin = 0; - int nout = 0; - int wcount = 0; - int rowsize = 0; - int srcidx = 0; - int cstart = 0; - int csize = 0; - int j = 0; - double problemcost = 0; - hpccores.mlpbuffers buf2 = null; - int len0 = 0; - int len1 = 0; - hpccores.mlpbuffers pbuf = null; - smlpgrad sgrad = null; - int i_ = 0; + s.rdfvars = 0; + } - alglib.ap.assert(datasetsize>=0, "MLPGradBatchX: SetSize<0"); - alglib.ap.assert(datasettype==0 || datasettype==1, "MLPGradBatchX: DatasetType is incorrect"); - alglib.ap.assert(subsettype==0 || subsettype==1, "MLPGradBatchX: SubsetType is incorrect"); - - // - // Determine network and dataset properties - // - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - if( mlpissoftmax(network, _params) ) - { - rowsize = nin+1; - } - else - { - rowsize = nin+nout; - } - - // - // Split problem. - // - // Splitting problem allows us to reduce effect of single-precision - // arithmetics (SSE-optimized version of MLPChunkedGradient uses single - // precision internally, but converts them to double precision after - // results are exported from HPC buffer to network). Small batches are - // calculated in single precision, results are aggregated in double - // precision, and it allows us to avoid accumulation of errors when - // we process very large batches (tens of thousands of items). - // - // NOTE: it is important to use real arithmetics for ProblemCost - // because ProblemCost may be larger than MAXINT. - // - problemcost = subset1-subset0; - problemcost = problemcost*wcount*2; - if( (double)(problemcost)>=(double)(apserv.smpactivationlevel(_params)) && subset1-subset0>=2*microbatchsize ) - { - if( _trypexec_mlpgradbatchx(network,densexy,sparsexy,datasetsize,datasettype,idx,subset0,subset1,subsettype,buf,gradbuf, _params) ) - { - return; - } - } - if( subset1-subset0>=2*microbatchsize && (double)(problemcost)>(double)(apserv.spawnlevel(_params)) ) - { - apserv.splitlength(subset1-subset0, microbatchsize, ref len0, ref len1, _params); - mlpgradbatchx(network, densexy, sparsexy, datasetsize, datasettype, idx, subset0, subset0+len0, subsettype, buf, gradbuf, _params); - mlpgradbatchx(network, densexy, sparsexy, datasetsize, datasettype, idx, subset0+len0, subset1, subsettype, buf, gradbuf, _params); - return; - } - - // - // Chunked processing - // - alglib.smp.ae_shared_pool_retrieve(gradbuf, ref sgrad); - alglib.smp.ae_shared_pool_retrieve(buf, ref pbuf); - hpccores.hpcpreparechunkedgradient(network.weights, wcount, mlpntotal(network, _params), nin, nout, pbuf, _params); - cstart = subset0; - while( cstart=0, "MLPGradBatchX: internal error"); - if( datasettype==0 ) - { - for(i_=0; i_<=rowsize-1;i_++) - { - pbuf.xy[j,i_] = densexy[srcidx,i_]; - } - } - if( datasettype==1 ) - { - sparse.sparsegetrow(sparsexy, srcidx, ref pbuf.xyrow, _params); - for(i_=0; i_<=rowsize-1;i_++) - { - pbuf.xy[j,i_] = pbuf.xyrow[i_]; - } - } - } - - // - // Process chunk and advance line pointer - // - mlpchunkedgradient(network, pbuf.xy, 0, csize, pbuf.batch4buf, pbuf.hpcbuf, ref sgrad.f, false, _params); - cstart = cstart+pbuf.chunksize; - } - hpccores.hpcfinalizechunkedgradient(pbuf, sgrad.g, _params); - alglib.smp.ae_shared_pool_recycle(buf, ref pbuf); - alglib.smp.ae_shared_pool_recycle(gradbuf, ref sgrad); + + /************************************************************************* + This function sets size of dataset subsample generated the decision forest + construction algorithm. Size is specified as a fraction of total dataset + size. + + The default option is to use 50% of the dataset for training, 50% for the + OOB estimates. You can decrease fraction F down to 10%, 1% or even below + in order to reduce overfitting. + + INPUT PARAMETERS: + S - decision forest builder object + F - fraction of the dataset to use, in (0,1] range. Values + outside of this range will be silently clipped. At + least one element is always selected for the training + set. + + OUTPUT PARAMETERS: + S - decision forest builder + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + public static void dfbuildersetsubsampleratio(decisionforestbuilder s, + double f, + alglib.xparams _params) + { + alglib.ap.assert(math.isfinite(f), "dfbuildersetrndvarsfraction: F is INF or NAN"); + s.rdfratio = Math.Max(f, math.machineepsilon); } /************************************************************************* - Serial stub for GPL edition. + This function sets seed used by internal RNG for random subsampling and + random selection of variable subsets. + + By default random seed is used, i.e. every time you build decision forest, + we seed generator with new value obtained from system-wide RNG. Thus, + decision forest builder returns non-deterministic results. You can change + such behavior by specyfing fixed positive seed value. + + INPUT PARAMETERS: + S - decision forest builder object + SeedVal - seed value: + * positive values are used for seeding RNG with fixed + seed, i.e. subsequent runs on same data will return + same decision forests + * non-positive seed means that random seed is used + for every run of builder, i.e. subsequent runs on + same datasets will return slightly different + decision forests + + OUTPUT PARAMETERS: + S - decision forest builder, see + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static bool _trypexec_mlpgradbatchx(multilayerperceptron network, - double[,] densexy, - sparse.sparsematrix sparsexy, - int datasetsize, - int datasettype, - int[] idx, - int subset0, - int subset1, - int subsettype, - alglib.smp.shared_pool buf, - alglib.smp.shared_pool gradbuf, alglib.xparams _params) + public static void dfbuildersetseed(decisionforestbuilder s, + int seedval, + alglib.xparams _params) + { + s.rdfglobalseed = seedval; + } + + + /************************************************************************* + This function sets random decision forest construction algorithm. + + As for now, only one decision forest construction algorithm is supported - + a dense "baseline" RDF algorithm. + + INPUT PARAMETERS: + S - decision forest builder object + AlgoType - algorithm type: + * 0 = baseline dense RDF + + OUTPUT PARAMETERS: + S - decision forest builder, see + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + public static void dfbuildersetrdfalgo(decisionforestbuilder s, + int algotype, + alglib.xparams _params) { - return false; + alglib.ap.assert(algotype==0, "dfbuildersetrdfalgo: unexpected algotype"); + s.rdfalgo = algotype; } /************************************************************************* - Batch gradient calculation for a set of inputs/outputs - (natural error function is used) + This function sets split selection algorithm used by decision forest + classifier. You may choose several algorithms, with different speed and + quality of the results. INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - set of inputs/outputs; one sample = one row; - first NIn columns contain inputs, - next NOut columns - desired outputs. - SSize - number of elements in XY - Grad - possibly preallocated array. If size of array is smaller - than WCount, it will be reallocated. It is recommended to - reuse previously allocated array to reduce allocation - overhead. + S - decision forest builder object + SplitStrength- split type: + * 0 = split at the random position, fastest one + * 1 = split at the middle of the range + * 2 = strong split at the best point of the range (default) OUTPUT PARAMETERS: - E - error function, sum-of-squares for regression networks, - cross-entropy for classification networks. - Grad - gradient of E with respect to weights of network, array[WCount] + S - decision forest builder, see -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpgradnbatch(multilayerperceptron network, - double[,] xy, - int ssize, - ref double e, - ref double[] grad, + public static void dfbuildersetrdfsplitstrength(decisionforestbuilder s, + int splitstrength, alglib.xparams _params) { - int i = 0; - int nin = 0; - int nout = 0; - int wcount = 0; - hpccores.mlpbuffers pbuf = null; - - e = 0; - - - // - // Alloc - // - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - alglib.smp.ae_shared_pool_retrieve(network.buf, ref pbuf); - hpccores.hpcpreparechunkedgradient(network.weights, wcount, mlpntotal(network, _params), nin, nout, pbuf, _params); - apserv.rvectorsetlengthatleast(ref grad, wcount, _params); - for(i=0; i<=wcount-1; i++) - { - grad[i] = 0; - } - e = 0; - i = 0; - while( i<=ssize-1 ) - { - mlpchunkedgradient(network, xy, i, Math.Min(ssize, i+pbuf.chunksize)-i, pbuf.batch4buf, pbuf.hpcbuf, ref e, true, _params); - i = i+pbuf.chunksize; - } - hpccores.hpcfinalizechunkedgradient(pbuf, grad, _params); - alglib.smp.ae_shared_pool_recycle(network.buf, ref pbuf); + alglib.ap.assert((splitstrength==0 || splitstrength==1) || splitstrength==2, "dfbuildersetrdfsplitstrength: unexpected split type"); + s.rdfsplitstrength = splitstrength; } /************************************************************************* - Batch Hessian calculation (natural error function) using R-algorithm. - Internal subroutine. + This function tells decision forest construction algorithm to use + Gini impurity based variable importance estimation (also known as MDI). + + This version of importance estimation algorithm analyzes mean decrease in + impurity (MDI) on training sample during splits. The result is divided + by impurity at the root node in order to produce estimate in [0,1] range. + + Such estimates are fast to calculate and beautifully normalized (sum to + one) but have following downsides: + * They ALWAYS sum to 1.0, even if output is completely unpredictable. I.e. + MDI allows to order variables by importance, but does not tell us about + "absolute" importances of variables + * there exist some bias towards continuous and high-cardinality categorical + variables + + NOTE: informally speaking, MDA (permutation importance) rating answers the + question "what part of the model predictive power is ruined by + permuting k-th variable?" while MDI tells us "what part of the model + predictive power was achieved due to usage of k-th variable". + + Thus, MDA rates each variable independently at "0 to 1" scale while + MDI (and OOB-MDI too) tends to divide "unit amount of importance" + between several important variables. + + If all variables are equally important, they will have same + MDI/OOB-MDI rating, equal (for OOB-MDI: roughly equal) to 1/NVars. + However, roughly same picture will be produced for the "all + variables provide information no one is critical" situation and for + the "all variables are critical, drop any one, everything is ruined" + situation. + + Contrary to that, MDA will rate critical variable as ~1.0 important, + and important but non-critical variable will have less than unit + rating. + + NOTE: quite an often MDA and MDI return same results. It generally happens + on problems with low test set error (a few percents at most) and + large enough training set to avoid overfitting. + + The difference between MDA, MDI and OOB-MDI becomes important only + on "hard" tasks with high test set error and/or small training set. + + INPUT PARAMETERS: + S - decision forest builder object + + OUTPUT PARAMETERS: + S - decision forest builder object. Next call to the forest + construction function will produce: + * importance estimates in rep.varimportances field + * variable ranks in rep.topvars field -- ALGLIB -- - Copyright 26.01.2008 by Bochkanov Sergey. - - Hessian calculation based on R-algorithm described in - "Fast Exact Multiplication by the Hessian", - B. A. Pearlmutter, - Neural Computation, 1994. + Copyright 29.07.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlphessiannbatch(multilayerperceptron network, - double[,] xy, - int ssize, - ref double e, - ref double[] grad, - ref double[,] h, + public static void dfbuildersetimportancetrngini(decisionforestbuilder s, alglib.xparams _params) { - e = 0; - - mlphessianbatchinternal(network, xy, ssize, true, ref e, ref grad, ref h, _params); + s.rdfimportance = needtrngini; } /************************************************************************* - Batch Hessian calculation using R-algorithm. - Internal subroutine. + This function tells decision forest construction algorithm to use + out-of-bag version of Gini variable importance estimation (also known as + OOB-MDI). - -- ALGLIB -- - Copyright 26.01.2008 by Bochkanov Sergey. + This version of importance estimation algorithm analyzes mean decrease in + impurity (MDI) on out-of-bag sample during splits. The result is divided + by impurity at the root node in order to produce estimate in [0,1] range. - Hessian calculation based on R-algorithm described in - "Fast Exact Multiplication by the Hessian", - B. A. Pearlmutter, - Neural Computation, 1994. - *************************************************************************/ - public static void mlphessianbatch(multilayerperceptron network, - double[,] xy, - int ssize, - ref double e, - ref double[] grad, - ref double[,] h, - alglib.xparams _params) - { - e = 0; + Such estimates are fast to calculate and resistant to overfitting issues + (thanks to the out-of-bag estimates used). However, OOB Gini rating has + following downsides: + * there exist some bias towards continuous and high-cardinality categorical + variables + * Gini rating allows us to order variables by importance, but it is hard + to define importance of the variable by itself. + + NOTE: informally speaking, MDA (permutation importance) rating answers the + question "what part of the model predictive power is ruined by + permuting k-th variable?" while MDI tells us "what part of the model + predictive power was achieved due to usage of k-th variable". - mlphessianbatchinternal(network, xy, ssize, false, ref e, ref grad, ref h, _params); - } + Thus, MDA rates each variable independently at "0 to 1" scale while + MDI (and OOB-MDI too) tends to divide "unit amount of importance" + between several important variables. + + If all variables are equally important, they will have same + MDI/OOB-MDI rating, equal (for OOB-MDI: roughly equal) to 1/NVars. + However, roughly same picture will be produced for the "all + variables provide information no one is critical" situation and for + the "all variables are critical, drop any one, everything is ruined" + situation. + + Contrary to that, MDA will rate critical variable as ~1.0 important, + and important but non-critical variable will have less than unit + rating. + NOTE: quite an often MDA and MDI return same results. It generally happens + on problems with low test set error (a few percents at most) and + large enough training set to avoid overfitting. + + The difference between MDA, MDI and OOB-MDI becomes important only + on "hard" tasks with high test set error and/or small training set. - /************************************************************************* - Internal subroutine, shouldn't be called by user. + INPUT PARAMETERS: + S - decision forest builder object + + OUTPUT PARAMETERS: + S - decision forest builder object. Next call to the forest + construction function will produce: + * importance estimates in rep.varimportances field + * variable ranks in rep.topvars field + + -- ALGLIB -- + Copyright 29.07.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpinternalprocessvector(int[] structinfo, - double[] weights, - double[] columnmeans, - double[] columnsigmas, - ref double[] neurons, - ref double[] dfdnet, - double[] x, - ref double[] y, + public static void dfbuildersetimportanceoobgini(decisionforestbuilder s, alglib.xparams _params) { - int i = 0; - int n1 = 0; - int n2 = 0; - int w1 = 0; - int w2 = 0; - int ntotal = 0; - int nin = 0; - int nout = 0; - int istart = 0; - int offs = 0; - double net = 0; - double f = 0; - double df = 0; - double d2f = 0; - double mx = 0; - bool perr = new bool(); - int i_ = 0; - int i1_ = 0; - - - // - // Read network geometry - // - nin = structinfo[1]; - nout = structinfo[2]; - ntotal = structinfo[3]; - istart = structinfo[5]; - - // - // Inputs standartisation and putting in the network - // - for(i=0; i<=nin-1; i++) - { - if( (double)(columnsigmas[i])!=(double)(0) ) - { - neurons[i] = (x[i]-columnmeans[i])/columnsigmas[i]; - } - else - { - neurons[i] = x[i]-columnmeans[i]; - } - } - - // - // Process network - // - for(i=0; i<=ntotal-1; i++) - { - offs = istart+i*nfieldwidth; - if( structinfo[offs+0]>0 || structinfo[offs+0]==-5 ) - { - - // - // Activation function - // - mlpactivationfunction(neurons[structinfo[offs+2]], structinfo[offs+0], ref f, ref df, ref d2f, _params); - neurons[i] = f; - dfdnet[i] = df; - continue; - } - if( structinfo[offs+0]==0 ) - { - - // - // Adaptive summator - // - n1 = structinfo[offs+2]; - n2 = n1+structinfo[offs+1]-1; - w1 = structinfo[offs+3]; - w2 = w1+structinfo[offs+1]-1; - i1_ = (n1)-(w1); - net = 0.0; - for(i_=w1; i_<=w2;i_++) - { - net += weights[i_]*neurons[i_+i1_]; - } - neurons[i] = net; - dfdnet[i] = 1.0; - apserv.touchint(ref n2, _params); - continue; - } - if( structinfo[offs+0]<0 ) - { - perr = true; - if( structinfo[offs+0]==-2 ) - { - - // - // input neuron, left unchanged - // - perr = false; - } - if( structinfo[offs+0]==-3 ) - { - - // - // "-1" neuron - // - neurons[i] = -1; - perr = false; - } - if( structinfo[offs+0]==-4 ) - { - - // - // "0" neuron - // - neurons[i] = 0; - perr = false; - } - alglib.ap.assert(!perr, "MLPInternalProcessVector: internal error - unknown neuron type!"); - continue; - } - } - - // - // Extract result - // - i1_ = (ntotal-nout) - (0); - for(i_=0; i_<=nout-1;i_++) - { - y[i_] = neurons[i_+i1_]; - } - - // - // Softmax post-processing or standardisation if needed - // - alglib.ap.assert(structinfo[6]==0 || structinfo[6]==1, "MLPInternalProcessVector: unknown normalization type!"); - if( structinfo[6]==1 ) - { - - // - // Softmax - // - mx = y[0]; - for(i=1; i<=nout-1; i++) - { - mx = Math.Max(mx, y[i]); - } - net = 0; - for(i=0; i<=nout-1; i++) - { - y[i] = Math.Exp(y[i]-mx); - net = net+y[i]; - } - for(i=0; i<=nout-1; i++) - { - y[i] = y[i]/net; - } - } - else - { - - // - // Standardisation - // - for(i=0; i<=nout-1; i++) - { - y[i] = y[i]*columnsigmas[nin+i]+columnmeans[nin+i]; - } - } + s.rdfimportance = needoobgini; } /************************************************************************* - Serializer: allocation + This function tells decision forest construction algorithm to use + permutation variable importance estimator (also known as MDA). + + This version of importance estimation algorithm analyzes mean increase in + out-of-bag sum of squared residuals after random permutation of J-th + variable. The result is divided by error computed with all variables being + perturbed in order to produce R-squared-like estimate in [0,1] range. + + Such estimate is slower to calculate than Gini-based rating because it + needs multiple inference runs for each of variables being studied. + + ALGLIB uses parallelized and highly optimized algorithm which analyzes + path through the decision tree and allows to handle most perturbations + in O(1) time; nevertheless, requesting MDA importances may increase forest + construction time from 10% to 200% (or more, if you have thousands of + variables). + + However, MDA rating has following benefits over Gini-based ones: + * no bias towards specific variable types + * ability to directly evaluate "absolute" importance of some variable at + "0 to 1" scale (contrary to Gini-based rating, which returns comparative + importances). + + NOTE: informally speaking, MDA (permutation importance) rating answers the + question "what part of the model predictive power is ruined by + permuting k-th variable?" while MDI tells us "what part of the model + predictive power was achieved due to usage of k-th variable". + + Thus, MDA rates each variable independently at "0 to 1" scale while + MDI (and OOB-MDI too) tends to divide "unit amount of importance" + between several important variables. + + If all variables are equally important, they will have same + MDI/OOB-MDI rating, equal (for OOB-MDI: roughly equal) to 1/NVars. + However, roughly same picture will be produced for the "all + variables provide information no one is critical" situation and for + the "all variables are critical, drop any one, everything is ruined" + situation. + + Contrary to that, MDA will rate critical variable as ~1.0 important, + and important but non-critical variable will have less than unit + rating. + + NOTE: quite an often MDA and MDI return same results. It generally happens + on problems with low test set error (a few percents at most) and + large enough training set to avoid overfitting. + + The difference between MDA, MDI and OOB-MDI becomes important only + on "hard" tasks with high test set error and/or small training set. + + INPUT PARAMETERS: + S - decision forest builder object + + OUTPUT PARAMETERS: + S - decision forest builder object. Next call to the forest + construction function will produce: + * importance estimates in rep.varimportances field + * variable ranks in rep.topvars field -- ALGLIB -- - Copyright 14.03.2011 by Bochkanov Sergey + Copyright 29.07.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpalloc(alglib.serializer s, - multilayerperceptron network, + public static void dfbuildersetimportancepermutation(decisionforestbuilder s, alglib.xparams _params) { - int i = 0; - int j = 0; - int k = 0; - int fkind = 0; - double threshold = 0; - double v0 = 0; - double v1 = 0; - int nin = 0; - int nout = 0; - int[] integerbuf = new int[0]; - - nin = network.hllayersizes[0]; - nout = network.hllayersizes[alglib.ap.len(network.hllayersizes)-1]; - s.alloc_entry(); - s.alloc_entry(); - s.alloc_entry(); - apserv.allocintegerarray(s, network.hllayersizes, -1, _params); - for(i=1; i<=alglib.ap.len(network.hllayersizes)-1; i++) - { - for(j=0; j<=network.hllayersizes[i]-1; j++) - { - mlpgetneuroninfox(network, i, j, ref integerbuf, ref fkind, ref threshold, _params); - s.alloc_entry(); - s.alloc_entry(); - for(k=0; k<=network.hllayersizes[i-1]-1; k++) - { - s.alloc_entry(); - } - } - } - for(j=0; j<=nin-1; j++) - { - mlpgetinputscaling(network, j, ref v0, ref v1, _params); - s.alloc_entry(); - s.alloc_entry(); - } - for(j=0; j<=nout-1; j++) - { - mlpgetoutputscaling(network, j, ref v0, ref v1, _params); - s.alloc_entry(); - s.alloc_entry(); - } + s.rdfimportance = needpermutation; } /************************************************************************* - Serializer: serialization + This function tells decision forest construction algorithm to skip + variable importance estimation. - -- ALGLIB -- - Copyright 14.03.2011 by Bochkanov Sergey - *************************************************************************/ - public static void mlpserialize(alglib.serializer s, - multilayerperceptron network, - alglib.xparams _params) - { - int i = 0; - int j = 0; - int k = 0; - int fkind = 0; - double threshold = 0; - double v0 = 0; - double v1 = 0; - int nin = 0; - int nout = 0; - int[] integerbuf = new int[0]; + INPUT PARAMETERS: + S - decision forest builder object + + OUTPUT PARAMETERS: + S - decision forest builder object. Next call to the forest + construction function will result in forest being built + without variable importance estimation. - nin = network.hllayersizes[0]; - nout = network.hllayersizes[alglib.ap.len(network.hllayersizes)-1]; - s.serialize_int(scodes.getmlpserializationcode(_params)); - s.serialize_int(mlpfirstversion); - s.serialize_bool(mlpissoftmax(network, _params)); - apserv.serializeintegerarray(s, network.hllayersizes, -1, _params); - for(i=1; i<=alglib.ap.len(network.hllayersizes)-1; i++) - { - for(j=0; j<=network.hllayersizes[i]-1; j++) - { - mlpgetneuroninfox(network, i, j, ref integerbuf, ref fkind, ref threshold, _params); - s.serialize_int(fkind); - s.serialize_double(threshold); - for(k=0; k<=network.hllayersizes[i-1]-1; k++) - { - s.serialize_double(mlpgetweightx(network, i-1, k, i, j, ref integerbuf, _params)); - } - } - } - for(j=0; j<=nin-1; j++) - { - mlpgetinputscaling(network, j, ref v0, ref v1, _params); - s.serialize_double(v0); - s.serialize_double(v1); - } - for(j=0; j<=nout-1; j++) - { - mlpgetoutputscaling(network, j, ref v0, ref v1, _params); - s.serialize_double(v0); - s.serialize_double(v1); - } + -- ALGLIB -- + Copyright 29.07.2019 by Bochkanov Sergey + *************************************************************************/ + public static void dfbuildersetimportancenone(decisionforestbuilder s, + alglib.xparams _params) + { + s.rdfimportance = 0; } /************************************************************************* - Serializer: unserialization + This function is an alias for dfbuilderpeekprogress(), left in ALGLIB for + backward compatibility reasons. -- ALGLIB -- - Copyright 14.03.2011 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpunserialize(alglib.serializer s, - multilayerperceptron network, + public static double dfbuildergetprogress(decisionforestbuilder s, alglib.xparams _params) { - int i0 = 0; - int i1 = 0; - int i = 0; - int j = 0; - int k = 0; - int fkind = 0; - double threshold = 0; - double v0 = 0; - double v1 = 0; - int nin = 0; - int nout = 0; - bool issoftmax = new bool(); - int[] layersizes = new int[0]; + double result = 0; - - // - // check correctness of header - // - i0 = s.unserialize_int(); - alglib.ap.assert(i0==scodes.getmlpserializationcode(_params), "MLPUnserialize: stream header corrupted"); - i1 = s.unserialize_int(); - alglib.ap.assert(i1==mlpfirstversion, "MLPUnserialize: stream header corrupted"); - - // - // Create network - // - issoftmax = s.unserialize_bool(); - apserv.unserializeintegerarray(s, ref layersizes, _params); - alglib.ap.assert((alglib.ap.len(layersizes)==2 || alglib.ap.len(layersizes)==3) || alglib.ap.len(layersizes)==4, "MLPUnserialize: too many hidden layers!"); - nin = layersizes[0]; - nout = layersizes[alglib.ap.len(layersizes)-1]; - if( alglib.ap.len(layersizes)==2 ) - { - if( issoftmax ) - { - mlpcreatec0(layersizes[0], layersizes[1], network, _params); - } - else - { - mlpcreate0(layersizes[0], layersizes[1], network, _params); - } - } - if( alglib.ap.len(layersizes)==3 ) - { - if( issoftmax ) - { - mlpcreatec1(layersizes[0], layersizes[1], layersizes[2], network, _params); - } - else - { - mlpcreate1(layersizes[0], layersizes[1], layersizes[2], network, _params); - } - } - if( alglib.ap.len(layersizes)==4 ) - { - if( issoftmax ) - { - mlpcreatec2(layersizes[0], layersizes[1], layersizes[2], layersizes[3], network, _params); - } - else - { - mlpcreate2(layersizes[0], layersizes[1], layersizes[2], layersizes[3], network, _params); - } - } - - // - // Load neurons and weights - // - for(i=1; i<=alglib.ap.len(layersizes)-1; i++) - { - for(j=0; j<=layersizes[i]-1; j++) - { - fkind = s.unserialize_int(); - threshold = s.unserialize_double(); - mlpsetneuroninfo(network, i, j, fkind, threshold, _params); - for(k=0; k<=layersizes[i-1]-1; k++) - { - v0 = s.unserialize_double(); - mlpsetweight(network, i-1, k, i, j, v0, _params); - } - } - } - - // - // Load standartizator - // - for(j=0; j<=nin-1; j++) - { - v0 = s.unserialize_double(); - v1 = s.unserialize_double(); - mlpsetinputscaling(network, j, v0, v1, _params); - } - for(j=0; j<=nout-1; j++) - { - v0 = s.unserialize_double(); - v1 = s.unserialize_double(); - mlpsetoutputscaling(network, j, v0, v1, _params); - } + result = dfbuilderpeekprogress(s, _params); + return result; } /************************************************************************* - Calculation of all types of errors on subset of dataset. + This function is used to peek into decision forest construction process + from some other thread and get current progress indicator. - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + It returns value in [0,1]. INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset; one sample = one row; - first NIn columns contain inputs, - next NOut columns - desired outputs. - SetSize - real size of XY, SetSize>=0; - Subset - subset of SubsetSize elements, array[SubsetSize]; - SubsetSize- number of elements in Subset[] array: - * if SubsetSize>0, rows of XY with indices Subset[0]... - ...Subset[SubsetSize-1] are processed - * if SubsetSize=0, zeros are returned - * if SubsetSize<0, entire dataset is processed; Subset[] - array is ignored in this case. + S - decision forest builder object used to build forest + in some other thread - OUTPUT PARAMETERS: - Rep - it contains all type of errors. + RESULT: + progress value, in [0,1] -- ALGLIB -- - Copyright 04.09.2012 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpallerrorssubset(multilayerperceptron network, - double[,] xy, - int setsize, - int[] subset, - int subsetsize, - modelerrors rep, + public static double dfbuilderpeekprogress(decisionforestbuilder s, alglib.xparams _params) { - int idx0 = 0; - int idx1 = 0; - int idxtype = 0; + double result = 0; - alglib.ap.assert(alglib.ap.rows(xy)>=setsize, "MLPAllErrorsSubset: XY has less than SetSize rows"); - if( setsize>0 ) - { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+1, "MLPAllErrorsSubset: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPAllErrorsSubset: XY has less than NIn+NOut columns"); - } - } - if( subsetsize>=0 ) - { - idx0 = 0; - idx1 = subsetsize; - idxtype = 1; - } - else - { - idx0 = 0; - idx1 = setsize; - idxtype = 0; - } - mlpallerrorsx(network, xy, network.dummysxy, setsize, 0, subset, idx0, idx1, idxtype, network.buf, rep, _params); + result = s.rdfprogress/Math.Max(s.rdftotal, 1); + result = Math.Max(result, 0); + result = Math.Min(result, 1); + return result; } /************************************************************************* - Calculation of all types of errors on subset of dataset. + This subroutine builds decision forest according to current settings using + dataset internally stored in the builder object. Dense algorithm is used. + + NOTE: this function uses dense algorithm for forest construction + independently from the dataset format (dense or sparse). + + NOTE: forest built with this function is stored in-memory using 64-bit + data structures for offsets/indexes/split values. It is possible to + convert forest into more memory-efficient compressed binary + representation. Depending on the problem properties, 3.7x-5.7x + compression factors are possible. + + The downsides of compression are (a) slight reduction in the model + accuracy and (b) ~1.5x reduction in the inference speed (due to + increased complexity of the storage format). + + See comments on dfbinarycompression() for more info. + + Default settings are used by the algorithm; you can tweak them with the + help of the following functions: + * dfbuildersetrfactor() - to control a fraction of the dataset used for + subsampling + * dfbuildersetrandomvars() - to control number of variables randomly chosen + for decision rule creation ! COMMERCIAL EDITION OF ALGLIB: ! @@ -18883,4266 +16006,4537 @@ Calculation of all types of errors on subset of dataset. ! related features provided by commercial edition of ALGLIB. INPUT PARAMETERS: - Network - network initialized with one of the network creation funcs - XY - original dataset given by sparse matrix; - one sample = one row; - first NIn columns contain inputs, - next NOut columns - desired outputs. - SetSize - real size of XY, SetSize>=0; - Subset - subset of SubsetSize elements, array[SubsetSize]; - SubsetSize- number of elements in Subset[] array: - * if SubsetSize>0, rows of XY with indices Subset[0]... - ...Subset[SubsetSize-1] are processed - * if SubsetSize=0, zeros are returned - * if SubsetSize<0, entire dataset is processed; Subset[] - array is ignored in this case. + S - decision forest builder object + NTrees - NTrees>=1, number of trees to train OUTPUT PARAMETERS: - Rep - it contains all type of errors. + DF - decision forest. You can compress this forest to more + compact 16-bit representation with dfbinarycompression() + Rep - report, see below for information on its fields. + + === report information produced by forest construction function ========== + + Decision forest training report includes following information: + * training set errors + * out-of-bag estimates of errors + * variable importance ratings + + Following fields are used to store information: + * training set errors are stored in rep.relclserror, rep.avgce, rep.rmserror, + rep.avgerror and rep.avgrelerror + * out-of-bag estimates of errors are stored in rep.oobrelclserror, rep.oobavgce, + rep.oobrmserror, rep.oobavgerror and rep.oobavgrelerror + + Variable importance reports, if requested by dfbuildersetimportancegini(), + dfbuildersetimportancetrngini() or dfbuildersetimportancepermutation() + call, are stored in: + * rep.varimportances field stores importance ratings + * rep.topvars stores variable indexes ordered from the most important to + less important ones + You can find more information about report fields in: + * comments on dfreport structure + * comments on dfbuildersetimportancegini function + * comments on dfbuildersetimportancetrngini function + * comments on dfbuildersetimportancepermutation function -- ALGLIB -- - Copyright 04.09.2012 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpallerrorssparsesubset(multilayerperceptron network, - sparse.sparsematrix xy, - int setsize, - int[] subset, - int subsetsize, - modelerrors rep, + public static void dfbuilderbuildrandomforest(decisionforestbuilder s, + int ntrees, + decisionforest df, + dfreport rep, alglib.xparams _params) { - int idx0 = 0; - int idx1 = 0; - int idxtype = 0; + int i = 0; + int j = 0; + int nvars = 0; + int nclasses = 0; + int npoints = 0; + int trnsize = 0; + int maxtreesize = 0; + int sessionseed = 0; + dfworkbuf workbufseed = new dfworkbuf(); + dfvotebuf votebufseed = new dfvotebuf(); + dftreebuf treebufseed = new dftreebuf(); - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPAllErrorsSparseSubset: XY is not in CRS format."); - alglib.ap.assert(sparse.sparsegetnrows(xy, _params)>=setsize, "MLPAllErrorsSparseSubset: XY has less than SetSize rows"); - if( setsize>0 ) + alglib.ap.assert(ntrees>=1, "DFBuilderBuildRandomForest: ntrees<1"); + cleanreport(s, rep, _params); + npoints = s.npoints; + nvars = s.nvars; + nclasses = s.nclasses; + + // + // Set up progress counter + // + s.rdfprogress = 0; + s.rdftotal = ntrees*npoints; + if( s.rdfimportance==needpermutation ) { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+1, "MLPAllErrorsSparseSubset: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPAllErrorsSparseSubset: XY has less than NIn+NOut columns"); - } + s.rdftotal = s.rdftotal+ntrees*npoints; } - if( subsetsize>=0 ) + + // + // Quick exit for empty dataset + // + if( s.dstype==-1 || npoints==0 ) { - idx0 = 0; - idx1 = subsetsize; - idxtype = 1; + alglib.ap.assert(leafnodewidth==2, "DFBuilderBuildRandomForest: integrity check failed"); + df.forestformat = dfuncompressedv0; + df.nvars = s.nvars; + df.nclasses = s.nclasses; + df.ntrees = 1; + df.bufsize = 1+leafnodewidth; + df.trees = new double[1+leafnodewidth]; + df.trees[0] = 1+leafnodewidth; + df.trees[1] = -1; + df.trees[2] = 0.0; + dfcreatebuffer(df, df.buffer, _params); + return; } - else + alglib.ap.assert(npoints>0, "DFBuilderBuildRandomForest: integrity check failed"); + + // + // Analyze dataset statistics, perform preprocessing + // + analyzeandpreprocessdataset(s, _params); + + // + // Prepare "work", "vote" and "tree" pools and other settings + // + trnsize = (int)Math.Round(npoints*s.rdfratio); + trnsize = Math.Max(trnsize, 1); + trnsize = Math.Min(trnsize, npoints); + maxtreesize = 1+innernodewidth*(trnsize-1)+leafnodewidth*trnsize; + workbufseed.varpool = new int[nvars]; + workbufseed.trnset = new int[trnsize]; + workbufseed.oobset = new int[npoints-trnsize]; + workbufseed.tmp0i = new int[npoints]; + workbufseed.tmp1i = new int[npoints]; + workbufseed.tmp0r = new double[npoints]; + workbufseed.tmp1r = new double[npoints]; + workbufseed.tmp2r = new double[npoints]; + workbufseed.tmp3r = new double[npoints]; + workbufseed.trnlabelsi = new int[npoints]; + workbufseed.trnlabelsr = new double[npoints]; + workbufseed.ooblabelsi = new int[npoints]; + workbufseed.ooblabelsr = new double[npoints]; + workbufseed.curvals = new double[npoints]; + workbufseed.bestvals = new double[npoints]; + workbufseed.classpriors = new int[nclasses]; + workbufseed.classtotals0 = new int[nclasses]; + workbufseed.classtotals1 = new int[nclasses]; + workbufseed.classtotals01 = new int[2*nclasses]; + workbufseed.treebuf = new double[maxtreesize]; + workbufseed.trnsize = trnsize; + workbufseed.oobsize = npoints-trnsize; + votebufseed.trntotals = new double[npoints*nclasses]; + votebufseed.oobtotals = new double[npoints*nclasses]; + for(i=0; i<=npoints*nclasses-1; i++) { - idx0 = 0; - idx1 = setsize; - idxtype = 0; + votebufseed.trntotals[i] = 0; + votebufseed.oobtotals[i] = 0; } - mlpallerrorsx(network, network.dummydxy, xy, setsize, 1, subset, idx0, idx1, idxtype, network.buf, rep, _params); + votebufseed.trncounts = new int[npoints]; + votebufseed.oobcounts = new int[npoints]; + for(i=0; i<=npoints-1; i++) + { + votebufseed.trncounts[i] = 0; + votebufseed.oobcounts[i] = 0; + } + votebufseed.giniimportances = new double[nvars]; + for(i=0; i<=nvars-1; i++) + { + votebufseed.giniimportances[i] = 0.0; + } + treebufseed.treeidx = -1; + alglib.smp.ae_shared_pool_set_seed(s.workpool, workbufseed); + alglib.smp.ae_shared_pool_set_seed(s.votepool, votebufseed); + alglib.smp.ae_shared_pool_set_seed(s.treepool, treebufseed); + alglib.smp.ae_shared_pool_set_seed(s.treefactory, treebufseed); + + // + // Select session seed (individual trees are constructed using + // combination of session and local seeds). + // + sessionseed = s.rdfglobalseed; + if( s.rdfglobalseed<=0 ) + { + sessionseed = math.randominteger(30000); + } + + // + // Prepare In-and-Out-of-Bag matrix, if needed + // + s.neediobmatrix = s.rdfimportance==needpermutation; + if( s.neediobmatrix ) + { + + // + // Prepare default state of In-and-Out-of-Bag matrix + // + apserv.bmatrixsetlengthatleast(ref s.iobmatrix, ntrees, npoints, _params); + for(i=0; i<=ntrees-1; i++) + { + for(j=0; j<=npoints-1; j++) + { + s.iobmatrix[i,j] = false; + } + } + } + + // + // Build trees (in parallel, if possible) + // + buildrandomtree(s, 0, ntrees, _params); + + // + // Merge trees and output result + // + mergetrees(s, df, _params); + + // + // Process voting results and output training set and OOB errors. + // Finalize tree construction. + // + processvotingresults(s, ntrees, votebufseed, rep, _params); + dfcreatebuffer(df, df.buffer, _params); + + // + // Perform variable importance estimation + // + estimatevariableimportance(s, sessionseed, df, ntrees, rep, _params); + + // + // Update progress counter + // + s.rdfprogress = s.rdftotal; } /************************************************************************* - Error of the neural network on subset of dataset. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. + This function performs binary compression of the decision forest. - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format; - SetSize - real size of XY, SetSize>=0; - Subset - subset of SubsetSize elements, array[SubsetSize]; - SubsetSize- number of elements in Subset[] array: - * if SubsetSize>0, rows of XY with indices Subset[0]... - ...Subset[SubsetSize-1] are processed - * if SubsetSize=0, zeros are returned - * if SubsetSize<0, entire dataset is processed; Subset[] - array is ignored in this case. + Original decision forest produced by the forest builder is stored using + 64-bit representation for all numbers - offsets, variable indexes, split + points. - RESULT: - sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) + It is possible to significantly reduce model size by means of: + * using compressed dynamic encoding for integers (offsets and variable + indexes), which uses just 1 byte to store small ints (less than 128), + just 2 bytes for larger values (less than 128^2) and so on + * storing floating point numbers using 8-bit exponent and 16-bit mantissa - DATASET FORMAT: + As result, model needs significantly less memory (compression factor + depends on variable and class counts). In particular: + * NVars<128 and NClasses<128 result in 4.4x-5.7x model size reduction + * NVars<16384 and NClasses<128 result in 3.7x-4.5x model size reduction - This function uses two different dataset formats - one for regression - networks, another one for classification networks. + Such storage format performs lossless compression of all integers, but + compression of floating point values (split values) is lossy, with roughly + 0.01% relative error introduced during rounding. Thus, we recommend you to + re-evaluate model accuracy after compression. - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs + Another downside of compression is ~1.5x reduction in the inference + speed due to necessity of dynamic decompression of the compressed model. - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + INPUT PARAMETERS: + DF - decision forest built by forest builder + + OUTPUT PARAMETERS: + DF - replaced by compressed forest + + RESULT: + compression factor (in-RAM size of the compressed model vs than of the + uncompressed one), positive number larger than 1.0 -- ALGLIB -- - Copyright 04.09.2012 by Bochkanov Sergey + Copyright 22.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlperrorsubset(multilayerperceptron network, - double[,] xy, - int setsize, - int[] subset, - int subsetsize, + public static double dfbinarycompression(decisionforest df, alglib.xparams _params) { double result = 0; - int idx0 = 0; - int idx1 = 0; - int idxtype = 0; - alglib.ap.assert(alglib.ap.rows(xy)>=setsize, "MLPErrorSubset: XY has less than SetSize rows"); - if( setsize>0 ) - { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+1, "MLPErrorSubset: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(alglib.ap.cols(xy)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPErrorSubset: XY has less than NIn+NOut columns"); - } - } - if( subsetsize>=0 ) - { - idx0 = 0; - idx1 = subsetsize; - idxtype = 1; - } - else - { - idx0 = 0; - idx1 = setsize; - idxtype = 0; - } - mlpallerrorsx(network, xy, network.dummysxy, setsize, 0, subset, idx0, idx1, idxtype, network.buf, network.err, _params); - result = math.sqr(network.err.rmserror)*(idx1-idx0)*mlpgetoutputscount(network, _params)/2; + result = binarycompression(df, false, _params); return result; } /************************************************************************* - Error of the neural network on subset of sparse dataset. - - ! COMMERCIAL EDITION OF ALGLIB: - ! - ! Commercial Edition of ALGLIB includes following important improvements - ! of this function: - ! * high-performance native backend with same C# interface (C# version) - ! * multithreading support (C++ and C# versions) - ! - ! We recommend you to read 'Working with commercial version' section of - ! ALGLIB Reference Manual in order to find out how to use performance- - ! related features provided by commercial edition of ALGLIB. - - INPUT PARAMETERS: - Network - neural network; - XY - training set, see below for information on the - training set format. This function checks correctness - of the dataset (no NANs/INFs, class numbers are - correct) and throws exception when incorrect dataset - is passed. Sparse matrix must use CRS format for - storage. - SetSize - real size of XY, SetSize>=0; - it is used when SubsetSize<0; - Subset - subset of SubsetSize elements, array[SubsetSize]; - SubsetSize- number of elements in Subset[] array: - * if SubsetSize>0, rows of XY with indices Subset[0]... - ...Subset[SubsetSize-1] are processed - * if SubsetSize=0, zeros are returned - * if SubsetSize<0, entire dataset is processed; Subset[] - array is ignored in this case. - - RESULT: - sum-of-squares error, SUM(sqr(y[i]-desired_y[i])/2) - - DATASET FORMAT: - - This function uses two different dataset formats - one for regression - networks, another one for classification networks. - - For regression networks with NIn inputs and NOut outputs following dataset - format is used: - * dataset is given by NPoints*(NIn+NOut) matrix - * each row corresponds to one example - * first NIn columns are inputs, next NOut columns are outputs - - For classification networks with NIn inputs and NClasses clases following - dataset format is used: - * dataset is given by NPoints*(NIn+1) matrix - * each row corresponds to one example - * first NIn columns are inputs, last column stores class number (from 0 to - NClasses-1). + This is a 8-bit version of dfbinarycompression. + Not recommended for external use because it is too lossy. -- ALGLIB -- - Copyright 04.09.2012 by Bochkanov Sergey + Copyright 22.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlperrorsparsesubset(multilayerperceptron network, - sparse.sparsematrix xy, - int setsize, - int[] subset, - int subsetsize, + public static double dfbinarycompression8(decisionforest df, alglib.xparams _params) { double result = 0; - int idx0 = 0; - int idx1 = 0; - int idxtype = 0; - alglib.ap.assert(sparse.sparseiscrs(xy, _params), "MLPErrorSparseSubset: XY is not in CRS format."); - alglib.ap.assert(sparse.sparsegetnrows(xy, _params)>=setsize, "MLPErrorSparseSubset: XY has less than SetSize rows"); - if( setsize>0 ) - { - if( mlpissoftmax(network, _params) ) - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+1, "MLPErrorSparseSubset: XY has less than NIn+1 columns"); - } - else - { - alglib.ap.assert(sparse.sparsegetncols(xy, _params)>=mlpgetinputscount(network, _params)+mlpgetoutputscount(network, _params), "MLPErrorSparseSubset: XY has less than NIn+NOut columns"); - } - } - if( subsetsize>=0 ) - { - idx0 = 0; - idx1 = subsetsize; - idxtype = 1; - } - else - { - idx0 = 0; - idx1 = setsize; - idxtype = 0; - } - mlpallerrorsx(network, network.dummydxy, xy, setsize, 1, subset, idx0, idx1, idxtype, network.buf, network.err, _params); - result = math.sqr(network.err.rmserror)*(idx1-idx0)*mlpgetoutputscount(network, _params)/2; + result = binarycompression(df, true, _params); return result; } /************************************************************************* - Calculation of all types of errors at once for a subset or full dataset, - which can be represented in different formats. + Inference using decision forest - THIS INTERNAL FUNCTION IS NOT INTENDED TO BE USED BY ALGLIB USERS! + IMPORTANT: this function is thread-unsafe and may modify internal + structures of the model! You can not use same model object for + parallel evaluation from several threads. + + Use dftsprocess() with independent thread-local buffers if + you need thread-safe evaluation. + + INPUT PARAMETERS: + DF - decision forest model + X - input vector, array[NVars] + Y - possibly preallocated buffer, reallocated if too small + + OUTPUT PARAMETERS: + Y - result. Regression estimate when solving regression task, + vector of posterior probabilities for classification task. + + See also DFProcessI. + -- ALGLIB -- - Copyright 26.07.2012 by Bochkanov Sergey + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - public static void mlpallerrorsx(multilayerperceptron network, - double[,] densexy, - sparse.sparsematrix sparsexy, - int datasetsize, - int datasettype, - int[] idx, - int subset0, - int subset1, - int subsettype, - alglib.smp.shared_pool buf, - modelerrors rep, + public static void dfprocess(decisionforest df, + double[] x, + ref double[] y, alglib.xparams _params) { - int nin = 0; - int nout = 0; - int wcount = 0; - int rowsize = 0; - bool iscls = new bool(); - int srcidx = 0; - int cstart = 0; - int csize = 0; - int j = 0; - hpccores.mlpbuffers pbuf = null; - int len0 = 0; - int len1 = 0; - modelerrors rep0 = new modelerrors(); - modelerrors rep1 = new modelerrors(); - double problemcost = 0; + int offs = 0; + int i = 0; + double v = 0; + int treesize = 0; + bool processed = new bool(); int i_ = 0; - int i1_ = 0; - alglib.ap.assert(datasetsize>=0, "MLPAllErrorsX: SetSize<0"); - alglib.ap.assert(datasettype==0 || datasettype==1, "MLPAllErrorsX: DatasetType is incorrect"); - alglib.ap.assert(subsettype==0 || subsettype==1, "MLPAllErrorsX: SubsetType is incorrect"); // - // Determine network properties - // - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - iscls = mlpissoftmax(network, _params); - - // - // Split problem. - // - // Splitting problem allows us to reduce effect of single-precision - // arithmetics (SSE-optimized version of MLPChunkedProcess uses single - // precision internally, but converts them to double precision after - // results are exported from HPC buffer to network). Small batches are - // calculated in single precision, results are aggregated in double - // precision, and it allows us to avoid accumulation of errors when - // we process very large batches (tens of thousands of items). - // - // NOTE: it is important to use real arithmetics for ProblemCost - // because ProblemCost may be larger than MAXINT. - // - problemcost = subset1-subset0; - problemcost = problemcost*wcount*2; - if( (double)(problemcost)>=(double)(apserv.smpactivationlevel(_params)) && subset1-subset0>=2*microbatchsize ) - { - if( _trypexec_mlpallerrorsx(network,densexy,sparsexy,datasetsize,datasettype,idx,subset0,subset1,subsettype,buf,rep, _params) ) - { - return; - } - } - if( subset1-subset0>=2*microbatchsize && (double)(problemcost)>(double)(apserv.spawnlevel(_params)) ) - { - apserv.splitlength(subset1-subset0, microbatchsize, ref len0, ref len1, _params); - mlpallerrorsx(network, densexy, sparsexy, datasetsize, datasettype, idx, subset0, subset0+len0, subsettype, buf, rep0, _params); - mlpallerrorsx(network, densexy, sparsexy, datasetsize, datasettype, idx, subset0+len0, subset1, subsettype, buf, rep1, _params); - rep.relclserror = (len0*rep0.relclserror+len1*rep1.relclserror)/(len0+len1); - rep.avgce = (len0*rep0.avgce+len1*rep1.avgce)/(len0+len1); - rep.rmserror = Math.Sqrt((len0*math.sqr(rep0.rmserror)+len1*math.sqr(rep1.rmserror))/(len0+len1)); - rep.avgerror = (len0*rep0.avgerror+len1*rep1.avgerror)/(len0+len1); - rep.avgrelerror = (len0*rep0.avgrelerror+len1*rep1.avgrelerror)/(len0+len1); - return; - } - + // Process // - // Retrieve and prepare + // Although comments above warn you about thread-unsafety of this + // function, it is de facto thread-safe. However, thread safety is + // an accidental side-effect of the specific inference algorithm + // being used. It may disappear in the future versions of the DF + // models, so you should NOT rely on it. // - alglib.smp.ae_shared_pool_retrieve(buf, ref pbuf); - if( iscls ) + if( alglib.ap.len(y)=0, "MLPAllErrorsX: internal error"); - if( datasettype==0 ) - { - for(i_=0; i_<=rowsize-1;i_++) - { - pbuf.xy[j,i_] = densexy[srcidx,i_]; - } - } - if( datasettype==1 ) - { - sparse.sparsegetrow(sparsexy, srcidx, ref pbuf.xyrow, _params); - for(i_=0; i_<=rowsize-1;i_++) - { - pbuf.xy[j,i_] = pbuf.xyrow[i_]; - } - } + dfprocessinternaluncompressed(df, offs, offs+1, x, ref y, _params); + offs = offs+(int)Math.Round(df.trees[offs]); } + processed = true; + } + if( df.forestformat==dfcompressedv0 ) + { // - // Unpack XY and process (temporary code, to be replaced by chunked processing) + // Process trees stored in compressed format // - for(j=0; j<=csize-1; j++) - { - for(i_=0; i_<=rowsize-1;i_++) - { - pbuf.xy2[j,i_] = pbuf.xy[j,i_]; - } - } - mlpchunkedprocess(network, pbuf.xy2, 0, csize, pbuf.batch4buf, pbuf.hpcbuf, _params); - for(j=0; j<=csize-1; j++) + offs = 0; + for(i=0; i<=df.ntrees-1; i++) { - for(i_=0; i_<=nin-1;i_++) - { - pbuf.x[i_] = pbuf.xy2[j,i_]; - } - i1_ = (nin) - (0); - for(i_=0; i_<=nout-1;i_++) - { - pbuf.y[i_] = pbuf.xy2[j,i_+i1_]; - } - if( iscls ) - { - pbuf.desiredy[0] = pbuf.xy[j,nin]; - } - else - { - i1_ = (nin) - (0); - for(i_=0; i_<=nout-1;i_++) - { - pbuf.desiredy[i_] = pbuf.xy[j,i_+i1_]; - } - } - bdss.dserraccumulate(ref pbuf.tmp0, pbuf.y, pbuf.desiredy, _params); + treesize = unstreamuint(df.trees8, ref offs, _params); + dfprocessinternalcompressed(df, offs, x, ref y, _params); + offs = offs+treesize; } - - // - // Process chunk and advance line pointer - // - cstart = cstart+pbuf.chunksize; + processed = true; + } + alglib.ap.assert(processed, "DFProcess: integrity check failed (unexpected format?)"); + v = (double)1/(double)df.ntrees; + for(i_=0; i_<=df.nclasses-1;i_++) + { + y[i_] = v*y[i_]; } - bdss.dserrfinish(ref pbuf.tmp0, _params); - rep.relclserror = pbuf.tmp0[0]; - rep.avgce = pbuf.tmp0[1]/Math.Log(2); - rep.rmserror = pbuf.tmp0[2]; - rep.avgerror = pbuf.tmp0[3]; - rep.avgrelerror = pbuf.tmp0[4]; - - // - // Recycle - // - alglib.smp.ae_shared_pool_recycle(buf, ref pbuf); } /************************************************************************* - Serial stub for GPL edition. + 'interactive' variant of DFProcess for languages like Python which support + constructs like "Y = DFProcessI(DF,X)" and interactive mode of interpreter + + This function allocates new array on each call, so it is significantly + slower than its 'non-interactive' counterpart, but it is more convenient + when you call it from command line. + + IMPORTANT: this function is thread-unsafe and may modify internal + structures of the model! You can not use same model object for + parallel evaluation from several threads. + + Use dftsprocess() with independent thread-local buffers if + you need thread-safe evaluation. + + -- ALGLIB -- + Copyright 28.02.2010 by Bochkanov Sergey *************************************************************************/ - public static bool _trypexec_mlpallerrorsx(multilayerperceptron network, - double[,] densexy, - sparse.sparsematrix sparsexy, - int datasetsize, - int datasettype, - int[] idx, - int subset0, - int subset1, - int subsettype, - alglib.smp.shared_pool buf, - modelerrors rep, alglib.xparams _params) + public static void dfprocessi(decisionforest df, + double[] x, + ref double[] y, + alglib.xparams _params) { - return false; + y = new double[0]; + + dfprocess(df, x, ref y, _params); } /************************************************************************* - Internal subroutine: adding new input layer to network + This function returns first component of the inferred vector (i.e. one + with index #0). + + It is a convenience wrapper for dfprocess() intended for either: + * 1-dimensional regression problems + * 2-class classification problems + + In the former case this function returns inference result as scalar, which + is definitely more convenient that wrapping it as vector. In the latter + case it returns probability of object belonging to class #0. + + If you call it for anything different from two cases above, it will work + as defined, i.e. return y[0], although it is of less use in such cases. + + IMPORTANT: this function is thread-unsafe and modifies internal structures + of the model! You can not use same model object for parallel + evaluation from several threads. + + Use dftsprocess() with independent thread-local buffers, if + you need thread-safe evaluation. + + INPUT PARAMETERS: + Model - DF model + X - input vector, array[0..NVars-1]. + + RESULT: + Y[0] + + -- ALGLIB -- + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - private static void addinputlayer(int ncount, - ref int[] lsizes, - ref int[] ltypes, - ref int[] lconnfirst, - ref int[] lconnlast, - ref int lastproc, + public static double dfprocess0(decisionforest model, + double[] x, alglib.xparams _params) { - lsizes[0] = ncount; - ltypes[0] = -2; - lconnfirst[0] = 0; - lconnlast[0] = 0; - lastproc = 0; + double result = 0; + int i = 0; + int nvars = 0; + + nvars = model.nvars; + for(i=0; i<=nvars-1; i++) + { + model.buffer.x[i] = x[i]; + } + dfprocess(model, model.buffer.x, ref model.buffer.y, _params); + result = model.buffer.y[0]; + return result; } - /************************************************************************* - Internal subroutine: adding new summator layer to network + /************************************************************************* + This function returns most probable class number for an input X. It is + same as calling dfprocess(model,x,y), then determining i=argmax(y[i]) and + returning i. + + A class number in [0,NOut) range in returned for classification problems, + -1 is returned when this function is called for regression problems. + + IMPORTANT: this function is thread-unsafe and modifies internal structures + of the model! You can not use same model object for parallel + evaluation from several threads. + + Use dftsprocess() with independent thread-local buffers, if + you need thread-safe evaluation. + + INPUT PARAMETERS: + Model - decision forest model + X - input vector, array[0..NVars-1]. + + RESULT: + class number, -1 for regression tasks + + -- ALGLIB -- + Copyright 15.02.2019 by Bochkanov Sergey *************************************************************************/ - private static void addbiasedsummatorlayer(int ncount, - ref int[] lsizes, - ref int[] ltypes, - ref int[] lconnfirst, - ref int[] lconnlast, - ref int lastproc, + public static int dfclassify(decisionforest model, + double[] x, alglib.xparams _params) { - lsizes[lastproc+1] = 1; - ltypes[lastproc+1] = -3; - lconnfirst[lastproc+1] = 0; - lconnlast[lastproc+1] = 0; - lsizes[lastproc+2] = ncount; - ltypes[lastproc+2] = 0; - lconnfirst[lastproc+2] = lastproc; - lconnlast[lastproc+2] = lastproc+1; - lastproc = lastproc+2; + int result = 0; + int i = 0; + int nvars = 0; + int nout = 0; + + if( model.nclasses<2 ) + { + result = -1; + return result; + } + nvars = model.nvars; + nout = model.nclasses; + for(i=0; i<=nvars-1; i++) + { + model.buffer.x[i] = x[i]; + } + dfprocess(model, model.buffer.x, ref model.buffer.y, _params); + result = 0; + for(i=1; i<=nout-1; i++) + { + if( model.buffer.y[i]>model.buffer.y[result] ) + { + result = i; + } + } + return result; } /************************************************************************* - Internal subroutine: adding new summator layer to network + Inference using decision forest + + Thread-safe procesing using external buffer for temporaries. + + This function is thread-safe (i.e . you can use same DF model from + multiple threads) as long as you use different buffer objects for different + threads. + + INPUT PARAMETERS: + DF - decision forest model + Buf - buffer object, must be allocated specifically for this + model with dfcreatebuffer(). + X - input vector, array[NVars] + Y - possibly preallocated buffer, reallocated if too small + + OUTPUT PARAMETERS: + Y - result. Regression estimate when solving regression task, + vector of posterior probabilities for classification task. + + See also DFProcessI. + + + -- ALGLIB -- + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - private static void addactivationlayer(int functype, - ref int[] lsizes, - ref int[] ltypes, - ref int[] lconnfirst, - ref int[] lconnlast, - ref int lastproc, + public static void dftsprocess(decisionforest df, + decisionforestbuffer buf, + double[] x, + ref double[] y, alglib.xparams _params) { - alglib.ap.assert(functype>0 || functype==-5, "AddActivationLayer: incorrect function type"); - lsizes[lastproc+1] = lsizes[lastproc]; - ltypes[lastproc+1] = functype; - lconnfirst[lastproc+1] = lastproc; - lconnlast[lastproc+1] = lastproc; - lastproc = lastproc+1; + + // + // Although docs warn you about thread-unsafety of the dfprocess() + // function, it is de facto thread-safe. However, thread safety is + // an accidental side-effect of the specific inference algorithm + // being used. It may disappear in the future versions of the DF + // models, so you should NOT rely on it. + // + dfprocess(df, x, ref y, _params); } /************************************************************************* - Internal subroutine: adding new zero layer to network + Relative classification error on the test set + + INPUT PARAMETERS: + DF - decision forest model + XY - test set + NPoints - test set size + + RESULT: + percent of incorrectly classified cases. + Zero if model solves regression task. + + -- ALGLIB -- + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - private static void addzerolayer(ref int[] lsizes, - ref int[] ltypes, - ref int[] lconnfirst, - ref int[] lconnlast, - ref int lastproc, + public static double dfrelclserror(decisionforest df, + double[,] xy, + int npoints, alglib.xparams _params) { - lsizes[lastproc+1] = 1; - ltypes[lastproc+1] = -4; - lconnfirst[lastproc+1] = 0; - lconnlast[lastproc+1] = 0; - lastproc = lastproc+1; + double result = 0; + + result = (double)dfclserror(df, xy, npoints, _params)/(double)npoints; + return result; } /************************************************************************* - This routine adds input layer to the high-level description of the network. + Average cross-entropy (in bits per element) on the test set - It modifies Network.HLConnections and Network.HLNeurons and assumes that - these arrays have enough place to store data. It accepts following - parameters: - Network - network - ConnIdx - index of the first free entry in the HLConnections - NeuroIdx - index of the first free entry in the HLNeurons - StructInfoIdx- index of the first entry in the low level description - of the current layer (in the StructInfo array) - NIn - number of inputs - - It modified Network and indices. + INPUT PARAMETERS: + DF - decision forest model + XY - test set + NPoints - test set size + + RESULT: + CrossEntropy/(NPoints*LN(2)). + Zero if model solves regression task. + + -- ALGLIB -- + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - private static void hladdinputlayer(multilayerperceptron network, - ref int connidx, - ref int neuroidx, - ref int structinfoidx, - int nin, + public static double dfavgce(decisionforest df, + double[,] xy, + int npoints, alglib.xparams _params) { + double result = 0; + double[] x = new double[0]; + double[] y = new double[0]; int i = 0; - int offs = 0; + int j = 0; + int k = 0; + int tmpi = 0; + int i_ = 0; - offs = hlnfieldwidth*neuroidx; - for(i=0; i<=nin-1; i++) + x = new double[df.nvars-1+1]; + y = new double[df.nclasses-1+1]; + result = 0; + for(i=0; i<=npoints-1; i++) { - network.hlneurons[offs+0] = 0; - network.hlneurons[offs+1] = i; - network.hlneurons[offs+2] = -1; - network.hlneurons[offs+3] = -1; - offs = offs+hlnfieldwidth; + for(i_=0; i_<=df.nvars-1;i_++) + { + x[i_] = xy[i,i_]; + } + dfprocess(df, x, ref y, _params); + if( df.nclasses>1 ) + { + + // + // classification-specific code + // + k = (int)Math.Round(xy[i,df.nvars]); + tmpi = 0; + for(j=1; j<=df.nclasses-1; j++) + { + if( (double)(y[j])>(double)(y[tmpi]) ) + { + tmpi = j; + } + } + if( (double)(y[k])!=(double)(0) ) + { + result = result-Math.Log(y[k]); + } + else + { + result = result-Math.Log(math.minrealnumber); + } + } } - neuroidx = neuroidx+nin; - structinfoidx = structinfoidx+nin; + result = result/npoints; + return result; } /************************************************************************* - This routine adds output layer to the high-level description of - the network. + RMS error on the test set - It modifies Network.HLConnections and Network.HLNeurons and assumes that - these arrays have enough place to store data. It accepts following - parameters: - Network - network - ConnIdx - index of the first free entry in the HLConnections - NeuroIdx - index of the first free entry in the HLNeurons - StructInfoIdx- index of the first entry in the low level description - of the current layer (in the StructInfo array) - WeightsIdx - index of the first entry in the Weights array which - corresponds to the current layer - K - current layer index - NPrev - number of neurons in the previous layer - NOut - number of outputs - IsCls - is it classifier network? - IsLinear - is it network with linear output? + INPUT PARAMETERS: + DF - decision forest model + XY - test set + NPoints - test set size - It modified Network and ConnIdx/NeuroIdx/StructInfoIdx/WeightsIdx. + RESULT: + root mean square error. + Its meaning for regression task is obvious. As for + classification task, RMS error means error when estimating posterior + probabilities. + + -- ALGLIB -- + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - private static void hladdoutputlayer(multilayerperceptron network, - ref int connidx, - ref int neuroidx, - ref int structinfoidx, - ref int weightsidx, - int k, - int nprev, - int nout, - bool iscls, - bool islinearout, + public static double dfrmserror(decisionforest df, + double[,] xy, + int npoints, alglib.xparams _params) { + double result = 0; + double[] x = new double[0]; + double[] y = new double[0]; int i = 0; int j = 0; - int neurooffs = 0; - int connoffs = 0; + int k = 0; + int tmpi = 0; + int i_ = 0; - alglib.ap.assert((iscls && islinearout) || !iscls, "HLAddOutputLayer: internal error"); - neurooffs = hlnfieldwidth*neuroidx; - connoffs = hlconnfieldwidth*connidx; - if( !iscls ) + x = new double[df.nvars-1+1]; + y = new double[df.nclasses-1+1]; + result = 0; + for(i=0; i<=npoints-1; i++) { - - // - // Regression network - // - for(i=0; i<=nout-1; i++) + for(i_=0; i_<=df.nvars-1;i_++) { - network.hlneurons[neurooffs+0] = k; - network.hlneurons[neurooffs+1] = i; - network.hlneurons[neurooffs+2] = structinfoidx+1+nout+i; - network.hlneurons[neurooffs+3] = weightsidx+nprev+(nprev+1)*i; - neurooffs = neurooffs+hlnfieldwidth; + x[i_] = xy[i,i_]; } - for(i=0; i<=nprev-1; i++) + dfprocess(df, x, ref y, _params); + if( df.nclasses>1 ) { - for(j=0; j<=nout-1; j++) + + // + // classification-specific code + // + k = (int)Math.Round(xy[i,df.nvars]); + tmpi = 0; + for(j=1; j<=df.nclasses-1; j++) { - network.hlconnections[connoffs+0] = k-1; - network.hlconnections[connoffs+1] = i; - network.hlconnections[connoffs+2] = k; - network.hlconnections[connoffs+3] = j; - network.hlconnections[connoffs+4] = weightsidx+i+j*(nprev+1); - connoffs = connoffs+hlconnfieldwidth; + if( (double)(y[j])>(double)(y[tmpi]) ) + { + tmpi = j; + } + } + for(j=0; j<=df.nclasses-1; j++) + { + if( j==k ) + { + result = result+math.sqr(y[j]-1); + } + else + { + result = result+math.sqr(y[j]); + } } } - connidx = connidx+nprev*nout; - neuroidx = neuroidx+nout; - structinfoidx = structinfoidx+2*nout+1; - weightsidx = weightsidx+nout*(nprev+1); + else + { + + // + // regression-specific code + // + result = result+math.sqr(y[0]-xy[i,df.nvars]); + } } - else + result = Math.Sqrt(result/(npoints*df.nclasses)); + return result; + } + + + /************************************************************************* + Average error on the test set + + INPUT PARAMETERS: + DF - decision forest model + XY - test set + NPoints - test set size + + RESULT: + Its meaning for regression task is obvious. As for + classification task, it means average error when estimating posterior + probabilities. + + -- ALGLIB -- + Copyright 16.02.2009 by Bochkanov Sergey + *************************************************************************/ + public static double dfavgerror(decisionforest df, + double[,] xy, + int npoints, + alglib.xparams _params) + { + double result = 0; + double[] x = new double[0]; + double[] y = new double[0]; + int i = 0; + int j = 0; + int k = 0; + int i_ = 0; + + x = new double[df.nvars-1+1]; + y = new double[df.nclasses-1+1]; + result = 0; + for(i=0; i<=npoints-1; i++) { - - // - // Classification network - // - for(i=0; i<=nout-2; i++) + for(i_=0; i_<=df.nvars-1;i_++) { - network.hlneurons[neurooffs+0] = k; - network.hlneurons[neurooffs+1] = i; - network.hlneurons[neurooffs+2] = -1; - network.hlneurons[neurooffs+3] = weightsidx+nprev+(nprev+1)*i; - neurooffs = neurooffs+hlnfieldwidth; + x[i_] = xy[i,i_]; } - network.hlneurons[neurooffs+0] = k; - network.hlneurons[neurooffs+1] = i; - network.hlneurons[neurooffs+2] = -1; - network.hlneurons[neurooffs+3] = -1; - for(i=0; i<=nprev-1; i++) + dfprocess(df, x, ref y, _params); + if( df.nclasses>1 ) { - for(j=0; j<=nout-2; j++) + + // + // classification-specific code + // + k = (int)Math.Round(xy[i,df.nvars]); + for(j=0; j<=df.nclasses-1; j++) { - network.hlconnections[connoffs+0] = k-1; - network.hlconnections[connoffs+1] = i; - network.hlconnections[connoffs+2] = k; - network.hlconnections[connoffs+3] = j; - network.hlconnections[connoffs+4] = weightsidx+i+j*(nprev+1); - connoffs = connoffs+hlconnfieldwidth; + if( j==k ) + { + result = result+Math.Abs(y[j]-1); + } + else + { + result = result+Math.Abs(y[j]); + } } } - connidx = connidx+nprev*(nout-1); - neuroidx = neuroidx+nout; - structinfoidx = structinfoidx+nout+2; - weightsidx = weightsidx+(nout-1)*(nprev+1); + else + { + + // + // regression-specific code + // + result = result+Math.Abs(y[0]-xy[i,df.nvars]); + } } + result = result/(npoints*df.nclasses); + return result; } /************************************************************************* - This routine adds hidden layer to the high-level description of - the network. + Average relative error on the test set - It modifies Network.HLConnections and Network.HLNeurons and assumes that - these arrays have enough place to store data. It accepts following - parameters: - Network - network - ConnIdx - index of the first free entry in the HLConnections - NeuroIdx - index of the first free entry in the HLNeurons - StructInfoIdx- index of the first entry in the low level description - of the current layer (in the StructInfo array) - WeightsIdx - index of the first entry in the Weights array which - corresponds to the current layer - K - current layer index - NPrev - number of neurons in the previous layer - NCur - number of neurons in the current layer + INPUT PARAMETERS: + DF - decision forest model + XY - test set + NPoints - test set size - It modified Network and ConnIdx/NeuroIdx/StructInfoIdx/WeightsIdx. + RESULT: + Its meaning for regression task is obvious. As for + classification task, it means average relative error when estimating + posterior probability of belonging to the correct class. + + -- ALGLIB -- + Copyright 16.02.2009 by Bochkanov Sergey *************************************************************************/ - private static void hladdhiddenlayer(multilayerperceptron network, - ref int connidx, - ref int neuroidx, - ref int structinfoidx, - ref int weightsidx, - int k, - int nprev, - int ncur, + public static double dfavgrelerror(decisionforest df, + double[,] xy, + int npoints, alglib.xparams _params) { + double result = 0; + double[] x = new double[0]; + double[] y = new double[0]; + int relcnt = 0; int i = 0; int j = 0; - int neurooffs = 0; - int connoffs = 0; + int k = 0; + int i_ = 0; - neurooffs = hlnfieldwidth*neuroidx; - connoffs = hlconnfieldwidth*connidx; - for(i=0; i<=ncur-1; i++) + x = new double[df.nvars-1+1]; + y = new double[df.nclasses-1+1]; + result = 0; + relcnt = 0; + for(i=0; i<=npoints-1; i++) { - network.hlneurons[neurooffs+0] = k; - network.hlneurons[neurooffs+1] = i; - network.hlneurons[neurooffs+2] = structinfoidx+1+ncur+i; - network.hlneurons[neurooffs+3] = weightsidx+nprev+(nprev+1)*i; - neurooffs = neurooffs+hlnfieldwidth; + for(i_=0; i_<=df.nvars-1;i_++) + { + x[i_] = xy[i,i_]; + } + dfprocess(df, x, ref y, _params); + if( df.nclasses>1 ) + { + + // + // classification-specific code + // + k = (int)Math.Round(xy[i,df.nvars]); + for(j=0; j<=df.nclasses-1; j++) + { + if( j==k ) + { + result = result+Math.Abs(y[j]-1); + relcnt = relcnt+1; + } + } + } + else + { + + // + // regression-specific code + // + if( (double)(xy[i,df.nvars])!=(double)(0) ) + { + result = result+Math.Abs((y[0]-xy[i,df.nvars])/xy[i,df.nvars]); + relcnt = relcnt+1; + } + } } - for(i=0; i<=nprev-1; i++) + if( relcnt>0 ) { - for(j=0; j<=ncur-1; j++) - { - network.hlconnections[connoffs+0] = k-1; - network.hlconnections[connoffs+1] = i; - network.hlconnections[connoffs+2] = k; - network.hlconnections[connoffs+3] = j; - network.hlconnections[connoffs+4] = weightsidx+i+j*(nprev+1); - connoffs = connoffs+hlconnfieldwidth; - } + result = result/relcnt; } - connidx = connidx+nprev*ncur; - neuroidx = neuroidx+ncur; - structinfoidx = structinfoidx+2*ncur+1; - weightsidx = weightsidx+ncur*(nprev+1); + return result; } /************************************************************************* - This function fills high level information about network created using - internal MLPCreate() function. - - This function does NOT examine StructInfo for low level information, it - just expects that network has following structure: + Copying of DecisionForest strucure - input neuron \ - ... | input layer - input neuron / - - "-1" neuron \ - biased summator | - ... | - biased summator | hidden layer(s), if there are exists any - activation function | - ... | - activation function / - - "-1" neuron \ - biased summator | output layer: - ... | - biased summator | * we have NOut summators/activators for regression networks - activation function | * we have only NOut-1 summators and no activators for classifiers - ... | * we have "0" neuron only when we have classifier - activation function | - "0" neuron / + INPUT PARAMETERS: + DF1 - original + OUTPUT PARAMETERS: + DF2 - copy -- ALGLIB -- - Copyright 30.03.2008 by Bochkanov Sergey + Copyright 13.02.2009 by Bochkanov Sergey *************************************************************************/ - private static void fillhighlevelinformation(multilayerperceptron network, - int nin, - int nhid1, - int nhid2, - int nout, - bool iscls, - bool islinearout, + public static void dfcopy(decisionforest df1, + decisionforest df2, alglib.xparams _params) { - int idxweights = 0; - int idxstruct = 0; - int idxneuro = 0; - int idxconn = 0; + int i = 0; + int bufsize = 0; + int i_ = 0; - alglib.ap.assert((iscls && islinearout) || !iscls, "FillHighLevelInformation: internal error"); - - // - // Preparations common to all types of networks - // - idxweights = 0; - idxneuro = 0; - idxstruct = 0; - idxconn = 0; - network.hlnetworktype = 0; - - // - // network without hidden layers - // - if( nhid1==0 ) + if( df1.forestformat==dfuncompressedv0 ) { - network.hllayersizes = new int[2]; - network.hllayersizes[0] = nin; - network.hllayersizes[1] = nout; - if( !iscls ) - { - network.hlconnections = new int[hlconnfieldwidth*nin*nout]; - network.hlneurons = new int[hlnfieldwidth*(nin+nout)]; - network.hlnormtype = 0; - } - else + df2.forestformat = df1.forestformat; + df2.nvars = df1.nvars; + df2.nclasses = df1.nclasses; + df2.ntrees = df1.ntrees; + df2.bufsize = df1.bufsize; + df2.trees = new double[df1.bufsize]; + for(i_=0; i_<=df1.bufsize-1;i_++) { - network.hlconnections = new int[hlconnfieldwidth*nin*(nout-1)]; - network.hlneurons = new int[hlnfieldwidth*(nin+nout)]; - network.hlnormtype = 1; + df2.trees[i_] = df1.trees[i_]; } - hladdinputlayer(network, ref idxconn, ref idxneuro, ref idxstruct, nin, _params); - hladdoutputlayer(network, ref idxconn, ref idxneuro, ref idxstruct, ref idxweights, 1, nin, nout, iscls, islinearout, _params); + dfcreatebuffer(df2, df2.buffer, _params); return; } - - // - // network with one hidden layers - // - if( nhid2==0 ) + if( df1.forestformat==dfcompressedv0 ) { - network.hllayersizes = new int[3]; - network.hllayersizes[0] = nin; - network.hllayersizes[1] = nhid1; - network.hllayersizes[2] = nout; - if( !iscls ) - { - network.hlconnections = new int[hlconnfieldwidth*(nin*nhid1+nhid1*nout)]; - network.hlneurons = new int[hlnfieldwidth*(nin+nhid1+nout)]; - network.hlnormtype = 0; - } - else + df2.forestformat = df1.forestformat; + df2.usemantissa8 = df1.usemantissa8; + df2.nvars = df1.nvars; + df2.nclasses = df1.nclasses; + df2.ntrees = df1.ntrees; + bufsize = alglib.ap.len(df1.trees8); + df2.trees8 = new byte[bufsize]; + for(i=0; i<=bufsize-1; i++) { - network.hlconnections = new int[hlconnfieldwidth*(nin*nhid1+nhid1*(nout-1))]; - network.hlneurons = new int[hlnfieldwidth*(nin+nhid1+nout)]; - network.hlnormtype = 1; + df2.trees8[i] = unchecked((byte)(df1.trees8[i])); } - hladdinputlayer(network, ref idxconn, ref idxneuro, ref idxstruct, nin, _params); - hladdhiddenlayer(network, ref idxconn, ref idxneuro, ref idxstruct, ref idxweights, 1, nin, nhid1, _params); - hladdoutputlayer(network, ref idxconn, ref idxneuro, ref idxstruct, ref idxweights, 2, nhid1, nout, iscls, islinearout, _params); + dfcreatebuffer(df2, df2.buffer, _params); return; } - - // - // Two hidden layers - // - network.hllayersizes = new int[4]; - network.hllayersizes[0] = nin; - network.hllayersizes[1] = nhid1; - network.hllayersizes[2] = nhid2; - network.hllayersizes[3] = nout; - if( !iscls ) + alglib.ap.assert(false, "DFCopy: unexpected forest format"); + } + + + /************************************************************************* + Serializer: allocation + + -- ALGLIB -- + Copyright 14.03.2011 by Bochkanov Sergey + *************************************************************************/ + public static void dfalloc(alglib.serializer s, + decisionforest forest, + alglib.xparams _params) + { + if( forest.forestformat==dfuncompressedv0 ) { - network.hlconnections = new int[hlconnfieldwidth*(nin*nhid1+nhid1*nhid2+nhid2*nout)]; - network.hlneurons = new int[hlnfieldwidth*(nin+nhid1+nhid2+nout)]; - network.hlnormtype = 0; + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + apserv.allocrealarray(s, forest.trees, forest.bufsize, _params); + return; } - else + if( forest.forestformat==dfcompressedv0 ) { - network.hlconnections = new int[hlconnfieldwidth*(nin*nhid1+nhid1*nhid2+nhid2*(nout-1))]; - network.hlneurons = new int[hlnfieldwidth*(nin+nhid1+nhid2+nout)]; - network.hlnormtype = 1; + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_entry(); + s.alloc_byte_array(forest.trees8); + return; } - hladdinputlayer(network, ref idxconn, ref idxneuro, ref idxstruct, nin, _params); - hladdhiddenlayer(network, ref idxconn, ref idxneuro, ref idxstruct, ref idxweights, 1, nin, nhid1, _params); - hladdhiddenlayer(network, ref idxconn, ref idxneuro, ref idxstruct, ref idxweights, 2, nhid1, nhid2, _params); - hladdoutputlayer(network, ref idxconn, ref idxneuro, ref idxstruct, ref idxweights, 3, nhid2, nout, iscls, islinearout, _params); + alglib.ap.assert(false, "DFAlloc: unexpected forest format"); } /************************************************************************* - Internal subroutine. + Serializer: serialization -- ALGLIB -- - Copyright 04.11.2007 by Bochkanov Sergey + Copyright 14.03.2011 by Bochkanov Sergey *************************************************************************/ - private static void mlpcreate(int nin, - int nout, - int[] lsizes, - int[] ltypes, - int[] lconnfirst, - int[] lconnlast, - int layerscount, - bool isclsnet, - multilayerperceptron network, + public static void dfserialize(alglib.serializer s, + decisionforest forest, alglib.xparams _params) { - int i = 0; - int j = 0; - int ssize = 0; - int ntotal = 0; - int wcount = 0; - int offs = 0; - int nprocessed = 0; - int wallocated = 0; - int[] localtemp = new int[0]; - int[] lnfirst = new int[0]; - int[] lnsyn = new int[0]; - hpccores.mlpbuffers buf = new hpccores.mlpbuffers(); - smlpgrad sgrad = new smlpgrad(); + if( forest.forestformat==dfuncompressedv0 ) + { + s.serialize_int(scodes.getrdfserializationcode(_params)); + s.serialize_int(dfuncompressedv0); + s.serialize_int(forest.nvars); + s.serialize_int(forest.nclasses); + s.serialize_int(forest.ntrees); + s.serialize_int(forest.bufsize); + apserv.serializerealarray(s, forest.trees, forest.bufsize, _params); + return; + } + if( forest.forestformat==dfcompressedv0 ) + { + s.serialize_int(scodes.getrdfserializationcode(_params)); + s.serialize_int(forest.forestformat); + s.serialize_bool(forest.usemantissa8); + s.serialize_int(forest.nvars); + s.serialize_int(forest.nclasses); + s.serialize_int(forest.ntrees); + s.serialize_byte_array(forest.trees8); + return; + } + alglib.ap.assert(false, "DFSerialize: unexpected forest format"); + } + + + /************************************************************************* + Serializer: unserialization + + -- ALGLIB -- + Copyright 14.03.2011 by Bochkanov Sergey + *************************************************************************/ + public static void dfunserialize(alglib.serializer s, + decisionforest forest, + alglib.xparams _params) + { + int i0 = 0; + int forestformat = 0; + bool processed = new bool(); // - // Check + // check correctness of header // - alglib.ap.assert(layerscount>0, "MLPCreate: wrong parameters!"); - alglib.ap.assert(ltypes[0]==-2, "MLPCreate: wrong LTypes[0] (must be -2)!"); - for(i=0; i<=layerscount-1; i++) - { - alglib.ap.assert(lsizes[i]>0, "MLPCreate: wrong LSizes!"); - alglib.ap.assert(lconnfirst[i]>=0 && (lconnfirst[i]=lconnfirst[i] && (lconnlast[i]=0 || ltypes[i]==-5 ) - { - lnsyn[i] = 0; - for(j=lconnfirst[i]; j<=lconnlast[i]; j++) - { - lnsyn[i] = lnsyn[i]+lsizes[j]; - } - } - else - { - if( (ltypes[i]==-2 || ltypes[i]==-3) || ltypes[i]==-4 ) - { - lnsyn[i] = 0; - } - } - alglib.ap.assert(lnsyn[i]>=0, "MLPCreate: internal error #0!"); + forest.forestformat = forestformat; + forest.nvars = s.unserialize_int(); + forest.nclasses = s.unserialize_int(); + forest.ntrees = s.unserialize_int(); + forest.bufsize = s.unserialize_int(); + apserv.unserializerealarray(s, ref forest.trees, _params); + processed = true; + } + if( forestformat==dfcompressedv0 ) + { // - // Other info + // Unserialize data // - lnfirst[i] = ntotal; - ntotal = ntotal+lsizes[i]; - if( ltypes[i]==0 ) - { - wcount = wcount+lnsyn[i]*lsizes[i]; - } - } - ssize = 7+ntotal*nfieldwidth; - - // - // Allocate - // - network.structinfo = new int[ssize-1+1]; - network.weights = new double[wcount-1+1]; - if( isclsnet ) - { - network.columnmeans = new double[nin-1+1]; - network.columnsigmas = new double[nin-1+1]; - } - else - { - network.columnmeans = new double[nin+nout-1+1]; - network.columnsigmas = new double[nin+nout-1+1]; - } - network.neurons = new double[ntotal-1+1]; - network.nwbuf = new double[Math.Max(wcount, 2*nout)-1+1]; - network.integerbuf = new int[3+1]; - network.dfdnet = new double[ntotal-1+1]; - network.x = new double[nin-1+1]; - network.y = new double[nout-1+1]; - network.derror = new double[ntotal-1+1]; - - // - // Fill structure: - // * first, fill by dummy values to avoid spurious reports by Valgrind - // * then fill global info header - // - for(i=0; i<=ssize-1; i++) - { - network.structinfo[i] = -999999; - } - network.structinfo[0] = ssize; - network.structinfo[1] = nin; - network.structinfo[2] = nout; - network.structinfo[3] = ntotal; - network.structinfo[4] = wcount; - network.structinfo[5] = 7; - if( isclsnet ) - { - network.structinfo[6] = 1; - } - else - { - network.structinfo[6] = 0; - } - - // - // Fill structure: neuron connections - // - nprocessed = 0; - wallocated = 0; - for(i=0; i<=layerscount-1; i++) - { - for(j=0; j<=lsizes[i]-1; j++) - { - offs = network.structinfo[5]+nprocessed*nfieldwidth; - network.structinfo[offs+0] = ltypes[i]; - if( ltypes[i]==0 ) - { - - // - // Adaptive summator: - // * connections with weights to previous neurons - // - network.structinfo[offs+1] = lnsyn[i]; - network.structinfo[offs+2] = lnfirst[lconnfirst[i]]; - network.structinfo[offs+3] = wallocated; - wallocated = wallocated+lnsyn[i]; - nprocessed = nprocessed+1; - } - if( ltypes[i]>0 || ltypes[i]==-5 ) - { - - // - // Activation layer: - // * each neuron connected to one (only one) of previous neurons. - // * no weights - // - network.structinfo[offs+1] = 1; - network.structinfo[offs+2] = lnfirst[lconnfirst[i]]+j; - network.structinfo[offs+3] = -1; - nprocessed = nprocessed+1; - } - if( (ltypes[i]==-2 || ltypes[i]==-3) || ltypes[i]==-4 ) - { - nprocessed = nprocessed+1; - } - } - } - alglib.ap.assert(wallocated==wcount, "MLPCreate: internal error #1!"); - alglib.ap.assert(nprocessed==ntotal, "MLPCreate: internal error #2!"); - - // - // Fill weights by small random values - // Initialize means and sigmas - // - for(i=0; i<=nin-1; i++) - { - network.columnmeans[i] = 0; - network.columnsigmas[i] = 1; - } - if( !isclsnet ) - { - for(i=0; i<=nout-1; i++) - { - network.columnmeans[nin+i] = 0; - network.columnsigmas[nin+i] = 1; - } + forest.forestformat = forestformat; + forest.usemantissa8 = s.unserialize_bool(); + forest.nvars = s.unserialize_int(); + forest.nclasses = s.unserialize_int(); + forest.ntrees = s.unserialize_int(); + forest.trees8 = s.unserialize_byte_array(); + processed = true; } - mlprandomize(network, _params); + alglib.ap.assert(processed, "DFUnserialize: unexpected forest format"); // - // Seed buffers + // Prepare buffer // - alglib.smp.ae_shared_pool_set_seed(network.buf, buf); - sgrad.g = new double[wcount]; - sgrad.f = 0.0; - for(i=0; i<=wcount-1; i++) - { - sgrad.g[i] = 0.0; - } - alglib.smp.ae_shared_pool_set_seed(network.gradbuf, sgrad); + dfcreatebuffer(forest, forest.buffer, _params); } /************************************************************************* - This function returns information about Ith neuron of Kth layer. + This subroutine builds random decision forest. + + --------- DEPRECATED VERSION! USE DECISION FOREST BUILDER OBJECT --------- -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 19.02.2009 by Bochkanov Sergey *************************************************************************/ - private static void mlpgetneuroninfox(multilayerperceptron network, - int k, - int i, - ref int[] integerbuf, - ref int fkind, - ref double threshold, + public static void dfbuildrandomdecisionforest(double[,] xy, + int npoints, + int nvars, + int nclasses, + int ntrees, + double r, + ref int info, + decisionforest df, + dfreport rep, alglib.xparams _params) { - int ncnt = 0; - int istart = 0; - int highlevelidx = 0; - int activationoffset = 0; - - fkind = 0; - threshold = 0; + int samplesize = 0; - ablasf.iallocv(2, ref integerbuf, _params); - ncnt = alglib.ap.len(network.hlneurons)/hlnfieldwidth; - istart = network.structinfo[5]; - - // - // search - // - integerbuf[0] = k; - integerbuf[1] = i; - highlevelidx = apserv.recsearch(network.hlneurons, hlnfieldwidth, 2, 0, ncnt, integerbuf, _params); - alglib.ap.assert(highlevelidx>=0, "MLPGetNeuronInfo: incorrect (nonexistent) layer or neuron index"); - - // - // 1. find offset of the activation function record in the - // - if( network.hlneurons[highlevelidx*hlnfieldwidth+2]>=0 ) - { - activationoffset = istart+network.hlneurons[highlevelidx*hlnfieldwidth+2]*nfieldwidth; - fkind = network.structinfo[activationoffset+0]; - } - else - { - fkind = 0; - } - if( network.hlneurons[highlevelidx*hlnfieldwidth+3]>=0 ) - { - threshold = network.weights[network.hlneurons[highlevelidx*hlnfieldwidth+3]]; - } - else + info = 0; + + if( (double)(r)<=(double)(0) || (double)(r)>(double)(1) ) { - threshold = 0; + info = -1; + return; } + samplesize = Math.Max((int)Math.Round(r*npoints), 1); + dfbuildinternal(xy, npoints, nvars, nclasses, ntrees, samplesize, Math.Max(nvars/2, 1), dfusestrongsplits+dfuseevs, ref info, df, rep, _params); } /************************************************************************* - This function returns information about connection from I0-th neuron of - K0-th layer to I1-th neuron of K1-th layer. - - INPUT PARAMETERS: - Network - network - K0 - layer index - I0 - neuron index (within layer) - K1 - layer index - I1 - neuron index (within layer) - - RESULT: - connection weight (zero for non-existent connections) + This subroutine builds random decision forest. - This function: - 1. throws exception if layer or neuron with given index do not exists. - 2. returns zero if neurons exist, but there is no connection between them + --------- DEPRECATED VERSION! USE DECISION FOREST BUILDER OBJECT --------- -- ALGLIB -- - Copyright 25.03.2011 by Bochkanov Sergey + Copyright 19.02.2009 by Bochkanov Sergey *************************************************************************/ - private static double mlpgetweightx(multilayerperceptron network, - int k0, - int i0, - int k1, - int i1, - ref int[] integerbuf, + public static void dfbuildrandomdecisionforestx1(double[,] xy, + int npoints, + int nvars, + int nclasses, + int ntrees, + int nrndvars, + double r, + ref int info, + decisionforest df, + dfreport rep, alglib.xparams _params) { - double result = 0; - int ccnt = 0; - int highlevelidx = 0; + int samplesize = 0; + + info = 0; + + if( (double)(r)<=(double)(0) || (double)(r)>(double)(1) ) + { + info = -1; + return; + } + if( nrndvars<=0 || nrndvars>nvars ) + { + info = -1; + return; + } + samplesize = Math.Max((int)Math.Round(r*npoints), 1); + dfbuildinternal(xy, npoints, nvars, nclasses, ntrees, samplesize, nrndvars, dfusestrongsplits+dfuseevs, ref info, df, rep, _params); + } + + + public static void dfbuildinternal(double[,] xy, + int npoints, + int nvars, + int nclasses, + int ntrees, + int samplesize, + int nfeatures, + int flags, + ref int info, + decisionforest df, + dfreport rep, + alglib.xparams _params) + { + decisionforestbuilder builder = new decisionforestbuilder(); + int i = 0; + + info = 0; - ablasf.iallocv(4, ref integerbuf, _params); - ccnt = alglib.ap.len(network.hlconnections)/hlconnfieldwidth; - - // - // check params - // - alglib.ap.assert(k0>=0 && k0=0 && i0=0 && k1=0 && i1=0 ) + if( (((((npoints<1 || samplesize<1) || samplesize>npoints) || nvars<1) || nclasses<1) || ntrees<1) || nfeatures<1 ) { - result = network.weights[network.hlconnections[highlevelidx*hlconnfieldwidth+4]]; + info = -1; + return; } - else + if( nclasses>1 ) { - result = 0; + for(i=0; i<=npoints-1; i++) + { + if( (int)Math.Round(xy[i,nvars])<0 || (int)Math.Round(xy[i,nvars])>=nclasses ) + { + info = -2; + return; + } + } } - return result; + info = 1; + dfbuildercreate(builder, _params); + dfbuildersetdataset(builder, xy, npoints, nvars, nclasses, _params); + dfbuildersetsubsampleratio(builder, (double)samplesize/(double)npoints, _params); + dfbuildersetrndvars(builder, nfeatures, _params); + dfbuilderbuildrandomforest(builder, ntrees, df, rep, _params); } /************************************************************************* - Internal subroutine for Hessian calculation. + Builds a range of random trees [TreeIdx0,TreeIdx1) using decision forest + algorithm. Tree index is used to seed per-tree RNG. - WARNING! Unspeakable math far beyong human capabilities :) + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - private static void mlphessianbatchinternal(multilayerperceptron network, - double[,] xy, - int ssize, - bool naturalerr, - ref double e, - ref double[] grad, - ref double[,] h, + private static void buildrandomtree(decisionforestbuilder s, + int treeidx0, + int treeidx1, alglib.xparams _params) { - int nin = 0; - int nout = 0; - int wcount = 0; - int ntotal = 0; - int istart = 0; + int treeidx = 0; int i = 0; int j = 0; - int k = 0; - int kl = 0; - int offs = 0; - int n1 = 0; - int n2 = 0; - int w1 = 0; - int w2 = 0; - double s = 0; - double t = 0; - double v = 0; - double et = 0; - bool bflag = new bool(); - double f = 0; - double df = 0; - double d2f = 0; - double deidyj = 0; - double mx = 0; - double q = 0; - double z = 0; - double s2 = 0; - double expi = 0; - double expj = 0; - double[] x = new double[0]; - double[] desiredy = new double[0]; - double[] gt = new double[0]; - double[] zeros = new double[0]; - double[,] rx = new double[0,0]; - double[,] ry = new double[0,0]; - double[,] rdx = new double[0,0]; - double[,] rdy = new double[0,0]; - int i_ = 0; - int i1_ = 0; - - e = 0; + int npoints = 0; + int nvars = 0; + int nclasses = 0; + hqrnd.hqrndstate rs = new hqrnd.hqrndstate(); + dfworkbuf workbuf = null; + dfvotebuf votebuf = null; + dftreebuf treebuf = null; + int treesize = 0; + int varstoselect = 0; + int workingsetsize = 0; + double meanloss = 0; - mlpproperties(network, ref nin, ref nout, ref wcount, _params); - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; // - // Prepare + // Perform parallelization // - x = new double[nin-1+1]; - desiredy = new double[nout-1+1]; - zeros = new double[wcount-1+1]; - gt = new double[wcount-1+1]; - rx = new double[ntotal+nout-1+1, wcount-1+1]; - ry = new double[ntotal+nout-1+1, wcount-1+1]; - rdx = new double[ntotal+nout-1+1, wcount-1+1]; - rdy = new double[ntotal+nout-1+1, wcount-1+1]; - e = 0; - for(i=0; i<=wcount-1; i++) + if( treeidx1-treeidx0>1 ) { - zeros[i] = 0; + if( _trypexec_buildrandomtree(s,treeidx0,treeidx1, _params) ) + { + return; + } + j = (treeidx1-treeidx0)/2; + buildrandomtree(s, treeidx0, treeidx0+j, _params); + buildrandomtree(s, treeidx0+j, treeidx1, _params); + return; } - for(i_=0; i_<=wcount-1;i_++) + else { - grad[i_] = zeros[i_]; + alglib.ap.assert(treeidx1-treeidx0==1, "RDF: integrity check failed"); + treeidx = treeidx0; } - for(i=0; i<=wcount-1; i++) + + // + // Prepare + // + npoints = s.npoints; + nvars = s.nvars; + nclasses = s.nclasses; + if( s.rdfglobalseed>0 ) { - for(i_=0; i_<=wcount-1;i_++) - { - h[i,i_] = zeros[i_]; - } + hqrnd.hqrndseed(s.rdfglobalseed, 1+treeidx, rs, _params); + } + else + { + hqrnd.hqrndseed(math.randominteger(30000), 1+treeidx, rs, _params); } // - // Process + // Retrieve buffers. // - for(k=0; k<=ssize-1; k++) + alglib.smp.ae_shared_pool_retrieve(s.workpool, ref workbuf); + alglib.smp.ae_shared_pool_retrieve(s.votepool, ref votebuf); + + // + // Prepare everything for tree construction. + // + alglib.ap.assert(workbuf.trnsize>=1, "DForest: integrity check failed (34636)"); + alglib.ap.assert(workbuf.oobsize>=0, "DForest: integrity check failed (45745)"); + alglib.ap.assert(workbuf.trnsize+workbuf.oobsize==npoints, "DForest: integrity check failed (89415)"); + workingsetsize = -1; + workbuf.varpoolsize = 0; + for(i=0; i<=nvars-1; i++) { - - // - // Process vector with MLPGradN. - // Now Neurons, DFDNET and DError contains results of the last run. - // - for(i_=0; i_<=nin-1;i_++) + if( (double)(s.dsmin[i])!=(double)(s.dsmax[i]) ) { - x[i_] = xy[k,i_]; + workbuf.varpool[workbuf.varpoolsize] = i; + apserv.inc(ref workbuf.varpoolsize, _params); } - if( mlpissoftmax(network, _params) ) + } + workingsetsize = workbuf.varpoolsize; + alglib.ap.assert(workingsetsize>=0, "DForest: integrity check failed (73f5)"); + for(i=0; i<=npoints-1; i++) + { + workbuf.tmp0i[i] = i; + } + for(i=0; i<=workbuf.trnsize-1; i++) + { + j = hqrnd.hqrnduniformi(rs, npoints-i, _params); + apserv.swapelementsi(workbuf.tmp0i, i, i+j, _params); + workbuf.trnset[i] = workbuf.tmp0i[i]; + if( nclasses>1 ) { - - // - // class labels outputs - // - kl = (int)Math.Round(xy[k,nin]); - for(i=0; i<=nout-1; i++) - { - if( i==kl ) - { - desiredy[i] = 1; - } - else - { - desiredy[i] = 0; - } - } + workbuf.trnlabelsi[i] = s.dsival[workbuf.tmp0i[i]]; } else { - - // - // real outputs - // - i1_ = (nin) - (0); - for(i_=0; i_<=nout-1;i_++) - { - desiredy[i_] = xy[k,i_+i1_]; - } + workbuf.trnlabelsr[i] = s.dsrval[workbuf.tmp0i[i]]; } - if( naturalerr ) + if( s.neediobmatrix ) { - mlpgradn(network, x, desiredy, ref et, ref gt, _params); + s.iobmatrix[treeidx,workbuf.trnset[i]] = true; } - else + } + for(i=0; i<=workbuf.oobsize-1; i++) + { + j = workbuf.tmp0i[workbuf.trnsize+i]; + workbuf.oobset[i] = j; + if( nclasses>1 ) { - mlpgrad(network, x, desiredy, ref et, ref gt, _params); + workbuf.ooblabelsi[i] = s.dsival[j]; } - - // - // grad, error - // - e = e+et; - for(i_=0; i_<=wcount-1;i_++) + else { - grad[i_] = grad[i_] + gt[i_]; + workbuf.ooblabelsr[i] = s.dsrval[j]; } - - // - // Hessian. - // Forward pass of the R-algorithm - // - for(i=0; i<=ntotal-1; i++) - { - offs = istart+i*nfieldwidth; - for(i_=0; i_<=wcount-1;i_++) - { - rx[i,i_] = zeros[i_]; - } - for(i_=0; i_<=wcount-1;i_++) - { - ry[i,i_] = zeros[i_]; - } - if( network.structinfo[offs+0]>0 || network.structinfo[offs+0]==-5 ) - { - - // - // Activation function - // - n1 = network.structinfo[offs+2]; - for(i_=0; i_<=wcount-1;i_++) - { - rx[i,i_] = ry[n1,i_]; - } - v = network.dfdnet[i]; - for(i_=0; i_<=wcount-1;i_++) - { - ry[i,i_] = v*rx[i,i_]; - } - continue; - } - if( network.structinfo[offs+0]==0 ) - { - - // - // Adaptive summator - // - n1 = network.structinfo[offs+2]; - n2 = n1+network.structinfo[offs+1]-1; - w1 = network.structinfo[offs+3]; - w2 = w1+network.structinfo[offs+1]-1; - for(j=n1; j<=n2; j++) - { - v = network.weights[w1+j-n1]; - for(i_=0; i_<=wcount-1;i_++) - { - rx[i,i_] = rx[i,i_] + v*ry[j,i_]; - } - rx[i,w1+j-n1] = rx[i,w1+j-n1]+network.neurons[j]; - } - for(i_=0; i_<=wcount-1;i_++) - { - ry[i,i_] = rx[i,i_]; - } - continue; - } - if( network.structinfo[offs+0]<0 ) - { - bflag = true; - if( network.structinfo[offs+0]==-2 ) - { - - // - // input neuron, left unchanged - // - bflag = false; - } - if( network.structinfo[offs+0]==-3 ) - { - - // - // "-1" neuron, left unchanged - // - bflag = false; - } - if( network.structinfo[offs+0]==-4 ) - { - - // - // "0" neuron, left unchanged - // - bflag = false; - } - alglib.ap.assert(!bflag, "MLPHessianNBatch: internal error - unknown neuron type!"); - continue; - } + } + varstoselect = (int)Math.Round(Math.Sqrt(nvars)); + if( (double)(s.rdfvars)>(double)(0) ) + { + varstoselect = (int)Math.Round(s.rdfvars); + } + if( (double)(s.rdfvars)<(double)(0) ) + { + varstoselect = (int)Math.Round(-(nvars*s.rdfvars)); + } + varstoselect = Math.Max(varstoselect, 1); + varstoselect = Math.Min(varstoselect, nvars); + + // + // Perform recurrent construction + // + if( s.rdfimportance==needtrngini ) + { + meanloss = meannrms2(nclasses, workbuf.trnlabelsi, workbuf.trnlabelsr, 0, workbuf.trnsize, workbuf.trnlabelsi, workbuf.trnlabelsr, 0, workbuf.trnsize, ref workbuf.tmpnrms2, _params); + } + else + { + meanloss = meannrms2(nclasses, workbuf.trnlabelsi, workbuf.trnlabelsr, 0, workbuf.trnsize, workbuf.ooblabelsi, workbuf.ooblabelsr, 0, workbuf.oobsize, ref workbuf.tmpnrms2, _params); + } + treesize = 1; + buildrandomtreerec(s, workbuf, workingsetsize, varstoselect, workbuf.treebuf, votebuf, rs, 0, workbuf.trnsize, 0, workbuf.oobsize, meanloss, meanloss, ref treesize, _params); + workbuf.treebuf[0] = treesize; + + // + // Store tree + // + alglib.smp.ae_shared_pool_retrieve(s.treefactory, ref treebuf); + treebuf.treebuf = new double[treesize]; + for(i=0; i<=treesize-1; i++) + { + treebuf.treebuf[i] = workbuf.treebuf[i]; + } + treebuf.treeidx = treeidx; + alglib.smp.ae_shared_pool_recycle(s.treepool, ref treebuf); + + // + // Return other buffers to appropriate pools + // + alglib.smp.ae_shared_pool_recycle(s.workpool, ref workbuf); + alglib.smp.ae_shared_pool_recycle(s.votepool, ref votebuf); + + // + // Update progress indicator + // + apserv.threadunsafeincby(ref s.rdfprogress, npoints, _params); + } + + + /************************************************************************* + Serial stub for GPL edition. + *************************************************************************/ + public static bool _trypexec_buildrandomtree(decisionforestbuilder s, + int treeidx0, + int treeidx1, alglib.xparams _params) + { + return false; + } + + + /************************************************************************* + Recurrent tree construction function using caller-allocated buffers and + caller-initialized RNG. + + Following iterms are processed: + * items [Idx0,Idx1) of WorkBuf.TrnSet + * items [OOBIdx0, OOBIdx1) of WorkBuf.OOBSet + + TreeSize on input must be 1 (header element of the tree), on output it + contains size of the tree. + + OOBLoss on input must contain value of MeanNRMS2(...) computed for entire + dataset. + + Variables from #0 to #WorkingSet-1 from WorkBuf.VarPool are used (for + block algorithm: blocks, not vars) + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + private static void buildrandomtreerec(decisionforestbuilder s, + dfworkbuf workbuf, + int workingset, + int varstoselect, + double[] treebuf, + dfvotebuf votebuf, + hqrnd.hqrndstate rs, + int idx0, + int idx1, + int oobidx0, + int oobidx1, + double meanloss, + double topmostmeanloss, + ref int treesize, + alglib.xparams _params) + { + int npoints = 0; + int nclasses = 0; + int i = 0; + int j = 0; + int j0 = 0; + double v = 0; + bool labelsaresame = new bool(); + int offs = 0; + int varbest = 0; + double splitbest = 0; + int i1 = 0; + int i2 = 0; + int idxtrn = 0; + int idxoob = 0; + double meanloss0 = 0; + double meanloss1 = 0; + + alglib.ap.assert(s.dstype==0, "not supported skbdgfsi!"); + alglib.ap.assert(idx01 ) + { + labelsaresame = true; + for(i=0; i<=nclasses-1; i++) + { + workbuf.classpriors[i] = 0; + } + j0 = workbuf.trnlabelsi[idx0]; + for(i=idx0; i<=idx1-1; i++) + { + j = workbuf.trnlabelsi[i]; + workbuf.classpriors[j] = workbuf.classpriors[j]+1; + labelsaresame = labelsaresame && j0==j; + } + } + else + { + labelsaresame = false; + } + + // + // Leaf node + // + if( idx1-idx0==1 || labelsaresame ) + { + if( nclasses==1 ) + { + outputleaf(s, workbuf, treebuf, votebuf, idx0, idx1, oobidx0, oobidx1, ref treesize, workbuf.trnlabelsr[idx0], _params); + } + else + { + outputleaf(s, workbuf, treebuf, votebuf, idx0, idx1, oobidx0, oobidx1, ref treesize, workbuf.trnlabelsi[idx0], _params); } + return; + } + + // + // Non-leaf node. + // Investigate possible splits. + // + alglib.ap.assert(s.rdfalgo==0, "BuildRandomForest: unexpected algo"); + choosecurrentsplitdense(s, workbuf, ref workingset, varstoselect, rs, idx0, idx1, ref varbest, ref splitbest, _params); + if( varbest<0 ) + { // - // Hessian. Backward pass of the R-algorithm. + // No good split was found; make leaf (label is randomly chosen) and exit. // - // Stage 1. Initialize RDY + if( nclasses>1 ) + { + v = workbuf.trnlabelsi[idx0+hqrnd.hqrnduniformi(rs, idx1-idx0, _params)]; + } + else + { + v = workbuf.trnlabelsr[idx0+hqrnd.hqrnduniformi(rs, idx1-idx0, _params)]; + } + outputleaf(s, workbuf, treebuf, votebuf, idx0, idx1, oobidx0, oobidx1, ref treesize, v, _params); + return; + } + + // + // Good split WAS found, we can perform it: + // * first, we split training set + // * then, we similarly split OOB set + // + alglib.ap.assert(s.dstype==0, "not supported 54bfdh"); + offs = npoints*varbest; + i1 = idx0; + i2 = idx1-1; + while( i1<=i2 ) + { + // - for(i=0; i<=ntotal+nout-1; i++) + // Reorder indexes so that left partition is in [Idx0..I1), + // and right partition is in [I2+1..Idx1) + // + if( workbuf.bestvals[i1]=splitbest ) { - - // - // Standardisation. - // - // In context of the Hessian calculation standardisation - // is considered as additional layer with weightless - // activation function: - // - // F(NET) := Sigma*NET - // - // So we add one more layer to forward pass, and - // make forward/backward pass through this layer. - // - for(i=0; i<=nout-1; i++) - { - n1 = ntotal-nout+i; - n2 = ntotal+i; - - // - // Forward pass from N1 to N2 - // - for(i_=0; i_<=wcount-1;i_++) - { - rx[n2,i_] = ry[n1,i_]; - } - v = network.columnsigmas[nin+i]; - for(i_=0; i_<=wcount-1;i_++) - { - ry[n2,i_] = v*rx[n2,i_]; - } - - // - // Initialization of RDY - // - for(i_=0; i_<=wcount-1;i_++) - { - rdy[n2,i_] = ry[n2,i_]; - } - - // - // Backward pass from N2 to N1: - // 1. Calculate R(dE/dX). - // 2. No R(dE/dWij) is needed since weight of activation neuron - // is fixed to 1. So we can update R(dE/dY) for - // the connected neuron (note that Vij=0, Wij=1) - // - df = network.columnsigmas[nin+i]; - for(i_=0; i_<=wcount-1;i_++) - { - rdx[n2,i_] = df*rdy[n2,i_]; - } - for(i_=0; i_<=wcount-1;i_++) - { - rdy[n1,i_] = rdy[n1,i_] + rdx[n2,i_]; - } - } + i2 = i2-1; + continue; + } + j = workbuf.trnset[i1]; + workbuf.trnset[i1] = workbuf.trnset[i2]; + workbuf.trnset[i2] = j; + if( nclasses>1 ) + { + j = workbuf.trnlabelsi[i1]; + workbuf.trnlabelsi[i1] = workbuf.trnlabelsi[i2]; + workbuf.trnlabelsi[i2] = j; } else { - - // - // Softmax. - // - // Initialize RDY using generalized expression for ei'(yi) - // (see expression (9) from p. 5 of "Fast Exact Multiplication by the Hessian"). - // - // When we are working with softmax network, generalized - // expression for ei'(yi) is used because softmax - // normalization leads to ei, which depends on all y's - // - if( naturalerr ) - { - - // - // softmax + cross-entropy. - // We have: - // - // S = sum(exp(yk)), - // ei = sum(trn)*exp(yi)/S-trn_i - // - // j=i: d(ei)/d(yj) = T*exp(yi)*(S-exp(yi))/S^2 - // j<>i: d(ei)/d(yj) = -T*exp(yi)*exp(yj)/S^2 - // - t = 0; - for(i=0; i<=nout-1; i++) - { - t = t+desiredy[i]; - } - mx = network.neurons[ntotal-nout]; - for(i=0; i<=nout-1; i++) - { - mx = Math.Max(mx, network.neurons[ntotal-nout+i]); - } - s = 0; - for(i=0; i<=nout-1; i++) - { - network.nwbuf[i] = Math.Exp(network.neurons[ntotal-nout+i]-mx); - s = s+network.nwbuf[i]; - } - for(i=0; i<=nout-1; i++) - { - for(j=0; j<=nout-1; j++) - { - if( j==i ) - { - deidyj = t*network.nwbuf[i]*(s-network.nwbuf[i])/math.sqr(s); - for(i_=0; i_<=wcount-1;i_++) - { - rdy[ntotal-nout+i,i_] = rdy[ntotal-nout+i,i_] + deidyj*ry[ntotal-nout+i,i_]; - } - } - else - { - deidyj = -(t*network.nwbuf[i]*network.nwbuf[j]/math.sqr(s)); - for(i_=0; i_<=wcount-1;i_++) - { - rdy[ntotal-nout+i,i_] = rdy[ntotal-nout+i,i_] + deidyj*ry[ntotal-nout+j,i_]; - } - } - } - } - } - else - { - - // - // For a softmax + squared error we have expression - // far beyond human imagination so we dont even try - // to comment on it. Just enjoy the code... - // - // P.S. That's why "natural error" is called "natural" - - // compact beatiful expressions, fast code.... - // - mx = network.neurons[ntotal-nout]; - for(i=0; i<=nout-1; i++) - { - mx = Math.Max(mx, network.neurons[ntotal-nout+i]); - } - s = 0; - s2 = 0; - for(i=0; i<=nout-1; i++) - { - network.nwbuf[i] = Math.Exp(network.neurons[ntotal-nout+i]-mx); - s = s+network.nwbuf[i]; - s2 = s2+math.sqr(network.nwbuf[i]); - } - q = 0; - for(i=0; i<=nout-1; i++) - { - q = q+(network.y[i]-desiredy[i])*network.nwbuf[i]; - } - for(i=0; i<=nout-1; i++) - { - z = -q+(network.y[i]-desiredy[i])*s; - expi = network.nwbuf[i]; - for(j=0; j<=nout-1; j++) - { - expj = network.nwbuf[j]; - if( j==i ) - { - deidyj = expi/math.sqr(s)*((z+expi)*(s-2*expi)/s+expi*s2/math.sqr(s)); - } - else - { - deidyj = expi*expj/math.sqr(s)*(s2/math.sqr(s)-2*z/s-(expi+expj)/s+(network.y[i]-desiredy[i])-(network.y[j]-desiredy[j])); - } - for(i_=0; i_<=wcount-1;i_++) - { - rdy[ntotal-nout+i,i_] = rdy[ntotal-nout+i,i_] + deidyj*ry[ntotal-nout+j,i_]; - } - } - } - } + v = workbuf.trnlabelsr[i1]; + workbuf.trnlabelsr[i1] = workbuf.trnlabelsr[i2]; + workbuf.trnlabelsr[i2] = v; } + i1 = i1+1; + i2 = i2-1; + } + alglib.ap.assert(i1==i2+1, "BuildRandomTreeRec: integrity check failed (45rds3)"); + idxtrn = i1; + if( oobidx0=0; i--) + i1 = oobidx0; + i2 = oobidx1-1; + while( i1<=i2 ) { // - // Possible variants: - // 1. Activation function - // 2. Adaptive summator - // 3. Special neuron + // Reorder indexes so that left partition is in [Idx0..I1), + // and right partition is in [I2+1..Idx1) // - offs = istart+i*nfieldwidth; - if( network.structinfo[offs+0]>0 || network.structinfo[offs+0]==-5 ) + if( s.dsdata[offs+workbuf.oobset[i1]]=splitbest ) { - - // - // Adaptive summator - // - n1 = network.structinfo[offs+2]; - n2 = n1+network.structinfo[offs+1]-1; - w1 = network.structinfo[offs+3]; - w2 = w1+network.structinfo[offs+1]-1; - - // - // First, calculate R(dE/dX). - // - for(i_=0; i_<=wcount-1;i_++) - { - rdx[i,i_] = rdy[i,i_]; - } - - // - // Then, calculate R(dE/dWij) - // - for(j=w1; j<=w2; j++) - { - v = network.neurons[n1+j-w1]; - for(i_=0; i_<=wcount-1;i_++) - { - h[j,i_] = h[j,i_] + v*rdx[i,i_]; - } - v = network.derror[i]; - for(i_=0; i_<=wcount-1;i_++) - { - h[j,i_] = h[j,i_] + v*ry[n1+j-w1,i_]; - } - } - - // - // And finally, update R(dE/dY) for connected neurons. - // - for(j=w1; j<=w2; j++) - { - v = network.weights[j]; - for(i_=0; i_<=wcount-1;i_++) - { - rdy[n1+j-w1,i_] = rdy[n1+j-w1,i_] + v*rdx[i,i_]; - } - rdy[n1+j-w1,j] = rdy[n1+j-w1,j]+network.derror[i]; - } + i2 = i2-1; continue; } - if( network.structinfo[offs+0]<0 ) + j = workbuf.oobset[i1]; + workbuf.oobset[i1] = workbuf.oobset[i2]; + workbuf.oobset[i2] = j; + if( nclasses>1 ) { - bflag = false; - if( (network.structinfo[offs+0]==-2 || network.structinfo[offs+0]==-3) || network.structinfo[offs+0]==-4 ) - { - - // - // Special neuron type, no back-propagation required - // - bflag = true; - } - alglib.ap.assert(bflag, "MLPHessianNBatch: unknown neuron type!"); - continue; + j = workbuf.ooblabelsi[i1]; + workbuf.ooblabelsi[i1] = workbuf.ooblabelsi[i2]; + workbuf.ooblabelsi[i2] = j; } + else + { + v = workbuf.ooblabelsr[i1]; + workbuf.ooblabelsr[i1] = workbuf.ooblabelsr[i2]; + workbuf.ooblabelsr[i2] = v; + } + i1 = i1+1; + i2 = i2-1; } + alglib.ap.assert(i1==i2+1, "BuildRandomTreeRec: integrity check failed (643fs3)"); + idxoob = i1; + } + else + { + idxoob = oobidx0; + } + + // + // Compute estimates of NRMS2 loss over TRN or OOB subsets, update Gini importances + // + if( s.rdfimportance==needtrngini ) + { + meanloss0 = meannrms2(nclasses, workbuf.trnlabelsi, workbuf.trnlabelsr, idx0, idxtrn, workbuf.trnlabelsi, workbuf.trnlabelsr, idx0, idxtrn, ref workbuf.tmpnrms2, _params); + meanloss1 = meannrms2(nclasses, workbuf.trnlabelsi, workbuf.trnlabelsr, idxtrn, idx1, workbuf.trnlabelsi, workbuf.trnlabelsr, idxtrn, idx1, ref workbuf.tmpnrms2, _params); + } + else + { + meanloss0 = meannrms2(nclasses, workbuf.trnlabelsi, workbuf.trnlabelsr, idx0, idxtrn, workbuf.ooblabelsi, workbuf.ooblabelsr, oobidx0, idxoob, ref workbuf.tmpnrms2, _params); + meanloss1 = meannrms2(nclasses, workbuf.trnlabelsi, workbuf.trnlabelsr, idxtrn, idx1, workbuf.ooblabelsi, workbuf.ooblabelsr, idxoob, oobidx1, ref workbuf.tmpnrms2, _params); } + votebuf.giniimportances[varbest] = votebuf.giniimportances[varbest]+(meanloss-(meanloss0+meanloss1))/(topmostmeanloss+1.0e-20); + + // + // Generate tree node and subtrees (recursively) + // + treebuf[treesize] = varbest; + treebuf[treesize+1] = splitbest; + i = treesize; + treesize = treesize+innernodewidth; + buildrandomtreerec(s, workbuf, workingset, varstoselect, treebuf, votebuf, rs, idx0, idxtrn, oobidx0, idxoob, meanloss0, topmostmeanloss, ref treesize, _params); + treebuf[i+2] = treesize; + buildrandomtreerec(s, workbuf, workingset, varstoselect, treebuf, votebuf, rs, idxtrn, idx1, idxoob, oobidx1, meanloss1, topmostmeanloss, ref treesize, _params); } /************************************************************************* - Internal subroutine + Estimates permutation variable importance ratings for a range of dataset + points. - Network must be processed by MLPProcess on X + Initial call to this function should span entire range of the dataset, + [Idx0,Idx1)=[0,NPoints), because function performs initialization of some + internal structures when called with these arguments. + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - private static void mlpinternalcalculategradient(multilayerperceptron network, - double[] neurons, - double[] weights, - ref double[] derror, - ref double[] grad, - bool naturalerrorfunc, + private static void estimatevariableimportance(decisionforestbuilder s, + int sessionseed, + decisionforest df, + int ntrees, + dfreport rep, alglib.xparams _params) { + int npoints = 0; + int nvars = 0; + int nclasses = 0; + int nperm = 0; int i = 0; - int n1 = 0; - int n2 = 0; - int w1 = 0; - int w2 = 0; - int ntotal = 0; - int istart = 0; - int nin = 0; - int nout = 0; - int offs = 0; - double dedf = 0; - double dfdnet = 0; - double v = 0; - double fown = 0; - double deown = 0; - double net = 0; - double mx = 0; - bool bflag = new bool(); - int i_ = 0; - int i1_ = 0; + int j = 0; + int k = 0; + dfvotebuf vote = null; + double[] tmpr0 = new double[0]; + double[] tmpr1 = new double[0]; + int[] tmpi0 = new int[0]; + double[] losses = new double[0]; + dfpermimpbuf permseed = new dfpermimpbuf(); + dfpermimpbuf permresult = null; + alglib.smp.shared_pool permpool = new alglib.smp.shared_pool(); + double nopermloss = 0; + double totalpermloss = 0; + hqrnd.hqrndstate varimprs = new hqrnd.hqrndstate(); + npoints = s.npoints; + nvars = s.nvars; + nclasses = s.nclasses; // - // Read network geometry + // No importance rating // - nin = network.structinfo[1]; - nout = network.structinfo[2]; - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; + if( s.rdfimportance==0 ) + { + return; + } // - // Pre-processing of dError/dOut: - // from dError/dOut(normalized) to dError/dOut(non-normalized) + // Gini importance // - alglib.ap.assert(network.structinfo[6]==0 || network.structinfo[6]==1, "MLPInternalCalculateGradient: unknown normalization type!"); - if( network.structinfo[6]==1 ) + if( s.rdfimportance==needtrngini || s.rdfimportance==needoobgini ) { // - // Softmax + // Merge OOB Gini importances computed during tree generation // - if( !naturalerrorfunc ) + alglib.smp.ae_shared_pool_first_recycled(s.votepool, ref vote); + while( vote!=null ) { - mx = network.neurons[ntotal-nout]; - for(i=0; i<=nout-1; i++) - { - mx = Math.Max(mx, network.neurons[ntotal-nout+i]); - } - net = 0; - for(i=0; i<=nout-1; i++) - { - network.nwbuf[i] = Math.Exp(network.neurons[ntotal-nout+i]-mx); - net = net+network.nwbuf[i]; - } - i1_ = (0)-(ntotal-nout); - v = 0.0; - for(i_=ntotal-nout; i_<=ntotal-1;i_++) - { - v += network.derror[i_]*network.nwbuf[i_+i1_]; - } - for(i=0; i<=nout-1; i++) - { - fown = network.nwbuf[i]; - deown = network.derror[ntotal-nout+i]; - network.nwbuf[nout+i] = (-v+deown*fown+deown*(net-fown))*fown/math.sqr(net); - } - for(i=0; i<=nout-1; i++) + for(i=0; i<=nvars-1; i++) { - network.derror[ntotal-nout+i] = network.nwbuf[nout+i]; + rep.varimportances[i] = rep.varimportances[i]+vote.giniimportances[i]/ntrees; } + alglib.smp.ae_shared_pool_next_recycled(s.votepool, ref vote); + } + for(i=0; i<=nvars-1; i++) + { + rep.varimportances[i] = apserv.boundval(rep.varimportances[i], 0, 1, _params); } - } - else - { // - // Un-standardisation + // Compute topvars[] array // - for(i=0; i<=nout-1; i++) + tmpr0 = new double[nvars]; + for(j=0; j<=nvars-1; j++) { - network.derror[ntotal-nout+i] = network.derror[ntotal-nout+i]*network.columnsigmas[nin+i]; + tmpr0[j] = -rep.varimportances[j]; + rep.topvars[j] = j; } + tsort.tagsortfasti(ref tmpr0, ref rep.topvars, ref tmpr1, ref tmpi0, nvars, _params); + return; } // - // Backpropagation + // Permutation importance // - for(i=ntotal-1; i>=0; i--) + if( s.rdfimportance==needpermutation ) { + alglib.ap.assert(df.forestformat==dfuncompressedv0, "EstimateVariableImportance: integrity check failed (ff)"); + alglib.ap.assert(alglib.ap.rows(s.iobmatrix)>=ntrees && alglib.ap.cols(s.iobmatrix)>=npoints, "EstimateVariableImportance: integrity check failed (IOB)"); // - // Extract info + // Generate packed representation of the shuffle which is applied to all variables // - offs = istart+i*nfieldwidth; - if( network.structinfo[offs+0]>0 || network.structinfo[offs+0]==-5 ) + // Ideally we want to apply different permutations to different variables, + // i.e. we have to generate and store NPoints*NVars random numbers. + // However due to performance and memory restrictions we prefer to use compact + // representation: + // * we store one "reference" permutation P_ref in VarImpShuffle2[0:NPoints-1] + // * a permutation P_j applied to variable J is obtained by circularly shifting + // elements in P_ref by VarImpShuffle2[NPoints+J] + // + hqrnd.hqrndseed(sessionseed, 1117, varimprs, _params); + apserv.ivectorsetlengthatleast(ref s.varimpshuffle2, npoints+nvars, _params); + for(i=0; i<=npoints-1; i++) { - - // - // Activation function - // - dedf = network.derror[i]; - dfdnet = network.dfdnet[i]; - derror[network.structinfo[offs+2]] = derror[network.structinfo[offs+2]]+dedf*dfdnet; - continue; + s.varimpshuffle2[i] = i; } - if( network.structinfo[offs+0]==0 ) + for(i=0; i<=npoints-2; i++) { - - // - // Adaptive summator - // - n1 = network.structinfo[offs+2]; - n2 = n1+network.structinfo[offs+1]-1; - w1 = network.structinfo[offs+3]; - w2 = w1+network.structinfo[offs+1]-1; - dedf = network.derror[i]; - dfdnet = 1.0; - v = dedf*dfdnet; - i1_ = (n1) - (w1); - for(i_=w1; i_<=w2;i_++) - { - grad[i_] = v*neurons[i_+i1_]; - } - i1_ = (w1) - (n1); - for(i_=n1; i_<=n2;i_++) - { - derror[i_] = derror[i_] + v*weights[i_+i1_]; - } - continue; + j = i+hqrnd.hqrnduniformi(varimprs, npoints-i, _params); + k = s.varimpshuffle2[i]; + s.varimpshuffle2[i] = s.varimpshuffle2[j]; + s.varimpshuffle2[j] = k; } - if( network.structinfo[offs+0]<0 ) + for(i=0; i<=nvars-1; i++) { - bflag = false; - if( (network.structinfo[offs+0]==-2 || network.structinfo[offs+0]==-3) || network.structinfo[offs+0]==-4 ) + s.varimpshuffle2[npoints+i] = hqrnd.hqrnduniformi(varimprs, npoints, _params); + } + + // + // Prepare buffer object, seed pool + // + nperm = nvars+2; + permseed.losses = new double[nperm]; + for(j=0; j<=nperm-1; j++) + { + permseed.losses[j] = 0; + } + permseed.yv = new double[nperm*nclasses]; + permseed.xraw = new double[nvars]; + permseed.xdist = new double[nvars]; + permseed.xcur = new double[nvars]; + permseed.targety = new double[nclasses]; + permseed.startnodes = new int[nvars]; + permseed.y = new double[nclasses]; + alglib.smp.ae_shared_pool_set_seed(permpool, permseed); + + // + // Recursively split subset and process (using parallel capabilities, if possible) + // + estimatepermutationimportances(s, df, ntrees, permpool, 0, npoints, _params); + + // + // Merge results + // + losses = new double[nperm]; + for(j=0; j<=nperm-1; j++) + { + losses[j] = 1.0e-20; + } + alglib.smp.ae_shared_pool_first_recycled(permpool, ref permresult); + while( permresult!=null ) + { + for(j=0; j<=nperm-1; j++) { - - // - // Special neuron type, no back-propagation required - // - bflag = true; + losses[j] = losses[j]+permresult.losses[j]; } - alglib.ap.assert(bflag, "MLPInternalCalculateGradient: unknown neuron type!"); - continue; + alglib.smp.ae_shared_pool_next_recycled(permpool, ref permresult); + } + + // + // Compute importances + // + nopermloss = losses[nvars+1]; + totalpermloss = losses[nvars]; + for(i=0; i<=nvars-1; i++) + { + rep.varimportances[i] = 1-nopermloss/totalpermloss-(1-losses[i]/totalpermloss); + rep.varimportances[i] = apserv.boundval(rep.varimportances[i], 0, 1, _params); + } + + // + // Compute topvars[] array + // + tmpr0 = new double[nvars]; + for(j=0; j<=nvars-1; j++) + { + tmpr0[j] = -rep.varimportances[j]; + rep.topvars[j] = j; } + tsort.tagsortfasti(ref tmpr0, ref rep.topvars, ref tmpr1, ref tmpi0, nvars, _params); + return; } + alglib.ap.assert(false, "EstimateVariableImportance: unexpected importance type"); } - private static void mlpchunkedgradient(multilayerperceptron network, - double[,] xy, - int cstart, - int csize, - double[] batch4buf, - double[] hpcbuf, - ref double e, - bool naturalerrorfunc, + /************************************************************************* + Serial stub for GPL edition. + *************************************************************************/ + public static bool _trypexec_estimatevariableimportance(decisionforestbuilder s, + int sessionseed, + decisionforest df, + int ntrees, + dfreport rep, alglib.xparams _params) + { + return false; + } + + + /************************************************************************* + Estimates permutation variable importance ratings for a range of dataset + points. + + Initial call to this function should span entire range of the dataset, + [Idx0,Idx1)=[0,NPoints), because function performs initialization of some + internal structures when called with these arguments. + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + private static void estimatepermutationimportances(decisionforestbuilder s, + decisionforest df, + int ntrees, + alglib.smp.shared_pool permpool, + int idx0, + int idx1, alglib.xparams _params) { + int npoints = 0; + int nvars = 0; + int nclasses = 0; + int nperm = 0; int i = 0; int j = 0; int k = 0; - int kl = 0; - int ntotal = 0; - int nin = 0; - int nout = 0; - int offs = 0; - double f = 0; - double df = 0; - double d2f = 0; double v = 0; - double vv = 0; - double s = 0; - double fown = 0; - double deown = 0; - bool bflag = new bool(); - int istart = 0; - int entrysize = 0; - int dfoffs = 0; - int derroroffs = 0; - int entryoffs = 0; - int neuronidx = 0; - int srcentryoffs = 0; - int srcneuronidx = 0; - int srcweightidx = 0; - int neurontype = 0; - int nweights = 0; - int offs0 = 0; - int offs1 = 0; - int offs2 = 0; - double v0 = 0; - double v1 = 0; - double v2 = 0; - double v3 = 0; - double s0 = 0; - double s1 = 0; - double s2 = 0; - double s3 = 0; - int chunksize = 0; + int treeroot = 0; + int nodeoffs = 0; + double prediction = 0; + int varidx = 0; + int oobcounts = 0; + int srcidx = 0; + dfpermimpbuf permimpbuf = null; - chunksize = 4; - alglib.ap.assert(csize<=chunksize, "MLPChunkedGradient: internal error (CSize>ChunkSize)"); - - // - // Try to use HPC core, if possible - // - if( hpccores.hpcchunkedgradient(network.weights, network.structinfo, network.columnmeans, network.columnsigmas, xy, cstart, csize, batch4buf, hpcbuf, ref e, naturalerrorfunc, _params) ) - { - return; - } - - // - // Read network geometry, prepare data - // - nin = network.structinfo[1]; - nout = network.structinfo[2]; - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; - entrysize = 12; - dfoffs = 4; - derroroffs = 8; - - // - // Fill Batch4Buf by zeros. - // - // THIS STAGE IS VERY IMPORTANT! - // - // We fill all components of entry - neuron values, dF/dNET, dError/dF. - // It allows us to easily handle situations when CSize=0 && idx0<=idx1) && idx1<=npoints, "EstimateVariableImportance: integrity check failed (idx)"); + alglib.ap.assert(alglib.ap.rows(s.iobmatrix)>=ntrees && alglib.ap.cols(s.iobmatrix)>=npoints, "EstimateVariableImportance: integrity check failed (IOB)"); // - // Forward pass: - // 1. Load data into Batch4Buf. If CSizepermutationimportancebatchsize ) { - entryoffs = entrysize*neuronidx; - offs = istart+neuronidx*nfieldwidth; - neurontype = network.structinfo[offs+0]; - if( neurontype>0 || neurontype==-5 ) - { - - // - // "activation function" neuron, which takes value of neuron SrcNeuronIdx - // and applies activation function to it. - // - // This neuron has no weights and no tunable parameters. - // - srcneuronidx = network.structinfo[offs+2]; - srcentryoffs = entrysize*srcneuronidx; - mlpactivationfunction(batch4buf[srcentryoffs+0], neurontype, ref f, ref df, ref d2f, _params); - batch4buf[entryoffs+0] = f; - batch4buf[entryoffs+0+dfoffs] = df; - mlpactivationfunction(batch4buf[srcentryoffs+1], neurontype, ref f, ref df, ref d2f, _params); - batch4buf[entryoffs+1] = f; - batch4buf[entryoffs+1+dfoffs] = df; - mlpactivationfunction(batch4buf[srcentryoffs+2], neurontype, ref f, ref df, ref d2f, _params); - batch4buf[entryoffs+2] = f; - batch4buf[entryoffs+2+dfoffs] = df; - mlpactivationfunction(batch4buf[srcentryoffs+3], neurontype, ref f, ref df, ref d2f, _params); - batch4buf[entryoffs+3] = f; - batch4buf[entryoffs+3+dfoffs] = df; - continue; - } - if( neurontype==0 ) - { - - // - // "adaptive summator" neuron, whose output is a weighted sum of inputs. - // It has weights, but has no activation function. - // - nweights = network.structinfo[offs+1]; - srcneuronidx = network.structinfo[offs+2]; - srcentryoffs = entrysize*srcneuronidx; - srcweightidx = network.structinfo[offs+3]; - v0 = 0; - v1 = 0; - v2 = 0; - v3 = 0; - for(j=0; j<=nweights-1; j++) - { - v = network.weights[srcweightidx]; - srcweightidx = srcweightidx+1; - v0 = v0+v*batch4buf[srcentryoffs+0]; - v1 = v1+v*batch4buf[srcentryoffs+1]; - v2 = v2+v*batch4buf[srcentryoffs+2]; - v3 = v3+v*batch4buf[srcentryoffs+3]; - srcentryoffs = srcentryoffs+entrysize; - } - batch4buf[entryoffs+0] = v0; - batch4buf[entryoffs+1] = v1; - batch4buf[entryoffs+2] = v2; - batch4buf[entryoffs+3] = v3; - batch4buf[entryoffs+0+dfoffs] = 1; - batch4buf[entryoffs+1+dfoffs] = 1; - batch4buf[entryoffs+2+dfoffs] = 1; - batch4buf[entryoffs+3+dfoffs] = 1; - continue; - } - if( neurontype<0 ) + if( _trypexec_estimatepermutationimportances(s,df,ntrees,permpool,idx0,idx1, _params) ) { - bflag = false; - if( neurontype==-2 ) - { - - // - // Input neuron, left unchanged - // - bflag = true; - } - if( neurontype==-3 ) - { - - // - // "-1" neuron - // - batch4buf[entryoffs+0] = -1; - batch4buf[entryoffs+1] = -1; - batch4buf[entryoffs+2] = -1; - batch4buf[entryoffs+3] = -1; - batch4buf[entryoffs+0+dfoffs] = 0; - batch4buf[entryoffs+1+dfoffs] = 0; - batch4buf[entryoffs+2+dfoffs] = 0; - batch4buf[entryoffs+3+dfoffs] = 0; - bflag = true; - } - if( neurontype==-4 ) - { - - // - // "0" neuron - // - batch4buf[entryoffs+0] = 0; - batch4buf[entryoffs+1] = 0; - batch4buf[entryoffs+2] = 0; - batch4buf[entryoffs+3] = 0; - batch4buf[entryoffs+0+dfoffs] = 0; - batch4buf[entryoffs+1+dfoffs] = 0; - batch4buf[entryoffs+2+dfoffs] = 0; - batch4buf[entryoffs+3+dfoffs] = 0; - bflag = true; - } - alglib.ap.assert(bflag, "MLPChunkedGradient: internal error - unknown neuron type!"); - continue; + return; } + j = (idx1-idx0)/2; + estimatepermutationimportances(s, df, ntrees, permpool, idx0, idx0+j, _params); + estimatepermutationimportances(s, df, ntrees, permpool, idx0+j, idx1, _params); + return; } // - // Intermediate phase between forward and backward passes. + // Retrieve buffer object from pool // - // For regression networks: - // * forward pass is completely done (no additional post-processing is - // needed). - // * before starting backward pass, we have to calculate dError/dOut - // for output neurons. We also update error at this phase. + alglib.smp.ae_shared_pool_retrieve(permpool, ref permimpbuf); + // - // For classification networks: - // * in addition to forward pass we apply SOFTMAX normalization to - // output neurons. - // * after applying normalization, we have to calculate dError/dOut, - // which is calculated in two steps: - // * first, we calculate derivative of error with respect to SOFTMAX - // normalized outputs (normalized dError) - // * then, we calculate derivative of error with respect to values - // of outputs BEFORE normalization was applied to them + // Process range of points [idx0,idx1) // - alglib.ap.assert(network.structinfo[6]==0 || network.structinfo[6]==1, "MLPChunkedGradient: unknown normalization type!"); - if( network.structinfo[6]==1 ) + nperm = nvars+2; + for(i=idx0; i<=idx1-1; i++) { - - // - // SOFTMAX-normalized network. - // - // First, calculate (V0,V1,V2,V3) - component-wise maximum - // of output neurons. This vector of maximum values will be - // used for normalization of outputs prior to calculating - // exponentials. - // - // NOTE: the only purpose of this stage is to prevent overflow - // during calculation of exponentials. With this stage - // we make sure that all exponentials are calculated - // with non-positive argument. If you load (0,0,0,0) to - // (V0,V1,V2,V3), your program will continue working - - // although with less robustness. - // - entryoffs = entrysize*(ntotal-nout); - v0 = batch4buf[entryoffs+0]; - v1 = batch4buf[entryoffs+1]; - v2 = batch4buf[entryoffs+2]; - v3 = batch4buf[entryoffs+3]; - entryoffs = entryoffs+entrysize; - for(i=1; i<=nout-1; i++) + alglib.ap.assert(s.dstype==0, "EstimateVariableImportance: unexpected dataset type"); + for(j=0; j<=nvars-1; j++) { - v = batch4buf[entryoffs+0]; - if( v>v0 ) - { - v0 = v; - } - v = batch4buf[entryoffs+1]; - if( v>v1 ) - { - v1 = v; - } - v = batch4buf[entryoffs+2]; - if( v>v2 ) - { - v2 = v; - } - v = batch4buf[entryoffs+3]; - if( v>v3 ) + permimpbuf.xraw[j] = s.dsdata[j*npoints+i]; + srcidx = s.varimpshuffle2[(i+s.varimpshuffle2[npoints+j])%npoints]; + permimpbuf.xdist[j] = s.dsdata[j*npoints+srcidx]; + } + if( nclasses>1 ) + { + for(j=0; j<=nclasses-1; j++) { - v3 = v; + permimpbuf.targety[j] = 0; } - entryoffs = entryoffs+entrysize; + permimpbuf.targety[s.dsival[i]] = 1; } - - // - // Then, calculate exponentials and place them to part of the - // array which is located past the last entry. We also - // calculate sum of exponentials which will be stored past the - // exponentials. - // - entryoffs = entrysize*(ntotal-nout); - offs0 = entrysize*ntotal; - s0 = 0; - s1 = 0; - s2 = 0; - s3 = 0; - for(i=0; i<=nout-1; i++) + else { - v = Math.Exp(batch4buf[entryoffs+0]-v0); - s0 = s0+v; - batch4buf[offs0+0] = v; - v = Math.Exp(batch4buf[entryoffs+1]-v1); - s1 = s1+v; - batch4buf[offs0+1] = v; - v = Math.Exp(batch4buf[entryoffs+2]-v2); - s2 = s2+v; - batch4buf[offs0+2] = v; - v = Math.Exp(batch4buf[entryoffs+3]-v3); - s3 = s3+v; - batch4buf[offs0+3] = v; - entryoffs = entryoffs+entrysize; - offs0 = offs0+chunksize; + permimpbuf.targety[0] = s.dsrval[i]; } - offs0 = entrysize*ntotal+2*nout*chunksize; - batch4buf[offs0+0] = s0; - batch4buf[offs0+1] = s1; - batch4buf[offs0+2] = s2; - batch4buf[offs0+3] = s3; // - // Now we have: - // * Batch4Buf[0...EntrySize*NTotal-1] stores: - // * NTotal*ChunkSize neuron output values (SOFTMAX normalization - // was not applied to these values), - // * NTotal*ChunkSize values of dF/dNET (derivative of neuron - // output with respect to its input) - // * NTotal*ChunkSize zeros in the elements which correspond to - // dError/dOut (derivative of error with respect to neuron output). - // * Batch4Buf[EntrySize*NTotal...EntrySize*NTotal+ChunkSize*NOut-1] - - // stores exponentials of last NOut neurons. - // * Batch4Buf[EntrySize*NTotal+ChunkSize*NOut-1...EntrySize*NTotal+ChunkSize*2*NOut-1] - // - can be used for temporary calculations - // * Batch4Buf[EntrySize*NTotal+ChunkSize*2*NOut...EntrySize*NTotal+ChunkSize*2*NOut+ChunkSize-1] - // - stores sum-of-exponentials - // - // Block below calculates derivatives of error function with respect - // to non-SOFTMAX-normalized output values of last NOut neurons. - // - // It is quite complicated; we do not describe algebra behind it, - // but if you want you may check it yourself :) + // Process all trees, for each tree compute NPerm losses corresponding + // to various permutations of variable values // - if( naturalerrorfunc ) + for(j=0; j<=nperm*nclasses-1; j++) { - - // - // Calculate derivative of error with respect to values of - // output neurons PRIOR TO SOFTMAX NORMALIZATION. Because we - // use natural error function (cross-entropy), we can do so - // very easy. - // - offs0 = entrysize*ntotal+2*nout*chunksize; - for(k=0; k<=csize-1; k++) + permimpbuf.yv[j] = 0; + } + oobcounts = 0; + treeroot = 0; + for(k=0; k<=ntrees-1; k++) + { + if( !s.iobmatrix[k,i] ) { - s = batch4buf[offs0+k]; - kl = (int)Math.Round(xy[cstart+k,nin]); - offs1 = (ntotal-nout)*entrysize+derroroffs+k; - offs2 = entrysize*ntotal+k; - for(i=0; i<=nout-1; i++) + + // + // Process original (unperturbed) point and analyze path from the + // tree root to the final leaf. Output prediction to RawPrediction. + // + // Additionally, for each variable in [0,NVars-1] save offset of + // the first split on this variable. It allows us to quickly compute + // tree decision when perturbation does not change decision path. + // + alglib.ap.assert(df.forestformat==dfuncompressedv0, "EstimateVariableImportance: integrity check failed (ff)"); + nodeoffs = treeroot+1; + for(j=0; j<=nvars-1; j++) { - if( i==kl ) + permimpbuf.startnodes[j] = -1; + } + prediction = 0; + while( true ) + { + if( (double)(df.trees[nodeoffs])==(double)(-1) ) { - v = 1; + prediction = df.trees[nodeoffs+1]; + break; + } + j = (int)Math.Round(df.trees[nodeoffs]); + if( permimpbuf.startnodes[j]<0 ) + { + permimpbuf.startnodes[j] = nodeoffs; + } + if( permimpbuf.xraw[j]1 ) { - fown = batch4buf[offs1]; - if( i==kl ) + j = (int)Math.Round(prediction); + permimpbuf.yv[varidx*nclasses+j] = permimpbuf.yv[varidx*nclasses+j]+1; + } + else + { + permimpbuf.yv[varidx] = permimpbuf.yv[varidx]+prediction; + } + + // + // Save loss for all variables being perturbed (XDist). + // This loss is used as a reference loss when we compute R-squared. + // + varidx = nvars; + for(j=0; j<=nclasses-1; j++) + { + permimpbuf.y[j] = 0; + } + dfprocessinternaluncompressed(df, treeroot, treeroot+1, permimpbuf.xdist, ref permimpbuf.y, _params); + for(j=0; j<=nclasses-1; j++) + { + permimpbuf.yv[varidx*nclasses+j] = permimpbuf.yv[varidx*nclasses+j]+permimpbuf.y[j]; + } + + // + // Compute losses for variable #VarIdx being perturbed. Quite an often decision + // process does not actually depend on the variable #VarIdx (path from the tree + // root does not include splits on this variable). In such cases we perform + // quick exit from the loop with precomputed value. + // + for(j=0; j<=nvars-1; j++) + { + permimpbuf.xcur[j] = permimpbuf.xraw[j]; + } + for(varidx=0; varidx<=nvars-1; varidx++) + { + if( permimpbuf.startnodes[varidx]>=0 ) { - deown = fown/s-1; + + // + // Path from tree root to the final leaf involves split on variable #VarIdx. + // Restart computation from the position first split on #VarIdx. + // + alglib.ap.assert(df.forestformat==dfuncompressedv0, "EstimateVariableImportance: integrity check failed (ff)"); + permimpbuf.xcur[varidx] = permimpbuf.xdist[varidx]; + nodeoffs = permimpbuf.startnodes[varidx]; + while( true ) + { + if( (double)(df.trees[nodeoffs])==(double)(-1) ) + { + if( nclasses>1 ) + { + j = (int)Math.Round(df.trees[nodeoffs+1]); + permimpbuf.yv[varidx*nclasses+j] = permimpbuf.yv[varidx*nclasses+j]+1; + } + else + { + permimpbuf.yv[varidx] = permimpbuf.yv[varidx]+df.trees[nodeoffs+1]; + } + break; + } + j = (int)Math.Round(df.trees[nodeoffs]); + if( permimpbuf.xcur[j]1 ) + { + j = (int)Math.Round(prediction); + permimpbuf.yv[varidx*nclasses+j] = permimpbuf.yv[varidx*nclasses+j]+1; + } + else + { + permimpbuf.yv[varidx] = permimpbuf.yv[varidx]+prediction; + } } - batch4buf[offs2] = deown; - vv = vv+deown*fown; - e = e+deown*deown/2; - offs1 = offs1+chunksize; - offs2 = offs2+chunksize; - } - offs1 = entrysize*ntotal+k; - offs2 = entrysize*ntotal+nout*chunksize+k; - for(i=0; i<=nout-1; i++) - { - fown = batch4buf[offs1]; - deown = batch4buf[offs2]; - batch4buf[(ntotal-nout+i)*entrysize+derroroffs+k] = (-vv+deown*fown+deown*(s-fown))*fown/math.sqr(s); - offs1 = offs1+chunksize; - offs2 = offs2+chunksize; } + + // + // update OOB counter + // + apserv.inc(ref oobcounts, _params); } + treeroot = treeroot+(int)Math.Round(df.trees[treeroot]); } - } - else - { // - // Regression network with sum-of-squares function. - // - // For each NOut of last neurons: - // * calculate difference between actual and desired output - // * calculate dError/dOut for this neuron (proportional to difference) - // * store in in last 4 components of entry (these values are used - // to start backpropagation) - // * update error + // Now YV[] stores NPerm versions of the forest output for various permutations of variable values. + // Update losses. // - for(i=0; i<=nout-1; i++) + for(j=0; j<=nperm-1; j++) { - v0 = network.columnsigmas[nin+i]; - v1 = network.columnmeans[nin+i]; - entryoffs = entrysize*(ntotal-nout+i); - offs0 = entryoffs; - offs1 = entryoffs+derroroffs; - for(j=0; j<=csize-1; j++) + for(k=0; k<=nclasses-1; k++) { - v = batch4buf[offs0+j]*v0+v1-xy[cstart+j,nin+i]; - batch4buf[offs1+j] = v*v0; - e = e+v*v/2; + permimpbuf.yv[j*nclasses+k] = permimpbuf.yv[j*nclasses+k]/apserv.coalesce(oobcounts, 1, _params); + } + v = 0; + for(k=0; k<=nclasses-1; k++) + { + v = v+math.sqr(permimpbuf.yv[j*nclasses+k]-permimpbuf.targety[k]); } + permimpbuf.losses[j] = permimpbuf.losses[j]+v; } + + // + // Update progress indicator + // + apserv.threadunsafeincby(ref s.rdfprogress, ntrees, _params); } // - // Backpropagation + // Recycle buffer object with updated Losses[] field // - for(neuronidx=ntotal-1; neuronidx>=0; neuronidx--) - { - entryoffs = entrysize*neuronidx; - offs = istart+neuronidx*nfieldwidth; - neurontype = network.structinfo[offs+0]; - if( neurontype>0 || neurontype==-5 ) - { - - // - // Activation function - // - srcneuronidx = network.structinfo[offs+2]; - srcentryoffs = entrysize*srcneuronidx; - offs0 = srcentryoffs+derroroffs; - offs1 = entryoffs+derroroffs; - offs2 = entryoffs+dfoffs; - batch4buf[offs0+0] = batch4buf[offs0+0]+batch4buf[offs1+0]*batch4buf[offs2+0]; - batch4buf[offs0+1] = batch4buf[offs0+1]+batch4buf[offs1+1]*batch4buf[offs2+1]; - batch4buf[offs0+2] = batch4buf[offs0+2]+batch4buf[offs1+2]*batch4buf[offs2+2]; - batch4buf[offs0+3] = batch4buf[offs0+3]+batch4buf[offs1+3]*batch4buf[offs2+3]; - continue; - } - if( neurontype==0 ) - { - - // - // Adaptive summator - // - nweights = network.structinfo[offs+1]; - srcneuronidx = network.structinfo[offs+2]; - srcentryoffs = entrysize*srcneuronidx; - srcweightidx = network.structinfo[offs+3]; - v0 = batch4buf[entryoffs+derroroffs+0]; - v1 = batch4buf[entryoffs+derroroffs+1]; - v2 = batch4buf[entryoffs+derroroffs+2]; - v3 = batch4buf[entryoffs+derroroffs+3]; - for(j=0; j<=nweights-1; j++) - { - offs0 = srcentryoffs; - offs1 = srcentryoffs+derroroffs; - v = network.weights[srcweightidx]; - hpcbuf[srcweightidx] = hpcbuf[srcweightidx]+batch4buf[offs0+0]*v0+batch4buf[offs0+1]*v1+batch4buf[offs0+2]*v2+batch4buf[offs0+3]*v3; - batch4buf[offs1+0] = batch4buf[offs1+0]+v*v0; - batch4buf[offs1+1] = batch4buf[offs1+1]+v*v1; - batch4buf[offs1+2] = batch4buf[offs1+2]+v*v2; - batch4buf[offs1+3] = batch4buf[offs1+3]+v*v3; - srcentryoffs = srcentryoffs+entrysize; - srcweightidx = srcweightidx+1; - } - continue; - } - if( neurontype<0 ) - { - bflag = false; - if( (neurontype==-2 || neurontype==-3) || neurontype==-4 ) - { - - // - // Special neuron type, no back-propagation required - // - bflag = true; - } - alglib.ap.assert(bflag, "MLPInternalCalculateGradient: unknown neuron type!"); - continue; - } - } + alglib.smp.ae_shared_pool_recycle(permpool, ref permimpbuf); } - private static void mlpchunkedprocess(multilayerperceptron network, - double[,] xy, - int cstart, - int csize, - double[] batch4buf, - double[] hpcbuf, + /************************************************************************* + Serial stub for GPL edition. + *************************************************************************/ + public static bool _trypexec_estimatepermutationimportances(decisionforestbuilder s, + decisionforest df, + int ntrees, + alglib.smp.shared_pool permpool, + int idx0, + int idx1, alglib.xparams _params) + { + return false; + } + + + /************************************************************************* + Sets report fields to their default values + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + private static void cleanreport(decisionforestbuilder s, + dfreport rep, alglib.xparams _params) { int i = 0; - int j = 0; - int ntotal = 0; - int nin = 0; - int nout = 0; - int offs = 0; - double f = 0; - double df = 0; - double d2f = 0; - double v = 0; - bool bflag = new bool(); - int istart = 0; - int entrysize = 0; - int entryoffs = 0; - int neuronidx = 0; - int srcentryoffs = 0; - int srcneuronidx = 0; - int srcweightidx = 0; - int neurontype = 0; - int nweights = 0; - int offs0 = 0; - double v0 = 0; - double v1 = 0; - double v2 = 0; - double v3 = 0; - double s0 = 0; - double s1 = 0; - double s2 = 0; - double s3 = 0; - int chunksize = 0; - chunksize = 4; - alglib.ap.assert(csize<=chunksize, "MLPChunkedProcess: internal error (CSize>ChunkSize)"); - - // - // Try to use HPC core, if possible - // - if( hpccores.hpcchunkedprocess(network.weights, network.structinfo, network.columnmeans, network.columnsigmas, xy, cstart, csize, batch4buf, hpcbuf, _params) ) + rep.relclserror = 0; + rep.avgce = 0; + rep.rmserror = 0; + rep.avgerror = 0; + rep.avgrelerror = 0; + rep.oobrelclserror = 0; + rep.oobavgce = 0; + rep.oobrmserror = 0; + rep.oobavgerror = 0; + rep.oobavgrelerror = 0; + rep.topvars = new int[s.nvars]; + rep.varimportances = new double[s.nvars]; + for(i=0; i<=s.nvars-1; i++) { - return; + rep.topvars[i] = i; + rep.varimportances[i] = 0; } + } + + + /************************************************************************* + This function returns NRMS2 loss (sum of squared residuals) for a constant- + output model: + * model output is a mean over TRN set being passed (for classification + problems - NClasses-dimensional vector of class probabilities) + * model is evaluated over TST set being passed, with L2 loss being returned + + Input parameters: + NClasses - ">1" for classification, "=1" for regression + TrnLabelsI - training set labels, class indexes (for NClasses>1) + TrnLabelsR - training set output values (for NClasses=1) + TrnIdx0, TrnIdx1 - a range [Idx0,Idx1) of elements in LabelsI/R is considered + TstLabelsI - training set labels, class indexes (for NClasses>1) + TstLabelsR - training set output values (for NClasses=1) + TstIdx0, TstIdx1 - a range [Idx0,Idx1) of elements in LabelsI/R is considered + TmpI - temporary array, reallocated as needed - // - // Read network geometry, prepare data - // - nin = network.structinfo[1]; - nout = network.structinfo[2]; - ntotal = network.structinfo[3]; - istart = network.structinfo[5]; - entrysize = 4; - - // - // Fill Batch4Buf by zeros. - // - // THIS STAGE IS VERY IMPORTANT! - // - // We fill all components of entry - neuron values, dF/dNET, dError/dF. - // It allows us to easily handle situations when CSize=2 it coincides with Gini impurity times (Idx1-Idx0) + + Following fields of WorkBuf are used as temporaries: + * TmpMeanNRMS2 + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + private static double meannrms2(int nclasses, + int[] trnlabelsi, + double[] trnlabelsr, + int trnidx0, + int trnidx1, + int[] tstlabelsi, + double[] tstlabelsr, + int tstidx0, + int tstidx1, + ref int[] tmpi, + alglib.xparams _params) + { + double result = 0; + int i = 0; + int k = 0; + int ntrn = 0; + int ntst = 0; + double v = 0; + double vv = 0; + double invntrn = 0; + double pitrn = 0; + double nitst = 0; + + alglib.ap.assert(trnidx0<=trnidx1, "MeanNRMS2: integrity check failed (8754)"); + alglib.ap.assert(tstidx0<=tstidx1, "MeanNRMS2: integrity check failed (8754)"); + result = 0; + ntrn = trnidx1-trnidx0; + ntst = tstidx1-tstidx0; + if( ntrn==0 || ntst==0 ) { - batch4buf[i] = 0; + return result; } - - // - // Forward pass: - // 1. Load data into Batch4Buf. If CSize1 ) { - entryoffs = entrysize*i; - for(j=0; j<=csize-1; j++) + + // + // Classification problem + // + apserv.ivectorsetlengthatleast(ref tmpi, 2*nclasses, _params); + for(i=0; i<=2*nclasses-1; i++) { - if( (double)(network.columnsigmas[i])!=(double)(0) ) - { - batch4buf[entryoffs+j] = (xy[cstart+j,i]-network.columnmeans[i])/network.columnsigmas[i]; - } - else - { - batch4buf[entryoffs+j] = xy[cstart+j,i]-network.columnmeans[i]; - } + tmpi[i] = 0; } - } - for(neuronidx=0; neuronidx<=ntotal-1; neuronidx++) - { - entryoffs = entrysize*neuronidx; - offs = istart+neuronidx*nfieldwidth; - neurontype = network.structinfo[offs+0]; - if( neurontype>0 || neurontype==-5 ) + for(i=trnidx0; i<=trnidx1-1; i++) { - - // - // "activation function" neuron, which takes value of neuron SrcNeuronIdx - // and applies activation function to it. - // - // This neuron has no weights and no tunable parameters. - // - srcneuronidx = network.structinfo[offs+2]; - srcentryoffs = entrysize*srcneuronidx; - mlpactivationfunction(batch4buf[srcentryoffs+0], neurontype, ref f, ref df, ref d2f, _params); - batch4buf[entryoffs+0] = f; - mlpactivationfunction(batch4buf[srcentryoffs+1], neurontype, ref f, ref df, ref d2f, _params); - batch4buf[entryoffs+1] = f; - mlpactivationfunction(batch4buf[srcentryoffs+2], neurontype, ref f, ref df, ref d2f, _params); - batch4buf[entryoffs+2] = f; - mlpactivationfunction(batch4buf[srcentryoffs+3], neurontype, ref f, ref df, ref d2f, _params); - batch4buf[entryoffs+3] = f; - continue; + k = trnlabelsi[i]; + tmpi[k] = tmpi[k]+1; } - if( neurontype==0 ) + for(i=tstidx0; i<=tstidx1-1; i++) { - - // - // "adaptive summator" neuron, whose output is a weighted sum of inputs. - // It has weights, but has no activation function. - // - nweights = network.structinfo[offs+1]; - srcneuronidx = network.structinfo[offs+2]; - srcentryoffs = entrysize*srcneuronidx; - srcweightidx = network.structinfo[offs+3]; - v0 = 0; - v1 = 0; - v2 = 0; - v3 = 0; - for(j=0; j<=nweights-1; j++) - { - v = network.weights[srcweightidx]; - srcweightidx = srcweightidx+1; - v0 = v0+v*batch4buf[srcentryoffs+0]; - v1 = v1+v*batch4buf[srcentryoffs+1]; - v2 = v2+v*batch4buf[srcentryoffs+2]; - v3 = v3+v*batch4buf[srcentryoffs+3]; - srcentryoffs = srcentryoffs+entrysize; - } - batch4buf[entryoffs+0] = v0; - batch4buf[entryoffs+1] = v1; - batch4buf[entryoffs+2] = v2; - batch4buf[entryoffs+3] = v3; - continue; + k = tstlabelsi[i]; + tmpi[k+nclasses] = tmpi[k+nclasses]+1; } - if( neurontype<0 ) + for(i=0; i<=nclasses-1; i++) { - bflag = false; - if( neurontype==-2 ) - { - - // - // Input neuron, left unchanged - // - bflag = true; - } - if( neurontype==-3 ) - { - - // - // "-1" neuron - // - batch4buf[entryoffs+0] = -1; - batch4buf[entryoffs+1] = -1; - batch4buf[entryoffs+2] = -1; - batch4buf[entryoffs+3] = -1; - bflag = true; - } - if( neurontype==-4 ) - { - - // - // "0" neuron - // - batch4buf[entryoffs+0] = 0; - batch4buf[entryoffs+1] = 0; - batch4buf[entryoffs+2] = 0; - batch4buf[entryoffs+3] = 0; - bflag = true; - } - alglib.ap.assert(bflag, "MLPChunkedProcess: internal error - unknown neuron type!"); - continue; + pitrn = tmpi[i]*invntrn; + nitst = tmpi[i+nclasses]; + result = result+nitst*(1-pitrn)*(1-pitrn); + result = result+(ntst-nitst)*pitrn*pitrn; + } + } + else + { + + // + // regression-specific code + // + v = 0; + for(i=trnidx0; i<=trnidx1-1; i++) + { + v = v+trnlabelsr[i]; + } + v = v*invntrn; + for(i=tstidx0; i<=tstidx1-1; i++) + { + vv = tstlabelsr[i]-v; + result = result+vv*vv; } } + return result; + } + + + /************************************************************************* + This function is a part of the recurrent tree construction function; it + selects variable for splitting according to current tree construction + algorithm. + + Note: modifies VarsInPool, may decrease it if some variables become non-informative + and leave the pool. + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey + *************************************************************************/ + private static void choosecurrentsplitdense(decisionforestbuilder s, + dfworkbuf workbuf, + ref int varsinpool, + int varstoselect, + hqrnd.hqrndstate rs, + int idx0, + int idx1, + ref int varbest, + ref double splitbest, + alglib.xparams _params) + { + int npoints = 0; + double errbest = 0; + int varstried = 0; + int varcur = 0; + bool valuesaresame = new bool(); + int offs = 0; + double split = 0; + int i = 0; + double v = 0; + double v0 = 0; + double currms = 0; + int info = 0; + + varbest = 0; + splitbest = 0; + + alglib.ap.assert(s.dstype==0, "sparsity is not supported 4terg!"); + alglib.ap.assert(s.rdfalgo==0, "BuildRandomTreeRec: integrity check failed (1657)"); + alglib.ap.assert(idx0v0 ) - { - v0 = v; - } - v = batch4buf[entryoffs+1]; - if( v>v1 ) - { - v1 = v; - } - v = batch4buf[entryoffs+2]; - if( v>v2 ) - { - v2 = v; - } - v = batch4buf[entryoffs+3]; - if( v>v3 ) - { - v3 = v; - } - entryoffs = entryoffs+entrysize; + v = s.dsdata[offs+workbuf.trnset[i]]; + workbuf.curvals[i] = v; + valuesaresame = valuesaresame && v==v0; } - - // - // Then, calculate exponentials and place them to part of the - // array which is located past the last entry. We also - // calculate sum of exponentials. - // - entryoffs = entrysize*(ntotal-nout); - offs0 = entrysize*ntotal; - s0 = 0; - s1 = 0; - s2 = 0; - s3 = 0; - for(i=0; i<=nout-1; i++) + if( valuesaresame ) { - v = Math.Exp(batch4buf[entryoffs+0]-v0); - s0 = s0+v; - batch4buf[offs0+0] = v; - v = Math.Exp(batch4buf[entryoffs+1]-v1); - s1 = s1+v; - batch4buf[offs0+1] = v; - v = Math.Exp(batch4buf[entryoffs+2]-v2); - s2 = s2+v; - batch4buf[offs0+2] = v; - v = Math.Exp(batch4buf[entryoffs+3]-v3); - s3 = s3+v; - batch4buf[offs0+3] = v; - entryoffs = entryoffs+entrysize; - offs0 = offs0+chunksize; + + // + // Variable does not change across current subset. + // Exclude variable from pool, go to the next iteration. + // VarsTried is not increased. + // + // NOTE: it is essential that updated VarsInPool is passed + // down to children but not up to caller - it is + // possible that one level higher this variable is + // not-fixed. + // + apserv.swapelementsi(workbuf.varpool, varstried, varsinpool-1, _params); + varsinpool = varsinpool-1; + continue; } // - // Write SOFTMAX-normalized values to the output array. + // Now we are ready to infer the split // - offs0 = entrysize*ntotal; - for(i=0; i<=nout-1; i++) + evaluatedensesplit(s, workbuf, rs, varcur, idx0, idx1, ref info, ref split, ref currms, _params); + if( info>0 && (varbest<0 || (double)(currms)<=(double)(errbest)) ) { - if( csize>0 ) - { - xy[cstart+0,nin+i] = batch4buf[offs0+0]/s0; - } - if( csize>1 ) - { - xy[cstart+1,nin+i] = batch4buf[offs0+1]/s1; - } - if( csize>2 ) - { - xy[cstart+2,nin+i] = batch4buf[offs0+2]/s2; - } - if( csize>3 ) + errbest = currms; + varbest = varcur; + splitbest = split; + for(i=idx0; i<=idx1-1; i++) { - xy[cstart+3,nin+i] = batch4buf[offs0+3]/s3; + workbuf.bestvals[i] = workbuf.curvals[i]; } - offs0 = offs0+chunksize; } - } - else - { // - // Regression network with sum-of-squares function. - // - // For each NOut of last neurons: - // * calculate difference between actual and desired output - // * calculate dError/dOut for this neuron (proportional to difference) - // * store in in last 4 components of entry (these values are used - // to start backpropagation) - // * update error + // Next iteration // - for(i=0; i<=nout-1; i++) - { - v0 = network.columnsigmas[nin+i]; - v1 = network.columnmeans[nin+i]; - entryoffs = entrysize*(ntotal-nout+i); - for(j=0; j<=csize-1; j++) - { - xy[cstart+j,nin+i] = batch4buf[entryoffs+j]*v0+v1; - } - } + varstried = varstried+1; } } /************************************************************************* - Returns T*Ln(T/Z), guarded against overflow/underflow. - Internal subroutine. + This function performs split on some specific dense variable whose values + are stored in WorkBuf.CurVals[Idx0,Idx1) and labels are stored in + WorkBuf.TrnLabelsR/I[Idx0,Idx1). + + It returns split value and associated RMS error. It is responsibility of + the caller to make sure that variable has at least two distinct values, + i.e. it is possible to make a split. + + Precomputed values of following fields of WorkBuf are used: + * ClassPriors + + Following fields of WorkBuf are used as temporaries: + * ClassTotals0,1,01 + * Tmp0I, Tmp1I, Tmp0R, Tmp1R, Tmp2R, Tmp3R + + -- ALGLIB -- + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - private static double safecrossentropy(double t, - double z, + private static void evaluatedensesplit(decisionforestbuilder s, + dfworkbuf workbuf, + hqrnd.hqrndstate rs, + int splitvar, + int idx0, + int idx1, + ref int info, + ref double split, + ref double rms, alglib.xparams _params) { - double result = 0; - double r = 0; + int nclasses = 0; + int i = 0; + int j = 0; + int k0 = 0; + int k1 = 0; + double v = 0; + double v0 = 0; + double v1 = 0; + double v2 = 0; + int sl = 0; + int sr = 0; - if( (double)(t)==(double)(0) ) - { - result = 0; - } - else + info = 0; + split = 0; + rms = 0; + + alglib.ap.assert(idx0(double)(1) ) + + // + // Try simple binary split, if possible + // Split can be inferred from minimum/maximum values, just calculate RMS error + // + info = 1; + split = getsplit(s, s.dsmin[splitvar], s.dsmax[splitvar], rs, _params); + if( nclasses>1 ) { // - // Shouldn't be the case with softmax, - // but we just want to be sure. + // Classification problem // - if( (double)(t/z)==(double)(0) ) + for(j=0; j<=nclasses-1; j++) { - r = math.minrealnumber; + workbuf.classtotals0[j] = 0; + } + sl = 0; + for(i=idx0; i<=idx1-1; i++) + { + if( workbuf.curvals[i]1 ) + { + for(i=0; i<=idx1-idx0-1; i++) { - r = t/z; + workbuf.tmp0r[i] = workbuf.curvals[idx0+i]; + workbuf.tmp0i[i] = workbuf.trnlabelsi[idx0+i]; } + classifiersplit(s, workbuf, ref workbuf.tmp0r, ref workbuf.tmp0i, idx1-idx0, rs, ref info, ref split, ref rms, ref workbuf.tmp1r, ref workbuf.tmp1i, _params); } else { - - // - // Normal case - // - if( (double)(z)==(double)(0) || (double)(Math.Abs(t))>=(double)(math.maxrealnumber*Math.Abs(z)) ) - { - r = math.maxrealnumber; - } - else + for(i=0; i<=idx1-idx0-1; i++) { - r = t/z; + workbuf.tmp0r[i] = workbuf.curvals[idx0+i]; + workbuf.tmp1r[i] = workbuf.trnlabelsr[idx0+i]; } + regressionsplit(s, workbuf, ref workbuf.tmp0r, ref workbuf.tmp1r, idx1-idx0, ref info, ref split, ref rms, ref workbuf.tmp2r, ref workbuf.tmp3r, _params); } - result = t*Math.Log(r); } - return result; } /************************************************************************* - This function performs backward pass of neural network randimization: - * it assumes that Network.Weights stores standard deviation of weights - (weights are not generated yet, only their deviations are present) - * it sets deviations of weights which feed NeuronIdx-th neuron to specified value - * it recursively passes to deeper neuron and modifies their weights - * it stops after encountering nonlinear neurons, linear activation function, - input neurons, "0" and "-1" neurons - - -- ALGLIB -- - Copyright 27.06.2013 by Bochkanov Sergey + Classifier split *************************************************************************/ - private static void randomizebackwardpass(multilayerperceptron network, - int neuronidx, - double v, + private static void classifiersplit(decisionforestbuilder s, + dfworkbuf workbuf, + ref double[] x, + ref int[] c, + int n, + hqrnd.hqrndstate rs, + ref int info, + ref double threshold, + ref double e, + ref double[] sortrbuf, + ref int[] sortibuf, alglib.xparams _params) { - int istart = 0; - int neurontype = 0; - int n1 = 0; - int n2 = 0; - int w1 = 0; - int w2 = 0; - int offs = 0; int i = 0; + int j = 0; + int k = 0; + int n0 = 0; + int n0prev = 0; + double v = 0; + int advanceby = 0; + double rms = 0; + int k0 = 0; + int k1 = 0; + double v0 = 0; + double v1 = 0; + int nclasses = 0; + double vmin = 0; + double vmax = 0; - istart = network.structinfo[5]; - neurontype = network.structinfo[istart+neuronidx*nfieldwidth+0]; - if( neurontype==-2 ) + info = 0; + threshold = 0; + e = 0; + + alglib.ap.assert((s.rdfsplitstrength==0 || s.rdfsplitstrength==1) || s.rdfsplitstrength==2, "RDF: unexpected split type at ClassifierSplit()"); + nclasses = s.nclasses; + advanceby = 1; + if( n>=20 ) + { + advanceby = Math.Max(2, (int)Math.Round(n*0.05)); + } + info = -1; + threshold = 0; + e = math.maxrealnumber; + + // + // Random split + // + if( s.rdfsplitstrength==0 ) { // - // Input neuron - stop + // Evaluate minimum, maximum and randomly selected values // - return; - } - if( neurontype==-3 ) - { + vmin = x[0]; + vmax = x[0]; + for(i=1; i<=n-1; i++) + { + v = x[i]; + if( vvmax ) + { + vmax = v; + } + } + if( (double)(vmin)==(double)(vmax) ) + { + return; + } + v = x[hqrnd.hqrnduniformi(rs, n, _params)]; + if( (double)(v)==(double)(vmin) ) + { + v = vmax; + } // - // "-1" neuron: stop + // Calculate RMS error associated with the split // + for(i=0; i<=nclasses-1; i++) + { + workbuf.classtotals0[i] = 0; + } + n0 = 0; + for(i=0; i<=n-1; i++) + { + if( x[i]0 && n00 && n00 ) + + // + // Strong split + // + if( s.rdfsplitstrength==2 ) { // - // Nonlinear activation function: stop + // Prepare initial split. + // Evaluate current split, prepare next one, repeat. // - return; - } - alglib.ap.assert(false, "RandomizeBackwardPass: unexpected neuron type"); - } - - - } - public class mlpe - { - /************************************************************************* - Neural networks ensemble - *************************************************************************/ - public class mlpensemble : apobject - { - public int ensemblesize; - public double[] weights; - public double[] columnmeans; - public double[] columnsigmas; - public mlpbase.multilayerperceptron network; - public double[] y; - public mlpensemble() - { - init(); - } - public override void init() - { - weights = new double[0]; - columnmeans = new double[0]; - columnsigmas = new double[0]; - network = new mlpbase.multilayerperceptron(); - y = new double[0]; - } - public override alglib.apobject make_copy() - { - mlpensemble _result = new mlpensemble(); - _result.ensemblesize = ensemblesize; - _result.weights = (double[])weights.Clone(); - _result.columnmeans = (double[])columnmeans.Clone(); - _result.columnsigmas = (double[])columnsigmas.Clone(); - _result.network = network!=null ? (mlpbase.multilayerperceptron)network.make_copy() : null; - _result.y = (double[])y.Clone(); - return _result; - } - }; - - - - - public const int mlpefirstversion = 1; - - - /************************************************************************* - Like MLPCreate0, but for ensembles. - - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpecreate0(int nin, - int nout, - int ensemblesize, - mlpensemble ensemble, - alglib.xparams _params) - { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); - - mlpbase.mlpcreate0(nin, nout, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); - } - - - /************************************************************************* - Like MLPCreate1, but for ensembles. - - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpecreate1(int nin, - int nhid, - int nout, - int ensemblesize, - mlpensemble ensemble, - alglib.xparams _params) - { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); - - mlpbase.mlpcreate1(nin, nhid, nout, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); - } - - - /************************************************************************* - Like MLPCreate2, but for ensembles. - - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpecreate2(int nin, - int nhid1, - int nhid2, - int nout, - int ensemblesize, - mlpensemble ensemble, - alglib.xparams _params) - { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); - - mlpbase.mlpcreate2(nin, nhid1, nhid2, nout, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); - } - - - /************************************************************************* - Like MLPCreateB0, but for ensembles. - - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpecreateb0(int nin, - int nout, - double b, - double d, - int ensemblesize, - mlpensemble ensemble, - alglib.xparams _params) - { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); - - mlpbase.mlpcreateb0(nin, nout, b, d, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); - } - - - /************************************************************************* - Like MLPCreateB1, but for ensembles. - - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpecreateb1(int nin, - int nhid, - int nout, - double b, - double d, - int ensemblesize, - mlpensemble ensemble, - alglib.xparams _params) - { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); - - mlpbase.mlpcreateb1(nin, nhid, nout, b, d, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); - } - - - /************************************************************************* - Like MLPCreateB2, but for ensembles. - - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpecreateb2(int nin, - int nhid1, - int nhid2, - int nout, - double b, - double d, - int ensemblesize, - mlpensemble ensemble, - alglib.xparams _params) - { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); - - mlpbase.mlpcreateb2(nin, nhid1, nhid2, nout, b, d, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); - } - - - /************************************************************************* - Like MLPCreateR0, but for ensembles. - - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpecreater0(int nin, - int nout, - double a, - double b, - int ensemblesize, - mlpensemble ensemble, - alglib.xparams _params) - { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); - - mlpbase.mlpcreater0(nin, nout, a, b, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); - } - - - /************************************************************************* - Like MLPCreateR1, but for ensembles. - - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpecreater1(int nin, - int nhid, - int nout, - double a, - double b, - int ensemblesize, - mlpensemble ensemble, - alglib.xparams _params) - { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); - - mlpbase.mlpcreater1(nin, nhid, nout, a, b, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); + for(i=0; i<=nclasses-1; i++) + { + workbuf.classtotals0[i] = 0; + } + n0 = 1; + while( n00 ) + { + e = Math.Sqrt(e/(nclasses*n)); + } + return; + } + alglib.ap.assert(false, "RDF: ClassifierSplit(), critical error"); } /************************************************************************* - Like MLPCreateR2, but for ensembles. - - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey + Regression model split *************************************************************************/ - public static void mlpecreater2(int nin, - int nhid1, - int nhid2, - int nout, - double a, - double b, - int ensemblesize, - mlpensemble ensemble, + private static void regressionsplit(decisionforestbuilder s, + dfworkbuf workbuf, + ref double[] x, + ref double[] y, + int n, + ref int info, + ref double threshold, + ref double e, + ref double[] sortrbuf, + ref double[] sortrbuf2, alglib.xparams _params) { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); - - mlpbase.mlpcreater2(nin, nhid1, nhid2, nout, a, b, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); - } - - - /************************************************************************* - Like MLPCreateC0, but for ensembles. + int i = 0; + double vmin = 0; + double vmax = 0; + double bnd01 = 0; + double bnd12 = 0; + double bnd23 = 0; + int total0 = 0; + int total1 = 0; + int total2 = 0; + int total3 = 0; + int cnt0 = 0; + int cnt1 = 0; + int cnt2 = 0; + int cnt3 = 0; + int n0 = 0; + int advanceby = 0; + double v = 0; + double v0 = 0; + double v1 = 0; + double rms = 0; + int n0prev = 0; + int k0 = 0; + int k1 = 0; - -- ALGLIB -- - Copyright 18.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpecreatec0(int nin, - int nout, - int ensemblesize, - mlpensemble ensemble, - alglib.xparams _params) - { - mlpbase.multilayerperceptron net = new mlpbase.multilayerperceptron(); + info = 0; + threshold = 0; + e = 0; - mlpbase.mlpcreatec0(nin, nout, net, _params); - mlpecreatefromnetwork(net, ensemblesize, ensemble, _params); + advanceby = 1; + if( n>=20 ) + { + advanceby = Math.Max(2, (int)Math.Round(n*0.05)); + } + + // + // Sort data + // Quick check for degeneracy + // + tsort.tagsortfastr(ref x, ref y, ref sortrbuf, ref sortrbuf2, n, _params); + v = 0.5*(x[0]+x[n-1]); + if( !((double)(x[0])<(double)(v) && (double)(v)<(double)(x[n-1])) ) + { + info = -1; + threshold = x[n-1]; + e = math.maxrealnumber; + return; + } + + // + // Prepare initial split. + // Evaluate current split, prepare next one, repeat. + // + vmin = y[0]; + vmax = y[0]; + for(i=1; i<=n-1; i++) + { + v = y[i]; + if( vvmax ) + { + vmax = v; + } + } + bnd12 = 0.5*(vmin+vmax); + bnd01 = 0.5*(vmin+bnd12); + bnd23 = 0.5*(vmax+bnd12); + total0 = 0; + total1 = 0; + total2 = 0; + total3 = 0; + for(i=0; i<=n-1; i++) + { + v = y[i]; + if( v0 ) + { + e = Math.Sqrt(e/(4*n)); + } } /************************************************************************* - Like MLPCreateC1, but for ensembles. + Returns split: either deterministic split at the middle of [A,B], or randomly + chosen split. + + It is guaranteed that A0, "MLPECreate: incorrect ensemble size!"); - - // - // Copy network - // - mlpbase.mlpcopy(network, ensemble.network, _params); + alglib.ap.assert(s.dstype==0, "no sparsity"); + npoints = s.npoints; + nvars = s.nvars; + nclasses = s.nclasses; // - // network properties + // seed local RNG // - if( mlpbase.mlpissoftmax(network, _params) ) + if( s.rdfglobalseed>0 ) { - ccount = mlpbase.mlpgetinputscount(ensemble.network, _params); + hqrnd.hqrndseed(s.rdfglobalseed, 3532, rs, _params); } else { - ccount = mlpbase.mlpgetinputscount(ensemble.network, _params)+mlpbase.mlpgetoutputscount(ensemble.network, _params); + hqrnd.hqrndseed(math.randominteger(30000), 3532, rs, _params); } - wcount = mlpbase.mlpgetweightscount(ensemble.network, _params); - ensemble.ensemblesize = ensemblesize; // - // weights, means, sigmas + // Generic processing // - ensemble.weights = new double[ensemblesize*wcount]; - ensemble.columnmeans = new double[ensemblesize*ccount]; - ensemble.columnsigmas = new double[ensemblesize*ccount]; - for(i=0; i<=ensemblesize*wcount-1; i++) + alglib.ap.assert(npoints>=1, "BuildRandomForest: integrity check failed"); + apserv.rvectorsetlengthatleast(ref s.dsmin, nvars, _params); + apserv.rvectorsetlengthatleast(ref s.dsmax, nvars, _params); + apserv.bvectorsetlengthatleast(ref s.dsbinary, nvars, _params); + for(i=0; i<=nvars-1; i++) { - ensemble.weights[i] = math.randomreal()-0.5; + v0 = s.dsdata[i*npoints+0]; + v1 = s.dsdata[i*npoints+0]; + for(j=1; j<=npoints-1; j++) + { + v = s.dsdata[i*npoints+j]; + if( vv1 ) + { + v1 = v; + } + } + s.dsmin[i] = v0; + s.dsmax[i] = v1; + alglib.ap.assert((double)(v0)<=(double)(v1), "BuildRandomForest: strange integrity check failure"); + isbinary = true; + for(j=0; j<=npoints-1; j++) + { + v = s.dsdata[i*npoints+j]; + isbinary = isbinary && (v==v0 || v==v1); + } + s.dsbinary[i] = isbinary; } - for(i=0; i<=ensemblesize-1; i++) + if( nclasses==1 ) { - i1_ = (0) - (i*ccount); - for(i_=i*ccount; i_<=(i+1)*ccount-1;i_++) + s.dsravg = 0; + for(i=0; i<=npoints-1; i++) { - ensemble.columnmeans[i_] = network.columnmeans[i_+i1_]; + s.dsravg = s.dsravg+s.dsrval[i]; } - i1_ = (0) - (i*ccount); - for(i_=i*ccount; i_<=(i+1)*ccount-1;i_++) + s.dsravg = s.dsravg/npoints; + } + else + { + apserv.ivectorsetlengthatleast(ref s.dsctotals, nclasses, _params); + for(i=0; i<=nclasses-1; i++) { - ensemble.columnsigmas[i_] = network.columnsigmas[i_+i1_]; + s.dsctotals[i] = 0; + } + for(i=0; i<=npoints-1; i++) + { + s.dsctotals[s.dsival[i]] = s.dsctotals[s.dsival[i]]+1; } } - - // - // temporaries, internal buffers - // - ensemble.y = new double[mlpbase.mlpgetoutputscount(ensemble.network, _params)]; } /************************************************************************* - Copying of MLPEnsemble strucure + This function merges together trees generated during training and outputs + it to the decision forest. INPUT PARAMETERS: - Ensemble1 - original + S - decision forest builder object + NTrees - NTrees>=1, number of trees to train OUTPUT PARAMETERS: - Ensemble2 - copy + DF - decision forest + Rep - report -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpecopy(mlpensemble ensemble1, - mlpensemble ensemble2, + private static void mergetrees(decisionforestbuilder s, + decisionforest df, alglib.xparams _params) { - int ccount = 0; - int wcount = 0; - int i_ = 0; + int i = 0; + int cursize = 0; + int offs = 0; + dftreebuf tree = null; + int[] treesizes = new int[0]; + int[] treeoffsets = new int[0]; + df.forestformat = dfuncompressedv0; + df.nvars = s.nvars; + df.nclasses = s.nclasses; + df.bufsize = 0; + df.ntrees = 0; + + // + // Determine trees count + // + alglib.smp.ae_shared_pool_first_recycled(s.treepool, ref tree); + while( tree!=null ) + { + df.ntrees = df.ntrees+1; + alglib.smp.ae_shared_pool_next_recycled(s.treepool, ref tree); + } + alglib.ap.assert(df.ntrees>0, "MergeTrees: integrity check failed, zero trees count"); // - // Unload info + // Determine individual tree sizes and total buffer size // - if( mlpbase.mlpissoftmax(ensemble1.network, _params) ) + treesizes = new int[df.ntrees]; + for(i=0; i<=df.ntrees-1; i++) { - ccount = mlpbase.mlpgetinputscount(ensemble1.network, _params); + treesizes[i] = -1; } - else + alglib.smp.ae_shared_pool_first_recycled(s.treepool, ref tree); + while( tree!=null ) { - ccount = mlpbase.mlpgetinputscount(ensemble1.network, _params)+mlpbase.mlpgetoutputscount(ensemble1.network, _params); + alglib.ap.assert(tree.treeidx>=0 && tree.treeidx0, "MergeTrees: integrity check failed (wrong TreeSize)"); } - wcount = mlpbase.mlpgetweightscount(ensemble1.network, _params); // - // Allocate space + // Determine offsets for individual trees in output buffer // - ensemble2.weights = new double[ensemble1.ensemblesize*wcount]; - ensemble2.columnmeans = new double[ensemble1.ensemblesize*ccount]; - ensemble2.columnsigmas = new double[ensemble1.ensemblesize*ccount]; - ensemble2.y = new double[mlpbase.mlpgetoutputscount(ensemble1.network, _params)]; + treeoffsets = new int[df.ntrees]; + treeoffsets[0] = 0; + for(i=1; i<=df.ntrees-1; i++) + { + treeoffsets[i] = treeoffsets[i-1]+treesizes[i-1]; + } // - // Copy + // Output trees // - ensemble2.ensemblesize = ensemble1.ensemblesize; - for(i_=0; i_<=ensemble1.ensemblesize*wcount-1;i_++) - { - ensemble2.weights[i_] = ensemble1.weights[i_]; - } - for(i_=0; i_<=ensemble1.ensemblesize*ccount-1;i_++) - { - ensemble2.columnmeans[i_] = ensemble1.columnmeans[i_]; - } - for(i_=0; i_<=ensemble1.ensemblesize*ccount-1;i_++) - { - ensemble2.columnsigmas[i_] = ensemble1.columnsigmas[i_]; - } - mlpbase.mlpcopy(ensemble1.network, ensemble2.network, _params); - } - - - /************************************************************************* - Randomization of MLP ensemble - - -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlperandomize(mlpensemble ensemble, - alglib.xparams _params) - { - int i = 0; - int wcount = 0; - - wcount = mlpbase.mlpgetweightscount(ensemble.network, _params); - for(i=0; i<=ensemble.ensemblesize*wcount-1; i++) + // NOTE: since ALGLIB 3.16.0 trees are sorted by tree index prior to + // output (necessary for variable importance estimation), that's + // why we need array of tree offsets + // + df.trees = new double[df.bufsize]; + alglib.smp.ae_shared_pool_first_recycled(s.treepool, ref tree); + while( tree!=null ) { - ensemble.weights[i] = math.randomreal()-0.5; + cursize = (int)Math.Round(tree.treebuf[0]); + offs = treeoffsets[tree.treeidx]; + for(i=0; i<=cursize-1; i++) + { + df.trees[offs+i] = tree.treebuf[i]; + } + alglib.smp.ae_shared_pool_next_recycled(s.treepool, ref tree); } } /************************************************************************* - Return ensemble properties (number of inputs and outputs). - - -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static void mlpeproperties(mlpensemble ensemble, - ref int nin, - ref int nout, - alglib.xparams _params) - { - nin = 0; - nout = 0; - - nin = mlpbase.mlpgetinputscount(ensemble.network, _params); - nout = mlpbase.mlpgetoutputscount(ensemble.network, _params); - } - - - /************************************************************************* - Return normalization type (whether ensemble is SOFTMAX-normalized or not). - - -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey - *************************************************************************/ - public static bool mlpeissoftmax(mlpensemble ensemble, - alglib.xparams _params) - { - bool result = new bool(); - - result = mlpbase.mlpissoftmax(ensemble.network, _params); - return result; - } - - - /************************************************************************* - Procesing + This function post-processes voting array and calculates TRN and OOB errors. INPUT PARAMETERS: - Ensemble- neural networks ensemble - X - input vector, array[0..NIn-1]. - Y - (possibly) preallocated buffer; if size of Y is less than - NOut, it will be reallocated. If it is large enough, it - is NOT reallocated, so we can save some time on reallocation. - + S - decision forest builder object + NTrees - number of trees in the forest + Buf - possibly preallocated vote buffer, its contents is + overwritten by this function OUTPUT PARAMETERS: - Y - result. Regression estimate when solving regression task, - vector of posterior probabilities for classification task. + Rep - report fields corresponding to errors are updated -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 21.05.2018 by Bochkanov Sergey *************************************************************************/ - public static void mlpeprocess(mlpensemble ensemble, - double[] x, - ref double[] y, + private static void processvotingresults(decisionforestbuilder s, + int ntrees, + dfvotebuf buf, + dfreport rep, alglib.xparams _params) { + dfvotebuf vote = null; + int nvars = 0; + int nclasses = 0; + int npoints = 0; int i = 0; - int es = 0; - int wc = 0; - int cc = 0; + int j = 0; + int k = 0; + int k1 = 0; double v = 0; - int nout = 0; - int i_ = 0; - int i1_ = 0; + int avgrelcnt = 0; + int oobavgrelcnt = 0; - if( alglib.ap.len(y)0, "DFOREST: integrity check failed"); + alglib.ap.assert(nvars>0, "DFOREST: integrity check failed"); + alglib.ap.assert(nclasses>0, "DFOREST: integrity check failed"); + + // + // Prepare vote buffer + // + apserv.rvectorsetlengthatleast(ref buf.trntotals, npoints*nclasses, _params); + apserv.rvectorsetlengthatleast(ref buf.oobtotals, npoints*nclasses, _params); + for(i=0; i<=npoints*nclasses-1; i++) { - cc = mlpbase.mlpgetinputscount(ensemble.network, _params); + buf.trntotals[i] = 0; + buf.oobtotals[i] = 0; } - else + apserv.ivectorsetlengthatleast(ref buf.trncounts, npoints, _params); + apserv.ivectorsetlengthatleast(ref buf.oobcounts, npoints, _params); + for(i=0; i<=npoints-1; i++) { - cc = mlpbase.mlpgetinputscount(ensemble.network, _params)+mlpbase.mlpgetoutputscount(ensemble.network, _params); + buf.trncounts[i] = 0; + buf.oobcounts[i] = 0; } - v = (double)1/(double)es; - nout = mlpbase.mlpgetoutputscount(ensemble.network, _params); - for(i=0; i<=nout-1; i++) + + // + // Merge voting arrays + // + alglib.smp.ae_shared_pool_first_recycled(s.votepool, ref vote); + while( vote!=null ) { - y[i] = 0; + for(i=0; i<=npoints*nclasses-1; i++) + { + buf.trntotals[i] = buf.trntotals[i]+vote.trntotals[i]+vote.oobtotals[i]; + buf.oobtotals[i] = buf.oobtotals[i]+vote.oobtotals[i]; + } + for(i=0; i<=npoints-1; i++) + { + buf.trncounts[i] = buf.trncounts[i]+vote.trncounts[i]+vote.oobcounts[i]; + buf.oobcounts[i] = buf.oobcounts[i]+vote.oobcounts[i]; + } + alglib.smp.ae_shared_pool_next_recycled(s.votepool, ref vote); } - for(i=0; i<=es-1; i++) + for(i=0; i<=npoints-1; i++) { - i1_ = (i*wc) - (0); - for(i_=0; i_<=wc-1;i_++) + v = 1/apserv.coalesce(buf.trncounts[i], 1, _params); + for(j=0; j<=nclasses-1; j++) { - ensemble.network.weights[i_] = ensemble.weights[i_+i1_]; + buf.trntotals[i*nclasses+j] = buf.trntotals[i*nclasses+j]*v; } - i1_ = (i*cc) - (0); - for(i_=0; i_<=cc-1;i_++) + v = 1/apserv.coalesce(buf.oobcounts[i], 1, _params); + for(j=0; j<=nclasses-1; j++) { - ensemble.network.columnmeans[i_] = ensemble.columnmeans[i_+i1_]; + buf.oobtotals[i*nclasses+j] = buf.oobtotals[i*nclasses+j]*v; } - i1_ = (i*cc) - (0); - for(i_=0; i_<=cc-1;i_++) + } + + // + // Use aggregated voting data to output error metrics + // + avgrelcnt = 0; + oobavgrelcnt = 0; + rep.rmserror = 0; + rep.avgerror = 0; + rep.avgrelerror = 0; + rep.relclserror = 0; + rep.avgce = 0; + rep.oobrmserror = 0; + rep.oobavgerror = 0; + rep.oobavgrelerror = 0; + rep.oobrelclserror = 0; + rep.oobavgce = 0; + for(i=0; i<=npoints-1; i++) + { + if( nclasses>1 ) { - ensemble.network.columnsigmas[i_] = ensemble.columnsigmas[i_+i1_]; + + // + // classification-specific code + // + k = s.dsival[i]; + for(j=0; j<=nclasses-1; j++) + { + v = buf.trntotals[i*nclasses+j]; + if( j==k ) + { + rep.avgce = rep.avgce-Math.Log(apserv.coalesce(v, math.minrealnumber, _params)); + rep.rmserror = rep.rmserror+math.sqr(v-1); + rep.avgerror = rep.avgerror+Math.Abs(v-1); + rep.avgrelerror = rep.avgrelerror+Math.Abs(v-1); + apserv.inc(ref avgrelcnt, _params); + } + else + { + rep.rmserror = rep.rmserror+math.sqr(v); + rep.avgerror = rep.avgerror+Math.Abs(v); + } + v = buf.oobtotals[i*nclasses+j]; + if( j==k ) + { + rep.oobavgce = rep.oobavgce-Math.Log(apserv.coalesce(v, math.minrealnumber, _params)); + rep.oobrmserror = rep.oobrmserror+math.sqr(v-1); + rep.oobavgerror = rep.oobavgerror+Math.Abs(v-1); + rep.oobavgrelerror = rep.oobavgrelerror+Math.Abs(v-1); + apserv.inc(ref oobavgrelcnt, _params); + } + else + { + rep.oobrmserror = rep.oobrmserror+math.sqr(v); + rep.oobavgerror = rep.oobavgerror+Math.Abs(v); + } + } + + // + // Classification errors are handled separately + // + k1 = 0; + for(j=1; j<=nclasses-1; j++) + { + if( buf.trntotals[i*nclasses+j]>buf.trntotals[i*nclasses+k1] ) + { + k1 = j; + } + } + if( k1!=k ) + { + rep.relclserror = rep.relclserror+1; + } + k1 = 0; + for(j=1; j<=nclasses-1; j++) + { + if( buf.oobtotals[i*nclasses+j]>buf.oobtotals[i*nclasses+k1] ) + { + k1 = j; + } + } + if( k1!=k ) + { + rep.oobrelclserror = rep.oobrelclserror+1; + } } - mlpbase.mlpprocess(ensemble.network, x, ref ensemble.y, _params); - for(i_=0; i_<=nout-1;i_++) + else { - y[i_] = y[i_] + v*ensemble.y[i_]; + + // + // regression-specific code + // + v = buf.trntotals[i]-s.dsrval[i]; + rep.rmserror = rep.rmserror+math.sqr(v); + rep.avgerror = rep.avgerror+Math.Abs(v); + if( (double)(s.dsrval[i])!=(double)(0) ) + { + rep.avgrelerror = rep.avgrelerror+Math.Abs(v/s.dsrval[i]); + avgrelcnt = avgrelcnt+1; + } + v = buf.oobtotals[i]-s.dsrval[i]; + rep.oobrmserror = rep.oobrmserror+math.sqr(v); + rep.oobavgerror = rep.oobavgerror+Math.Abs(v); + if( (double)(s.dsrval[i])!=(double)(0) ) + { + rep.oobavgrelerror = rep.oobavgrelerror+Math.Abs(v/s.dsrval[i]); + oobavgrelcnt = oobavgrelcnt+1; + } } } + rep.relclserror = rep.relclserror/npoints; + rep.rmserror = Math.Sqrt(rep.rmserror/(npoints*nclasses)); + rep.avgerror = rep.avgerror/(npoints*nclasses); + rep.avgrelerror = rep.avgrelerror/apserv.coalesce(avgrelcnt, 1, _params); + rep.oobrelclserror = rep.oobrelclserror/npoints; + rep.oobrmserror = Math.Sqrt(rep.oobrmserror/(npoints*nclasses)); + rep.oobavgerror = rep.oobavgerror/(npoints*nclasses); + rep.oobavgrelerror = rep.oobavgrelerror/apserv.coalesce(oobavgrelcnt, 1, _params); } /************************************************************************* - 'interactive' variant of MLPEProcess for languages like Python which - support constructs like "Y = MLPEProcess(LM,X)" and interactive mode of the - interpreter + This function performs binary compression of decision forest, using either + 8-bit mantissa (a bit more compact representation) or 16-bit mantissa for + splits and regression outputs. - This function allocates new array on each call, so it is significantly - slower than its 'non-interactive' counterpart, but it is more convenient - when you call it from command line. + Forest is compressed in-place. + + Return value is a compression factor. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 22.07.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpeprocessi(mlpensemble ensemble, - double[] x, - ref double[] y, + private static double binarycompression(decisionforest df, + bool usemantissa8, alglib.xparams _params) { - y = new double[0]; + double result = 0; + int size8 = 0; + int size8i = 0; + int offssrc = 0; + int offsdst = 0; + int i = 0; + int[] dummyi = new int[0]; + int maxrawtreesize = 0; + int[] compressedsizes = new int[0]; - mlpeprocess(ensemble, x, ref y, _params); + + // + // Quick exit if already compressed + // + if( df.forestformat==dfcompressedv0 ) + { + result = 1; + return result; + } + + // + // Check that source format is supported + // + alglib.ap.assert(df.forestformat==dfuncompressedv0, "BinaryCompression: unexpected forest format"); + + // + // Compute sizes of uncompressed and compressed trees. + // + size8 = 0; + offssrc = 0; + maxrawtreesize = 0; + for(i=0; i<=df.ntrees-1; i++) + { + size8i = computecompressedsizerec(df, usemantissa8, offssrc, offssrc+1, dummyi, false, _params); + size8 = size8+computecompresseduintsize(size8i, _params)+size8i; + maxrawtreesize = Math.Max(maxrawtreesize, (int)Math.Round(df.trees[offssrc])); + offssrc = offssrc+(int)Math.Round(df.trees[offssrc]); + } + result = (double)(8*alglib.ap.len(df.trees))/(double)(size8+1); + + // + // Allocate memory and perform compression + // + df.trees8 = new byte[size8]; + compressedsizes = new int[maxrawtreesize]; + offssrc = 0; + offsdst = 0; + for(i=0; i<=df.ntrees-1; i++) + { + + // + // Call compressed size evaluator one more time, now saving subtree sizes into temporary array + // + size8i = computecompressedsizerec(df, usemantissa8, offssrc, offssrc+1, compressedsizes, true, _params); + + // + // Output tree header (length in bytes) + // + streamuint(df.trees8, ref offsdst, size8i, _params); + + // + // Compress recursively + // + compressrec(df, usemantissa8, offssrc, offssrc+1, compressedsizes, df.trees8, ref offsdst, _params); + + // + // Next tree + // + offssrc = offssrc+(int)Math.Round(df.trees[offssrc]); + } + alglib.ap.assert(offsdst==size8, "BinaryCompression: integrity check failed (stream length)"); + + // + // Finalize forest conversion, clear previously allocated memory + // + df.forestformat = dfcompressedv0; + df.usemantissa8 = usemantissa8; + df.trees = new double[0]; + return result; } /************************************************************************* - Calculation of all types of errors + This function returns exact number of bytes required to store compressed + version of the tree starting at location TreeBase. + + PARAMETERS: + DF - decision forest + UseMantissa8 - whether 8-bit or 16-bit mantissas are used to store + floating point numbers + TreeRoot - root of the specific tree being stored (offset in DF.Trees) + TreePos - position within tree (first location in the tree + is TreeRoot+1) + CompressedSizes - not referenced if SaveCompressedSizes is False; + otherwise, values computed by this function for + specific values of TreePos are stored to + CompressedSizes[TreePos-TreeRoot] (other elements + of the array are not referenced). + This array must be preallocated by caller. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 22.07.2019 by Bochkanov Sergey *************************************************************************/ - public static void mlpeallerrorsx(mlpensemble ensemble, - double[,] densexy, - sparse.sparsematrix sparsexy, - int datasetsize, - int datasettype, - int[] idx, - int subset0, - int subset1, - int subsettype, - alglib.smp.shared_pool buf, - mlpbase.modelerrors rep, + private static int computecompressedsizerec(decisionforest df, + bool usemantissa8, + int treeroot, + int treepos, + int[] compressedsizes, + bool savecompressedsizes, alglib.xparams _params) { - int i = 0; - int j = 0; - int nin = 0; - int nout = 0; - bool iscls = new bool(); - int srcidx = 0; - hpccores.mlpbuffers pbuf = null; - mlpbase.modelerrors rep0 = new mlpbase.modelerrors(); - mlpbase.modelerrors rep1 = new mlpbase.modelerrors(); - int i_ = 0; - int i1_ = 0; - - - // - // Get network information - // - nin = mlpbase.mlpgetinputscount(ensemble.network, _params); - nout = mlpbase.mlpgetoutputscount(ensemble.network, _params); - iscls = mlpbase.mlpissoftmax(ensemble.network, _params); - - // - // Retrieve buffer, prepare, process data, recycle buffer - // - alglib.smp.ae_shared_pool_retrieve(buf, ref pbuf); - if( iscls ) + int result = 0; + int jmponbranch = 0; + int child0size = 0; + int child1size = 0; + int fpwidth = 0; + + if( usemantissa8 ) { - bdss.dserrallocate(nout, ref pbuf.tmp0, _params); + fpwidth = 2; } else { - bdss.dserrallocate(-nout, ref pbuf.tmp0, _params); + fpwidth = 3; } - apserv.rvectorsetlengthatleast(ref pbuf.x, nin, _params); - apserv.rvectorsetlengthatleast(ref pbuf.y, nout, _params); - apserv.rvectorsetlengthatleast(ref pbuf.desiredy, nout, _params); - for(i=subset0; i<=subset1-1; i++) + + // + // Leaf or split? + // + if( (double)(df.trees[treepos])==(double)(-1) ) { - srcidx = -1; - if( subsettype==0 ) - { - srcidx = i; - } - if( subsettype==1 ) - { - srcidx = idx[i]; - } - alglib.ap.assert(srcidx>=0, "MLPEAllErrorsX: internal error"); - if( datasettype==0 ) + + // + // Leaf + // + result = computecompresseduintsize(2*df.nvars, _params); + if( df.nclasses==1 ) { - for(i_=0; i_<=nin-1;i_++) - { - pbuf.x[i_] = densexy[srcidx,i_]; - } + result = result+fpwidth; } - if( datasettype==1 ) + else { - sparse.sparsegetrow(sparsexy, srcidx, ref pbuf.x, _params); + result = result+computecompresseduintsize((int)Math.Round(df.trees[treepos+1]), _params); } - mlpeprocess(ensemble, pbuf.x, ref pbuf.y, _params); - if( mlpbase.mlpissoftmax(ensemble.network, _params) ) + } + else + { + + // + // Split + // + jmponbranch = (int)Math.Round(df.trees[treepos+2]); + child0size = computecompressedsizerec(df, usemantissa8, treeroot, treepos+innernodewidth, compressedsizes, savecompressedsizes, _params); + child1size = computecompressedsizerec(df, usemantissa8, treeroot, treeroot+jmponbranch, compressedsizes, savecompressedsizes, _params); + if( child0size<=child1size ) { - if( datasettype==0 ) - { - pbuf.desiredy[0] = densexy[srcidx,nin]; - } - if( datasettype==1 ) - { - pbuf.desiredy[0] = sparse.sparseget(sparsexy, srcidx, nin, _params); - } + + // + // Child #0 comes first because it is shorter + // + result = computecompresseduintsize((int)Math.Round(df.trees[treepos]), _params); + result = result+fpwidth; + result = result+computecompresseduintsize(child0size, _params); } else { - if( datasettype==0 ) - { - i1_ = (nin) - (0); - for(i_=0; i_<=nout-1;i_++) - { - pbuf.desiredy[i_] = densexy[srcidx,i_+i1_]; - } - } - if( datasettype==1 ) - { - for(j=0; j<=nout-1; j++) - { - pbuf.desiredy[j] = sparse.sparseget(sparsexy, srcidx, nin+j, _params); - } - } + + // + // Child #1 comes first because it is shorter + // + result = computecompresseduintsize((int)Math.Round(df.trees[treepos])+df.nvars, _params); + result = result+fpwidth; + result = result+computecompresseduintsize(child1size, _params); } - bdss.dserraccumulate(ref pbuf.tmp0, pbuf.y, pbuf.desiredy, _params); + result = result+child0size+child1size; } - bdss.dserrfinish(ref pbuf.tmp0, _params); - rep.relclserror = pbuf.tmp0[0]; - rep.avgce = pbuf.tmp0[1]/Math.Log(2); - rep.rmserror = pbuf.tmp0[2]; - rep.avgerror = pbuf.tmp0[3]; - rep.avgrelerror = pbuf.tmp0[4]; - alglib.smp.ae_shared_pool_recycle(buf, ref pbuf); + + // + // Do we have to save compressed sizes? + // + if( savecompressedsizes ) + { + alglib.ap.assert(treepos-treeroot=VAL then BRANCH0 else BRANCH1" + // * stream value used for splitting + // * stream children #0 and #1 + // + streamuint(buf, ref dstoffs, varidx+df.nvars, _params); + streamfloat(buf, usemantissa8, ref dstoffs, splitval, _params); + streamuint(buf, ref dstoffs, child1size, _params); + compressrec(df, usemantissa8, treeroot, treeroot+jmponbranch, compressedsizes, buf, ref dstoffs, _params); + compressrec(df, usemantissa8, treeroot, treepos+innernodewidth, compressedsizes, buf, ref dstoffs, _params); } - bdss.dserraccumulate(ref buf, y, dy, _params); } - bdss.dserrfinish(ref buf, _params); - relcls = buf[0]; - avgce = buf[1]; - rms = buf[2]; - avg = buf[3]; - avgrel = buf[4]; + + // + // Integrity check at the end + // + alglib.ap.assert(dstoffs-dstoffsold==compressedsizes[treepos-treeroot], "CompressRec: integrity check failed (compressed size at leaf)"); } /************************************************************************* - Relative classification error on the test set - - INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size - - RESULT: - percent of incorrectly classified cases. - Works both for classifier betwork and for regression networks which - are used as classifiers. + This function returns exact number of bytes required to store compressed + unsigned integer number (negative arguments result in assertion being + generated). -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 22.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlperelclserror(mlpensemble ensemble, - double[,] xy, - int npoints, + private static int computecompresseduintsize(int v, alglib.xparams _params) { - double result = 0; - mlpbase.modelerrors rep = new mlpbase.modelerrors(); + int result = 0; - mlpeallerrorsx(ensemble, xy, ensemble.network.dummysxy, npoints, 0, ensemble.network.dummyidx, 0, npoints, 0, ensemble.network.buf, rep, _params); - result = rep.relclserror; + alglib.ap.assert(v>=0); + result = 1; + while( v>=128 ) + { + v = v/128; + result = result+1; + } return result; } /************************************************************************* - Average cross-entropy (in bits per element) on the test set + This function stores compressed unsigned integer number (negative arguments + result in assertion being generated) to byte array at location Offs and + increments Offs by number of bytes being stored. - INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size + -- ALGLIB -- + Copyright 22.07.2019 by Bochkanov Sergey + *************************************************************************/ + private static void streamuint(byte[] buf, + ref int offs, + int v, + alglib.xparams _params) + { + int v0 = 0; - RESULT: - CrossEntropy/(NPoints*LN(2)). - Zero if ensemble solves regression task. + alglib.ap.assert(v>=0); + while( true ) + { + + // + // Save 7 least significant bits of V, use 8th bit as a flag which + // tells us whether subsequent 7-bit packages will be sent. + // + v0 = v%128; + if( v>=128 ) + { + v0 = v0+128; + } + buf[offs] = unchecked((byte)(v0)); + offs = offs+1; + v = v/128; + if( v==0 ) + { + break; + } + } + } + + + /************************************************************************* + This function reads compressed unsigned integer number from byte array + starting at location Offs and increments Offs by number of bytes being + read. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 22.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpeavgce(mlpensemble ensemble, - double[,] xy, - int npoints, + private static int unstreamuint(byte[] buf, + ref int offs, alglib.xparams _params) { - double result = 0; - mlpbase.modelerrors rep = new mlpbase.modelerrors(); + int result = 0; + int v0 = 0; + int p = 0; - mlpeallerrorsx(ensemble, xy, ensemble.network.dummysxy, npoints, 0, ensemble.network.dummyidx, 0, npoints, 0, ensemble.network.buf, rep, _params); - result = rep.avgce; + result = 0; + p = 1; + while( true ) + { + + // + // Rad 7 bits of V, use 8th bit as a flag which tells us whether + // subsequent 7-bit packages will be received. + // + v0 = buf[offs]; + offs = offs+1; + result = result+v0%128*p; + if( v0<128 ) + { + break; + } + p = p*128; + } return result; } /************************************************************************* - RMS error on the test set - - INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size + This function stores compressed floating point number to byte array at + location Offs and increments Offs by number of bytes being stored. - RESULT: - root mean square error. - Its meaning for regression task is obvious. As for classification task - RMS error means error when estimating posterior probabilities. + Either 8-bit mantissa or 16-bit mantissa is used. The exponent is always + 7 bits of exponent + sign. Values which do not fit into exponent range are + truncated to fit. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 22.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpermserror(mlpensemble ensemble, - double[,] xy, - int npoints, + private static void streamfloat(byte[] buf, + bool usemantissa8, + ref int offs, + double v, alglib.xparams _params) { - double result = 0; - mlpbase.modelerrors rep = new mlpbase.modelerrors(); + int signbit = 0; + int e = 0; + int m = 0; + double twopow30 = 0; + double twopowm30 = 0; + double twopow10 = 0; + double twopowm10 = 0; - mlpeallerrorsx(ensemble, xy, ensemble.network.dummysxy, npoints, 0, ensemble.network.dummyidx, 0, npoints, 0, ensemble.network.buf, rep, _params); - result = rep.rmserror; - return result; + alglib.ap.assert(math.isfinite(v), "StreamFloat: V is not finite number"); + + // + // Special case: zero + // + if( v==0.0 ) + { + if( usemantissa8 ) + { + buf[offs+0] = unchecked((byte)(0)); + buf[offs+1] = unchecked((byte)(0)); + offs = offs+2; + } + else + { + buf[offs+0] = unchecked((byte)(0)); + buf[offs+1] = unchecked((byte)(0)); + buf[offs+2] = unchecked((byte)(0)); + offs = offs+3; + } + return; + } + + // + // Handle sign + // + signbit = 0; + if( v<0.0 ) + { + v = -v; + signbit = 128; + } + + // + // Compute exponent + // + twopow30 = 1073741824; + twopow10 = 1024; + twopowm30 = 1.0/twopow30; + twopowm10 = 1.0/twopow10; + e = 0; + while( v>=twopow30 ) + { + v = v*twopowm30; + e = e+30; + } + while( v>=twopow10 ) + { + v = v*twopowm10; + e = e+10; + } + while( v>=1.0 ) + { + v = v*0.5; + e = e+1; + } + while( v=0.5 && v<1.0, "StreamFloat: integrity check failed"); + + // + // Handle exponent underflow/overflow + // + if( e<-63 ) + { + signbit = 0; + e = 0; + v = 0; + } + if( e>63 ) + { + e = 63; + v = 1.0; + } + + // + // Save to stream + // + if( usemantissa8 ) + { + m = (int)Math.Round(v*256); + if( m==256 ) + { + m = m/2; + e = Math.Min(e+1, 63); + } + buf[offs+0] = unchecked((byte)(e+64+signbit)); + buf[offs+1] = unchecked((byte)(m)); + offs = offs+2; + } + else + { + m = (int)Math.Round(v*65536); + if( m==65536 ) + { + m = m/2; + e = Math.Min(e+1, 63); + } + buf[offs+0] = unchecked((byte)(e+64+signbit)); + buf[offs+1] = unchecked((byte)(m%256)); + buf[offs+2] = unchecked((byte)(m/256)); + offs = offs+3; + } } /************************************************************************* - Average error on the test set - - INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size + This function reads compressed floating point number from the byte array + starting from location Offs and increments Offs by number of bytes being + read. - RESULT: - Its meaning for regression task is obvious. As for classification task - it means average error when estimating posterior probabilities. + Either 8-bit mantissa or 16-bit mantissa is used. The exponent is always + 7 bits of exponent + sign. Values which do not fit into exponent range are + truncated to fit. -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Copyright 22.07.2019 by Bochkanov Sergey *************************************************************************/ - public static double mlpeavgerror(mlpensemble ensemble, - double[,] xy, - int npoints, + private static double unstreamfloat(byte[] buf, + bool usemantissa8, + ref int offs, alglib.xparams _params) { double result = 0; - mlpbase.modelerrors rep = new mlpbase.modelerrors(); + int e = 0; + double v = 0; + double inv256 = 0; - mlpeallerrorsx(ensemble, xy, ensemble.network.dummysxy, npoints, 0, ensemble.network.dummyidx, 0, npoints, 0, ensemble.network.buf, rep, _params); - result = rep.avgerror; + + // + // Read from stream + // + inv256 = 1.0/256.0; + if( usemantissa8 ) + { + e = buf[offs+0]; + v = buf[offs+1]*inv256; + offs = offs+2; + } + else + { + e = buf[offs+0]; + v = (buf[offs+1]*inv256+buf[offs+2])*inv256; + offs = offs+3; + } + + // + // Decode + // + if( e>128 ) + { + v = -v; + e = e-128; + } + e = e-64; + result = xfastpow(2, e, _params)*v; return result; } /************************************************************************* - Average relative error on the test set - - INPUT PARAMETERS: - Ensemble- ensemble - XY - test set - NPoints - test set size - - RESULT: - Its meaning for regression task is obvious. As for classification task - it means average relative error when estimating posterior probabilities. - - -- ALGLIB -- - Copyright 17.02.2009 by Bochkanov Sergey + Classification error *************************************************************************/ - public static double mlpeavgrelerror(mlpensemble ensemble, + private static int dfclserror(decisionforest df, double[,] xy, int npoints, alglib.xparams _params) { - double result = 0; - mlpbase.modelerrors rep = new mlpbase.modelerrors(); + int result = 0; + double[] x = new double[0]; + double[] y = new double[0]; + int i = 0; + int j = 0; + int k = 0; + int tmpi = 0; + int i_ = 0; - mlpeallerrorsx(ensemble, xy, ensemble.network.dummysxy, npoints, 0, ensemble.network.dummyidx, 0, npoints, 0, ensemble.network.buf, rep, _params); - result = rep.avgrelerror; + if( df.nclasses<=1 ) + { + result = 0; + return result; + } + x = new double[df.nvars-1+1]; + y = new double[df.nclasses-1+1]; + result = 0; + for(i=0; i<=npoints-1; i++) + { + for(i_=0; i_<=df.nvars-1;i_++) + { + x[i_] = xy[i,i_]; + } + dfprocess(df, x, ref y, _params); + k = (int)Math.Round(xy[i,df.nvars]); + tmpi = 0; + for(j=1; j<=df.nclasses-1; j++) + { + if( (double)(y[j])>(double)(y[tmpi]) ) + { + tmpi = j; + } + } + if( tmpi!=k ) + { + result = result+1; + } + } return result; } /************************************************************************* - Serializer: allocation - - -- ALGLIB -- - Copyright 19.10.2011 by Bochkanov Sergey + Internal subroutine for processing one decision tree stored in uncompressed + format starting at SubtreeRoot (this index points to the header of the tree, + not its first node). First node being processed is located at NodeOffs. *************************************************************************/ - public static void mlpealloc(alglib.serializer s, - mlpensemble ensemble, + private static void dfprocessinternaluncompressed(decisionforest df, + int subtreeroot, + int nodeoffs, + double[] x, + ref double[] y, alglib.xparams _params) { - s.alloc_entry(); - s.alloc_entry(); - s.alloc_entry(); - apserv.allocrealarray(s, ensemble.weights, -1, _params); - apserv.allocrealarray(s, ensemble.columnmeans, -1, _params); - apserv.allocrealarray(s, ensemble.columnsigmas, -1, _params); - mlpbase.mlpalloc(s, ensemble.network, _params); - } - - - /************************************************************************* - Serializer: serialization + int idx = 0; - -- ALGLIB -- - Copyright 14.03.2011 by Bochkanov Sergey - *************************************************************************/ - public static void mlpeserialize(alglib.serializer s, - mlpensemble ensemble, - alglib.xparams _params) - { - s.serialize_int(scodes.getmlpeserializationcode(_params)); - s.serialize_int(mlpefirstversion); - s.serialize_int(ensemble.ensemblesize); - apserv.serializerealarray(s, ensemble.weights, -1, _params); - apserv.serializerealarray(s, ensemble.columnmeans, -1, _params); - apserv.serializerealarray(s, ensemble.columnsigmas, -1, _params); - mlpbase.mlpserialize(s, ensemble.network, _params); + alglib.ap.assert(df.forestformat==dfuncompressedv0, "DFProcessInternal: unexpected forest format"); + + // + // Navigate through the tree + // + while( true ) + { + if( (double)(df.trees[nodeoffs])==(double)(-1) ) + { + if( df.nclasses==1 ) + { + y[0] = y[0]+df.trees[nodeoffs+1]; + } + else + { + idx = (int)Math.Round(df.trees[nodeoffs+1]); + y[idx] = y[idx]+1; + } + break; + } + if( x[(int)Math.Round(df.trees[nodeoffs])]=splitval ) + { + offs = offs+jmplen; + } + } + else + { + + // + // The split rule is "if VAR>=VAL then BRANCH0 else BRANCH1" + // + varidx = varidx-df.nvars; + if( x[varidx]0 ) + { + if( n%2==0 ) + { + result = xfastpow(r, n/2, _params); + result = result*result; + } + else + { + result = r*xfastpow(r, n-1, _params); + } + return result; + } + if( n==0 ) + { + result = 1; + } + if( n<0 ) + { + result = xfastpow(1/r, -n, _params); + } + return result; + } - NOTE: there is one-to-one correspondence between merges described by Z and - PM. I-th row of Z describes same merge of clusters as I-th row of PM, - with "left" cluster from Z corresponding to the "left" one from PM. - -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + } + public partial class mlpbase + { + /************************************************************************* + Model's errors: + * RelCLSError - fraction of misclassified cases. + * AvgCE - acerage cross-entropy + * RMSError - root-mean-square error + * AvgError - average error + * AvgRelError - average relative error + + NOTE 1: RelCLSError/AvgCE are zero on regression problems. + + NOTE 2: on classification problems RMSError/AvgError/AvgRelError contain + errors in prediction of posterior probabilities *************************************************************************/ - public class ahcreport : apobject + public class modelerrors : apobject { - public int terminationtype; - public int npoints; - public int[] p; - public int[,] z; - public int[,] pz; - public int[,] pm; - public double[] mergedist; - public ahcreport() + public double relclserror; + public double avgce; + public double rmserror; + public double avgerror; + public double avgrelerror; + public modelerrors() { init(); } public override void init() { - p = new int[0]; - z = new int[0,0]; - pz = new int[0,0]; - pm = new int[0,0]; - mergedist = new double[0]; } public override alglib.apobject make_copy() { - ahcreport _result = new ahcreport(); - _result.terminationtype = terminationtype; - _result.npoints = npoints; - _result.p = (int[])p.Clone(); - _result.z = (int[,])z.Clone(); - _result.pz = (int[,])pz.Clone(); - _result.pm = (int[,])pm.Clone(); - _result.mergedist = (double[])mergedist.Clone(); + modelerrors _result = new modelerrors(); + _result.relclserror = relclserror; + _result.avgce = avgce; + _result.rmserror = rmserror; + _result.avgerror = avgerror; + _result.avgrelerror = avgrelerror; return _result; } }; /************************************************************************* - This structure is used to store results of the k-means clustering - algorithm. - - Following information is always returned: - * NPoints contains number of points in the original dataset - * TerminationType contains completion code, negative on failure, positive - on success - * K contains number of clusters + This structure is used to store MLP error and gradient. + *************************************************************************/ + public class smlpgrad : apobject + { + public double f; + public double[] g; + public smlpgrad() + { + init(); + } + public override void init() + { + g = new double[0]; + } + public override alglib.apobject make_copy() + { + smlpgrad _result = new smlpgrad(); + _result.f = f; + _result.g = (double[])g.Clone(); + return _result; + } + }; - For positive TerminationType we return: - * NFeatures contains number of variables in the original dataset - * C, which contains centers found by algorithm - * CIdx, which maps points of the original dataset to clusters - FORMAL DESCRIPTION OF FIELDS: - NPoints number of points, >=0 - NFeatures number of variables, >=1 - TerminationType completion code: - * -5 if distance type is anything different from - Euclidean metric - * -3 for degenerate dataset: a) less than K distinct - points, b) K=0 for non-empty dataset. - * +1 for successful completion - K number of clusters - C array[K,NFeatures], rows of the array store centers - CIdx array[NPoints], which contains cluster indexes - IterationsCount actual number of iterations performed by clusterizer. - If algorithm performed more than one random restart, - total number of iterations is returned. - Energy merit function, "energy", sum of squared deviations - from cluster centers - - -- ALGLIB -- - Copyright 27.11.2012 by Bochkanov Sergey - *************************************************************************/ - public class kmeansreport : apobject + public class multilayerperceptron : apobject { - public int npoints; - public int nfeatures; - public int terminationtype; - public int iterationscount; - public double energy; - public int k; - public double[,] c; - public int[] cidx; - public kmeansreport() + public int hlnetworktype; + public int hlnormtype; + public int[] hllayersizes; + public int[] hlconnections; + public int[] hlneurons; + public int[] structinfo; + public double[] weights; + public double[] columnmeans; + public double[] columnsigmas; + public double[] neurons; + public double[] dfdnet; + public double[] derror; + public double[] x; + public double[] y; + public double[,] xy; + public double[] xyrow; + public double[] nwbuf; + public int[] integerbuf; + public modelerrors err; + public double[] rndbuf; + public alglib.smp.shared_pool buf; + public alglib.smp.shared_pool gradbuf; + public double[,] dummydxy; + public sparse.sparsematrix dummysxy; + public int[] dummyidx; + public alglib.smp.shared_pool dummypool; + public multilayerperceptron() { init(); } public override void init() { - c = new double[0,0]; - cidx = new int[0]; + hllayersizes = new int[0]; + hlconnections = new int[0]; + hlneurons = new int[0]; + structinfo = new int[0]; + weights = new double[0]; + columnmeans = new double[0]; + columnsigmas = new double[0]; + neurons = new double[0]; + dfdnet = new double[0]; + derror = new double[0]; + x = new double[0]; + y = new double[0]; + xy = new double[0,0]; + xyrow = new double[0]; + nwbuf = new double[0]; + integerbuf = new int[0]; + err = new modelerrors(); + rndbuf = new double[0]; + buf = new alglib.smp.shared_pool(); + gradbuf = new alglib.smp.shared_pool(); + dummydxy = new double[0,0]; + dummysxy = new sparse.sparsematrix(); + dummyidx = new int[0]; + dummypool = new alglib.smp.shared_pool(); } public override alglib.apobject make_copy() { - kmeansreport _result = new kmeansreport(); - _result.npoints = npoints; - _result.nfeatures = nfeatures; - _result.terminationtype = terminationtype; - _result.iterationscount = iterationscount; - _result.energy = energy; - _result.k = k; - _result.c = (double[,])c.Clone(); - _result.cidx = (int[])cidx.Clone(); + multilayerperceptron _result = new multilayerperceptron(); + _result.hlnetworktype = hlnetworktype; + _result.hlnormtype = hlnormtype; + _result.hllayersizes = (int[])hllayersizes.Clone(); + _result.hlconnections = (int[])hlconnections.Clone(); + _result.hlneurons = (int[])hlneurons.Clone(); + _result.structinfo = (int[])structinfo.Clone(); + _result.weights = (double[])weights.Clone(); + _result.columnmeans = (double[])columnmeans.Clone(); + _result.columnsigmas = (double[])columnsigmas.Clone(); + _result.neurons = (double[])neurons.Clone(); + _result.dfdnet = (double[])dfdnet.Clone(); + _result.derror = (double[])derror.Clone(); + _result.x = (double[])x.Clone(); + _result.y = (double[])y.Clone(); + _result.xy = (double[,])xy.Clone(); + _result.xyrow = (double[])xyrow.Clone(); + _result.nwbuf = (double[])nwbuf.Clone(); + _result.integerbuf = (int[])integerbuf.Clone(); + _result.err = err!=null ? (modelerrors)err.make_copy() : null; + _result.rndbuf = (double[])rndbuf.Clone(); + _result.buf = buf!=null ? (alglib.smp.shared_pool)buf.make_copy() : null; + _result.gradbuf = gradbuf!=null ? (alglib.smp.shared_pool)gradbuf.make_copy() : null; + _result.dummydxy = (double[,])dummydxy.Clone(); + _result.dummysxy = dummysxy!=null ? (sparse.sparsematrix)dummysxy.make_copy() : null; + _result.dummyidx = (int[])dummyidx.Clone(); + _result.dummypool = dummypool!=null ? (alglib.smp.shared_pool)dummypool.make_copy() : null; return _result; } }; @@ -23150,2918 +20544,2090 @@ public override alglib.apobject make_copy() - public const int kmeansblocksize = 32; - public const int kmeansparalleldim = 8; - public const int kmeansparallelk = 4; - public const double complexitymultiplier = 1.0; + public const int mlpvnum = 7; + public const int mlpfirstversion = 0; + public const int nfieldwidth = 4; + public const int hlconnfieldwidth = 5; + public const int hlnfieldwidth = 4; + public const int gradbasecasecost = 50000; + public const int microbatchsize = 64; /************************************************************************* - This function initializes clusterizer object. Newly initialized object is - empty, i.e. it does not contain dataset. You should use it as follows: - 1. creation - 2. dataset is added with ClusterizerSetPoints() - 3. additional parameters are set - 3. clusterization is performed with one of the clustering functions - - -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + This function returns number of weights updates which is required for + gradient calculation problem to be splitted. *************************************************************************/ - public static void clusterizercreate(clusterizerstate s, - alglib.xparams _params) + public static int mlpgradsplitcost(alglib.xparams _params) { - s.npoints = 0; - s.nfeatures = 0; - s.disttype = 2; - s.ahcalgo = 0; - s.kmeansrestarts = 1; - s.kmeansmaxits = 0; - s.kmeansinitalgo = 0; - s.kmeansdbgnoits = false; - s.seed = 1; - kmeansinitbuf(s.kmeanstmp, _params); + int result = 0; + + result = gradbasecasecost; + return result; } /************************************************************************* - This function adds dataset to the clusterizer structure. + This function returns number of elements in subset of dataset which is + required for gradient calculation problem to be splitted. + *************************************************************************/ + public static int mlpgradsplitsize(alglib.xparams _params) + { + int result = 0; - This function overrides all previous calls of ClusterizerSetPoints() or - ClusterizerSetDistances(). + result = microbatchsize; + return result; + } - INPUT PARAMETERS: - S - clusterizer state, initialized by ClusterizerCreate() - XY - array[NPoints,NFeatures], dataset - NPoints - number of points, >=0 - NFeatures- number of features, >=1 - DistType- distance function: - * 0 Chebyshev distance (L-inf norm) - * 1 city block distance (L1 norm) - * 2 Euclidean distance (L2 norm), non-squared - * 10 Pearson correlation: - dist(a,b) = 1-corr(a,b) - * 11 Absolute Pearson correlation: - dist(a,b) = 1-|corr(a,b)| - * 12 Uncentered Pearson correlation (cosine of the angle): - dist(a,b) = a'*b/(|a|*|b|) - * 13 Absolute uncentered Pearson correlation - dist(a,b) = |a'*b|/(|a|*|b|) - * 20 Spearman rank correlation: - dist(a,b) = 1-rankcorr(a,b) - * 21 Absolute Spearman rank correlation - dist(a,b) = 1-|rankcorr(a,b)| - NOTE 1: different distance functions have different performance penalty: - * Euclidean or Pearson correlation distances are the fastest ones - * Spearman correlation distance function is a bit slower - * city block and Chebyshev distances are order of magnitude slower - - The reason behing difference in performance is that correlation-based - distance functions are computed using optimized linear algebra kernels, - while Chebyshev and city block distance functions are computed using - simple nested loops with two branches at each iteration. - - NOTE 2: different clustering algorithms have different limitations: - * agglomerative hierarchical clustering algorithms may be used with - any kind of distance metric - * k-means++ clustering algorithm may be used only with Euclidean - distance function - Thus, list of specific clustering algorithms you may use depends - on distance function you specify when you set your dataset. - + /************************************************************************* + Creates neural network with NIn inputs, NOut outputs, without hidden + layers, with linear output layer. Network weights are filled with small + random values. + -- ALGLIB -- - Copyright 10.07.2012 by Bochkanov Sergey + Copyright 04.11.2007 by Bochkanov Sergey *************************************************************************/ - public static void clusterizersetpoints(clusterizerstate s, - double[,] xy, - int npoints, - int nfeatures, - int disttype, + public static void mlpcreate0(int nin, + int nout, + multilayerperceptron network, alglib.xparams _params) { - int i = 0; - int i_ = 0; + int[] lsizes = new int[0]; + int[] ltypes = new int[0]; + int[] lconnfirst = new int[0]; + int[] lconnlast = new int[0]; + int layerscount = 0; + int lastproc = 0; - alglib.ap.assert((((((((disttype==0 || disttype==1) || disttype==2) || disttype==10) || disttype==11) || disttype==12) || disttype==13) || disttype==20) || disttype==21, "ClusterizerSetPoints: incorrect DistType"); - alglib.ap.assert(npoints>=0, "ClusterizerSetPoints: NPoints<0"); - alglib.ap.assert(nfeatures>=1, "ClusterizerSetPoints: NFeatures<1"); - alglib.ap.assert(alglib.ap.rows(xy)>=npoints, "ClusterizerSetPoints: Rows(XY)=nfeatures, "ClusterizerSetPoints: Cols(XY)