Ejemplo n.º 1
0
        /*************************************************************************
        Algorithm for reduction of the following generalized symmetric positive-
        definite eigenvalue problem:
            A*x = lambda*B*x (1) or
            A*B*x = lambda*x (2) or
            B*A*x = lambda*x (3)
        to the symmetric eigenvalues problem C*y = lambda*y (eigenvalues of this and
        the given problems are the same, and the eigenvectors of the given problem
        could be obtained by multiplying the obtained eigenvectors by the
        transformation matrix x = R*y).

        Here A is a symmetric matrix, B - symmetric positive-definite matrix.

        Input parameters:
            A           -   symmetric matrix which is given by its upper or lower
                            triangular part.
                            Array whose indexes range within [0..N-1, 0..N-1].
            N           -   size of matrices A and B.
            IsUpperA    -   storage format of matrix A.
            B           -   symmetric positive-definite matrix which is given by
                            its upper or lower triangular part.
                            Array whose indexes range within [0..N-1, 0..N-1].
            IsUpperB    -   storage format of matrix B.
            ProblemType -   if ProblemType is equal to:
                             * 1, the following problem is solved: A*x = lambda*B*x;
                             * 2, the following problem is solved: A*B*x = lambda*x;
                             * 3, the following problem is solved: B*A*x = lambda*x.

        Output parameters:
            A           -   symmetric matrix which is given by its upper or lower
                            triangle depending on IsUpperA. Contains matrix C.
                            Array whose indexes range within [0..N-1, 0..N-1].
            R           -   upper triangular or low triangular transformation matrix
                            which is used to obtain the eigenvectors of a given problem
                            as the product of eigenvectors of C (from the right) and
                            matrix R (from the left). If the matrix is upper
                            triangular, the elements below the main diagonal
                            are equal to 0 (and vice versa). Thus, we can perform
                            the multiplication without taking into account the
                            internal structure (which is an easier though less
                            effective way).
                            Array whose indexes range within [0..N-1, 0..N-1].
            IsUpperR    -   type of matrix R (upper or lower triangular).

        Result:
            True, if the problem was reduced successfully.
            False, if the error occurred during the Cholesky decomposition of
                matrix B (the matrix is not positive-definite).

          -- ALGLIB --
             Copyright 1.28.2006 by Bochkanov Sergey
        *************************************************************************/
        public static bool smatrixgevdreduce(ref double[,] a,
            int n,
            bool isuppera,
            ref double[,] b,
            bool isupperb,
            int problemtype,
            ref double[,] r,
            ref bool isupperr)
        {
            bool result = new bool();
            double[,] t = new double[0,0];
            double[] w1 = new double[0];
            double[] w2 = new double[0];
            double[] w3 = new double[0];
            int i = 0;
            int j = 0;
            double v = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int info = 0;
            int i_ = 0;
            int i1_ = 0;

            System.Diagnostics.Debug.Assert(n>0, "SMatrixGEVDReduce: N<=0!");
            System.Diagnostics.Debug.Assert(problemtype==1 | problemtype==2 | problemtype==3, "SMatrixGEVDReduce: incorrect ProblemType!");
            result = true;
            
            //
            // Problem 1:  A*x = lambda*B*x
            //
            // Reducing to:
            //     C*y = lambda*y
            //     C = L^(-1) * A * L^(-T)
            //     x = L^(-T) * y
            //
            if( problemtype==1 )
            {
                
                //
                // Factorize B in T: B = LL'
                //
                t = new double[n-1+1, n-1+1];
                if( isupperb )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=i; i_<=n-1;i_++)
                        {
                            t[i_,i] = b[i,i_];
                        }
                    }
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=0; i_<=i;i_++)
                        {
                            t[i,i_] = b[i,i_];
                        }
                    }
                }
                if( !trfac.spdmatrixcholesky(ref t, n, false) )
                {
                    result = false;
                    return result;
                }
                
                //
                // Invert L in T
                //
                matinv.rmatrixtrinverse(ref t, n, false, false, ref info, ref rep);
                if( info<=0 )
                {
                    result = false;
                    return result;
                }
                
                //
                // Build L^(-1) * A * L^(-T) in R
                //
                w1 = new double[n+1];
                w2 = new double[n+1];
                r = new double[n-1+1, n-1+1];
                for(j=1; j<=n; j++)
                {
                    
                    //
                    // Form w2 = A * l'(j) (here l'(j) is j-th column of L^(-T))
                    //
                    i1_ = (0) - (1);
                    for(i_=1; i_<=j;i_++)
                    {
                        w1[i_] = t[j-1,i_+i1_];
                    }
                    sblas.symmetricmatrixvectormultiply(ref a, isuppera, 0, j-1, ref w1, 1.0, ref w2);
                    if( isuppera )
                    {
                        blas.matrixvectormultiply(ref a, 0, j-1, j, n-1, true, ref w1, 1, j, 1.0, ref w2, j+1, n, 0.0);
                    }
                    else
                    {
                        blas.matrixvectormultiply(ref a, j, n-1, 0, j-1, false, ref w1, 1, j, 1.0, ref w2, j+1, n, 0.0);
                    }
                    
                    //
                    // Form l(i)*w2 (here l(i) is i-th row of L^(-1))
                    //
                    for(i=1; i<=n; i++)
                    {
                        i1_ = (1)-(0);
                        v = 0.0;
                        for(i_=0; i_<=i-1;i_++)
                        {
                            v += t[i-1,i_]*w2[i_+i1_];
                        }
                        r[i-1,j-1] = v;
                    }
                }
                
                //
                // Copy R to A
                //
                for(i=0; i<=n-1; i++)
                {
                    for(i_=0; i_<=n-1;i_++)
                    {
                        a[i,i_] = r[i,i_];
                    }
                }
                
                //
                // Copy L^(-1) from T to R and transpose
                //
                isupperr = true;
                for(i=0; i<=n-1; i++)
                {
                    for(j=0; j<=i-1; j++)
                    {
                        r[i,j] = 0;
                    }
                }
                for(i=0; i<=n-1; i++)
                {
                    for(i_=i; i_<=n-1;i_++)
                    {
                        r[i,i_] = t[i_,i];
                    }
                }
                return result;
            }
            
            //
            // Problem 2:  A*B*x = lambda*x
            // or
            // problem 3:  B*A*x = lambda*x
            //
            // Reducing to:
            //     C*y = lambda*y
            //     C = U * A * U'
            //     B = U'* U
            //
            if( problemtype==2 | problemtype==3 )
            {
                
                //
                // Factorize B in T: B = U'*U
                //
                t = new double[n-1+1, n-1+1];
                if( isupperb )
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=i; i_<=n-1;i_++)
                        {
                            t[i,i_] = b[i,i_];
                        }
                    }
                }
                else
                {
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=i; i_<=n-1;i_++)
                        {
                            t[i,i_] = b[i_,i];
                        }
                    }
                }
                if( !trfac.spdmatrixcholesky(ref t, n, true) )
                {
                    result = false;
                    return result;
                }
                
                //
                // Build U * A * U' in R
                //
                w1 = new double[n+1];
                w2 = new double[n+1];
                w3 = new double[n+1];
                r = new double[n-1+1, n-1+1];
                for(j=1; j<=n; j++)
                {
                    
                    //
                    // Form w2 = A * u'(j) (here u'(j) is j-th column of U')
                    //
                    i1_ = (j-1) - (1);
                    for(i_=1; i_<=n-j+1;i_++)
                    {
                        w1[i_] = t[j-1,i_+i1_];
                    }
                    sblas.symmetricmatrixvectormultiply(ref a, isuppera, j-1, n-1, ref w1, 1.0, ref w3);
                    i1_ = (1) - (j);
                    for(i_=j; i_<=n;i_++)
                    {
                        w2[i_] = w3[i_+i1_];
                    }
                    i1_ = (j-1) - (j);
                    for(i_=j; i_<=n;i_++)
                    {
                        w1[i_] = t[j-1,i_+i1_];
                    }
                    if( isuppera )
                    {
                        blas.matrixvectormultiply(ref a, 0, j-2, j-1, n-1, false, ref w1, j, n, 1.0, ref w2, 1, j-1, 0.0);
                    }
                    else
                    {
                        blas.matrixvectormultiply(ref a, j-1, n-1, 0, j-2, true, ref w1, j, n, 1.0, ref w2, 1, j-1, 0.0);
                    }
                    
                    //
                    // Form u(i)*w2 (here u(i) is i-th row of U)
                    //
                    for(i=1; i<=n; i++)
                    {
                        i1_ = (i)-(i-1);
                        v = 0.0;
                        for(i_=i-1; i_<=n-1;i_++)
                        {
                            v += t[i-1,i_]*w2[i_+i1_];
                        }
                        r[i-1,j-1] = v;
                    }
                }
                
                //
                // Copy R to A
                //
                for(i=0; i<=n-1; i++)
                {
                    for(i_=0; i_<=n-1;i_++)
                    {
                        a[i,i_] = r[i,i_];
                    }
                }
                if( problemtype==2 )
                {
                    
                    //
                    // Invert U in T
                    //
                    matinv.rmatrixtrinverse(ref t, n, true, false, ref info, ref rep);
                    if( info<=0 )
                    {
                        result = false;
                        return result;
                    }
                    
                    //
                    // Copy U^-1 from T to R
                    //
                    isupperr = true;
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=0; j<=i-1; j++)
                        {
                            r[i,j] = 0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=i; i_<=n-1;i_++)
                        {
                            r[i,i_] = t[i,i_];
                        }
                    }
                }
                else
                {
                    
                    //
                    // Copy U from T to R and transpose
                    //
                    isupperr = false;
                    for(i=0; i<=n-1; i++)
                    {
                        for(j=i+1; j<=n-1; j++)
                        {
                            r[i,j] = 0;
                        }
                    }
                    for(i=0; i<=n-1; i++)
                    {
                        for(i_=i; i_<=n-1;i_++)
                        {
                            r[i_,i] = t[i,i_];
                        }
                    }
                }
            }
            return result;
        }
Ejemplo n.º 2
0
        /*************************************************************************
        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:
            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:
            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 10.03.2009 by Bochkanov Sergey
        *************************************************************************/
        public static void mlptrainlm(ref mlpbase.multilayerperceptron network,
            ref double[,] xy,
            int npoints,
            double decay,
            int restarts,
            ref int info,
            ref mlpreport rep)
        {
            int nin = 0;
            int nout = 0;
            int wcount = 0;
            double lmftol = 0;
            double lmsteptol = 0;
            int i = 0;
            int k = 0;
            double v = 0;
            double e = 0;
            double enew = 0;
            double xnorm2 = 0;
            double stepnorm = 0;
            double[] g = new double[0];
            double[] d = new double[0];
            double[,] h = new double[0,0];
            double[,] hmod = new double[0,0];
            double[,] z = new double[0,0];
            bool spd = new bool();
            double nu = 0;
            double lambda = 0;
            double lambdaup = 0;
            double lambdadown = 0;
            minlbfgs.minlbfgsreport internalrep = new minlbfgs.minlbfgsreport();
            minlbfgs.minlbfgsstate state = new minlbfgs.minlbfgsstate();
            double[] x = new double[0];
            double[] y = new double[0];
            double[] wbase = new double[0];
            double[] wdir = new double[0];
            double[] wt = new double[0];
            double[] wx = new double[0];
            int pass = 0;
            double[] wbest = new double[0];
            double ebest = 0;
            int invinfo = 0;
            matinv.matinvreport invrep = new matinv.matinvreport();
            int solverinfo = 0;
            densesolver.densesolverreport solverrep = new densesolver.densesolverreport();
            int i_ = 0;

            mlpbase.mlpproperties(ref network, ref nin, ref nout, ref wcount);
            lambdaup = 10;
            lambdadown = 0.3;
            lmftol = 0.001;
            lmsteptol = 0.001;
            
            //
            // Test for inputs
            //
            if( npoints<=0 | restarts<1 )
            {
                info = -1;
                return;
            }
            if( mlpbase.mlpissoftmax(ref network) )
            {
                for(i=0; i<=npoints-1; i++)
                {
                    if( (int)Math.Round(xy[i,nin])<0 | (int)Math.Round(xy[i,nin])>=nout )
                    {
                        info = -2;
                        return;
                    }
                }
            }
            decay = Math.Max(decay, mindecay);
            info = 2;
            
            //
            // Initialize data
            //
            rep.ngrad = 0;
            rep.nhess = 0;
            rep.ncholesky = 0;
            
            //
            // General case.
            // Prepare task and network. Allocate space.
            //
            mlpbase.mlpinitpreprocessor(ref network, ref xy, npoints);
            g = new double[wcount-1+1];
            h = new double[wcount-1+1, wcount-1+1];
            hmod = new double[wcount-1+1, wcount-1+1];
            wbase = new double[wcount-1+1];
            wdir = new double[wcount-1+1];
            wbest = new double[wcount-1+1];
            wt = new double[wcount-1+1];
            wx = new double[wcount-1+1];
            ebest = AP.Math.MaxRealNumber;
            
            //
            // Multiple passes
            //
            for(pass=1; pass<=restarts; pass++)
            {
                
                //
                // Initialize weights
                //
                mlpbase.mlprandomize(ref network);
                
                //
                // First stage of the hybrid algorithm: LBFGS
                //
                for(i_=0; i_<=wcount-1;i_++)
                {
                    wbase[i_] = network.weights[i_];
                }
                minlbfgs.minlbfgscreate(wcount, Math.Min(wcount, 5), ref wbase, ref state);
                minlbfgs.minlbfgssetcond(ref state, 0, 0, 0, Math.Max(25, wcount));
                while( minlbfgs.minlbfgsiteration(ref state) )
                {
                    
                    //
                    // gradient
                    //
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        network.weights[i_] = state.x[i_];
                    }
                    mlpbase.mlpgradbatch(ref network, ref xy, npoints, ref state.f, ref state.g);
                    
                    //
                    // weight decay
                    //
                    v = 0.0;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        v += network.weights[i_]*network.weights[i_];
                    }
                    state.f = state.f+0.5*decay*v;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        state.g[i_] = state.g[i_] + decay*network.weights[i_];
                    }
                    
                    //
                    // next iteration
                    //
                    rep.ngrad = rep.ngrad+1;
                }
                minlbfgs.minlbfgsresults(ref state, ref wbase, ref internalrep);
                for(i_=0; i_<=wcount-1;i_++)
                {
                    network.weights[i_] = wbase[i_];
                }
                
                //
                // Second stage of the hybrid algorithm: LM
                //
                // Initialize H with identity matrix,
                // G with gradient,
                // E with regularized error.
                //
                mlpbase.mlphessianbatch(ref network, ref xy, npoints, ref e, ref g, ref h);
                v = 0.0;
                for(i_=0; i_<=wcount-1;i_++)
                {
                    v += network.weights[i_]*network.weights[i_];
                }
                e = e+0.5*decay*v;
                for(i_=0; i_<=wcount-1;i_++)
                {
                    g[i_] = g[i_] + decay*network.weights[i_];
                }
                for(k=0; k<=wcount-1; k++)
                {
                    h[k,k] = h[k,k]+decay;
                }
                rep.nhess = rep.nhess+1;
                lambda = 0.001;
                nu = 2;
                while( true )
                {
                    
                    //
                    // 1. HMod = H+lambda*I
                    // 2. Try to solve (H+Lambda*I)*dx = -g.
                    //    Increase lambda if left part is not positive definite.
                    //
                    for(i=0; i<=wcount-1; i++)
                    {
                        for(i_=0; i_<=wcount-1;i_++)
                        {
                            hmod[i,i_] = h[i,i_];
                        }
                        hmod[i,i] = hmod[i,i]+lambda;
                    }
                    spd = trfac.spdmatrixcholesky(ref hmod, wcount, true);
                    rep.ncholesky = rep.ncholesky+1;
                    if( !spd )
                    {
                        lambda = lambda*lambdaup*nu;
                        nu = nu*2;
                        continue;
                    }
                    densesolver.spdmatrixcholeskysolve(ref hmod, wcount, true, ref g, ref solverinfo, ref solverrep, ref wdir);
                    if( solverinfo<0 )
                    {
                        lambda = lambda*lambdaup*nu;
                        nu = nu*2;
                        continue;
                    }
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        wdir[i_] = -1*wdir[i_];
                    }
                    
                    //
                    // Lambda found.
                    // 1. Save old w in WBase
                    // 1. Test some stopping criterions
                    // 2. If error(w+wdir)>error(w), increase lambda
                    //
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        network.weights[i_] = network.weights[i_] + wdir[i_];
                    }
                    xnorm2 = 0.0;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        xnorm2 += network.weights[i_]*network.weights[i_];
                    }
                    stepnorm = 0.0;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        stepnorm += wdir[i_]*wdir[i_];
                    }
                    stepnorm = Math.Sqrt(stepnorm);
                    enew = mlpbase.mlperror(ref network, ref xy, npoints)+0.5*decay*xnorm2;
                    if( (double)(stepnorm)<(double)(lmsteptol*(1+Math.Sqrt(xnorm2))) )
                    {
                        break;
                    }
                    if( (double)(enew)>(double)(e) )
                    {
                        lambda = lambda*lambdaup*nu;
                        nu = nu*2;
                        continue;
                    }
                    
                    //
                    // Optimize using inv(cholesky(H)) as preconditioner
                    //
                    matinv.rmatrixtrinverse(ref hmod, wcount, true, false, ref invinfo, ref invrep);
                    if( invinfo<=0 )
                    {
                        
                        //
                        // if matrix can't be inverted then exit with errors
                        // TODO: make WCount steps in direction suggested by HMod
                        //
                        info = -9;
                        return;
                    }
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        wbase[i_] = network.weights[i_];
                    }
                    for(i=0; i<=wcount-1; i++)
                    {
                        wt[i] = 0;
                    }
                    minlbfgs.minlbfgscreatex(wcount, wcount, ref wt, 1, ref state);
                    minlbfgs.minlbfgssetcond(ref state, 0, 0, 0, 5);
                    while( minlbfgs.minlbfgsiteration(ref state) )
                    {
                        
                        //
                        // gradient
                        //
                        for(i=0; i<=wcount-1; i++)
                        {
                            v = 0.0;
                            for(i_=i; i_<=wcount-1;i_++)
                            {
                                v += state.x[i_]*hmod[i,i_];
                            }
                            network.weights[i] = wbase[i]+v;
                        }
                        mlpbase.mlpgradbatch(ref network, ref xy, npoints, ref state.f, ref g);
                        for(i=0; i<=wcount-1; i++)
                        {
                            state.g[i] = 0;
                        }
                        for(i=0; i<=wcount-1; i++)
                        {
                            v = g[i];
                            for(i_=i; i_<=wcount-1;i_++)
                            {
                                state.g[i_] = state.g[i_] + v*hmod[i,i_];
                            }
                        }
                        
                        //
                        // weight decay
                        // grad(x'*x) = A'*(x0+A*t)
                        //
                        v = 0.0;
                        for(i_=0; i_<=wcount-1;i_++)
                        {
                            v += network.weights[i_]*network.weights[i_];
                        }
                        state.f = state.f+0.5*decay*v;
                        for(i=0; i<=wcount-1; i++)
                        {
                            v = decay*network.weights[i];
                            for(i_=i; i_<=wcount-1;i_++)
                            {
                                state.g[i_] = state.g[i_] + v*hmod[i,i_];
                            }
                        }
                        
                        //
                        // next iteration
                        //
                        rep.ngrad = rep.ngrad+1;
                    }
                    minlbfgs.minlbfgsresults(ref state, ref wt, ref internalrep);
                    
                    //
                    // Accept new position.
                    // Calculate Hessian
                    //
                    for(i=0; i<=wcount-1; i++)
                    {
                        v = 0.0;
                        for(i_=i; i_<=wcount-1;i_++)
                        {
                            v += wt[i_]*hmod[i,i_];
                        }
                        network.weights[i] = wbase[i]+v;
                    }
                    mlpbase.mlphessianbatch(ref network, ref xy, npoints, ref e, ref g, ref h);
                    v = 0.0;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        v += network.weights[i_]*network.weights[i_];
                    }
                    e = e+0.5*decay*v;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        g[i_] = g[i_] + decay*network.weights[i_];
                    }
                    for(k=0; k<=wcount-1; k++)
                    {
                        h[k,k] = h[k,k]+decay;
                    }
                    rep.nhess = rep.nhess+1;
                    
                    //
                    // Update lambda
                    //
                    lambda = lambda*lambdadown;
                    nu = 2;
                }
                
                //
                // update WBest
                //
                v = 0.0;
                for(i_=0; i_<=wcount-1;i_++)
                {
                    v += network.weights[i_]*network.weights[i_];
                }
                e = 0.5*decay*v+mlpbase.mlperror(ref network, ref xy, npoints);
                if( (double)(e)<(double)(ebest) )
                {
                    ebest = e;
                    for(i_=0; i_<=wcount-1;i_++)
                    {
                        wbest[i_] = network.weights[i_];
                    }
                }
            }
            
            //
            // copy WBest to output
            //
            for(i_=0; i_<=wcount-1;i_++)
            {
                network.weights[i_] = wbest[i_];
            }
        }
        /*************************************************************************
        Complex TR inverse
        *************************************************************************/
        private static void testctrinv(int maxn,
            int passcount,
            double threshold,
            ref bool ctrerrors)
        {
            AP.Complex[,] a = new AP.Complex[0,0];
            AP.Complex[,] b = new AP.Complex[0,0];
            int n = 0;
            int pass = 0;
            int i = 0;
            int j = 0;
            int task = 0;
            bool isupper = new bool();
            bool isunit = new bool();
            AP.Complex v = 0;
            bool waserrors = new bool();
            int info = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int i_ = 0;

            waserrors = false;
            
            //
            // Test
            //
            for(n=1; n<=maxn; n++)
            {
                a = new AP.Complex[n, n];
                b = new AP.Complex[n, n];
                for(task=0; task<=3; task++)
                {
                    for(pass=1; pass<=passcount; pass++)
                    {
                        
                        //
                        // Determine task
                        //
                        isupper = task%2==0;
                        isunit = task/2%2==0;
                        
                        //
                        // Generate matrix
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( i==j )
                                {
                                    a[i,i].x = 1+AP.Math.RandomReal();
                                    a[i,i].y = 1+AP.Math.RandomReal();
                                }
                                else
                                {
                                    a[i,j].x = 0.2*AP.Math.RandomReal()-0.1;
                                    a[i,j].y = 0.2*AP.Math.RandomReal()-0.1;
                                }
                                b[i,j] = a[i,j];
                            }
                        }
                        
                        //
                        // Inverse
                        //
                        matinv.cmatrixtrinverse(ref b, n, isupper, isunit, ref info, ref rep);
                        if( info<=0 )
                        {
                            ctrerrors = true;
                            return;
                        }
                        
                        //
                        // Structural test
                        //
                        if( isunit )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                ctrerrors = ctrerrors | a[i,i]!=b[i,i];
                            }
                        }
                        if( isupper )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=i-1; j++)
                                {
                                    ctrerrors = ctrerrors | a[i,j]!=b[i,j];
                                }
                            }
                        }
                        else
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=i+1; j<=n-1; j++)
                                {
                                    ctrerrors = ctrerrors | a[i,j]!=b[i,j];
                                }
                            }
                        }
                        
                        //
                        // Inverse test
                        //
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                if( j<i & isupper | j>i & !isupper )
                                {
                                    a[i,j] = 0;
                                    b[i,j] = 0;
                                }
                            }
                        }
                        if( isunit )
                        {
                            for(i=0; i<=n-1; i++)
                            {
                                a[i,i] = 1;
                                b[i,i] = 1;
                            }
                        }
                        for(i=0; i<=n-1; i++)
                        {
                            for(j=0; j<=n-1; j++)
                            {
                                v = 0.0;
                                for(i_=0; i_<=n-1;i_++)
                                {
                                    v += a[i,i_]*b[i_,j];
                                }
                                if( j!=i )
                                {
                                    ctrerrors = ctrerrors | (double)(AP.Math.AbsComplex(v))>(double)(threshold);
                                }
                                else
                                {
                                    ctrerrors = ctrerrors | (double)(AP.Math.AbsComplex(v-1))>(double)(threshold);
                                }
                            }
                        }
                    }
                }
            }
        }
        /*************************************************************************
        Real test
        *************************************************************************/
        private static void testrinv(int maxn,
            int passcount,
            double threshold,
            ref bool rerrors)
        {
            double[,] a = new double[0,0];
            double[,] lua = new double[0,0];
            double[,] inva = new double[0,0];
            double[,] invlua = new double[0,0];
            int[] p = new int[0];
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int pass = 0;
            int taskkind = 0;
            double v = 0;
            int info = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int i_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    
                    //
                    // ********************************************************
                    // WELL CONDITIONED TASKS
                    // ability to find correct solution is tested
                    // ********************************************************
                    //
                    // 1. generate random well conditioned matrix A.
                    // 2. generate random solution vector xe
                    // 3. generate right part b=A*xe
                    // 4. test different methods on original A
                    //
                    matgen.rmatrixrndcond(n, 1000, ref a);
                    rmatrixmakeacopy(ref a, n, n, ref lua);
                    trfac.rmatrixlu(ref lua, n, n, ref p);
                    rmatrixmakeacopy(ref a, n, n, ref inva);
                    rmatrixmakeacopy(ref lua, n, n, ref invlua);
                    info = 0;
                    unsetrep(ref rep);
                    matinv.rmatrixinverse(ref inva, n, ref info, ref rep);
                    rerrors = rerrors | !rmatrixcheckinverse(ref a, ref inva, n, threshold, info, ref rep);
                    info = 0;
                    unsetrep(ref rep);
                    matinv.rmatrixluinverse(ref invlua, ref p, n, ref info, ref rep);
                    rerrors = rerrors | !rmatrixcheckinverse(ref a, ref invlua, n, threshold, info, ref rep);
                    
                    //
                    // ********************************************************
                    // EXACTLY SINGULAR MATRICES
                    // ability to detect singularity is tested
                    // ********************************************************
                    //
                    // 1. generate different types of singular matrices:
                    //    * zero
                    //    * with zero columns
                    //    * with zero rows
                    //    * with equal rows/columns
                    // 2. test different methods
                    //
                    for(taskkind=0; taskkind<=4; taskkind++)
                    {
                        unset2d(ref a);
                        if( taskkind==0 )
                        {
                            
                            //
                            // all zeros
                            //
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        if( taskkind==1 )
                        {
                            
                            //
                            // there is zero column
                            //
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*AP.Math.RandomReal()-1;
                                }
                            }
                            k = AP.Math.RandomInteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,k] = 0*a[i_,k];
                            }
                        }
                        if( taskkind==2 )
                        {
                            
                            //
                            // there is zero row
                            //
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*AP.Math.RandomReal()-1;
                                }
                            }
                            k = AP.Math.RandomInteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[k,i_] = 0*a[k,i_];
                            }
                        }
                        if( taskkind==3 )
                        {
                            
                            //
                            // equal columns
                            //
                            if( n<2 )
                            {
                                continue;
                            }
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*AP.Math.RandomReal()-1;
                                }
                            }
                            k = 1+AP.Math.RandomInteger(n-1);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,0] = a[i_,k];
                            }
                        }
                        if( taskkind==4 )
                        {
                            
                            //
                            // equal rows
                            //
                            if( n<2 )
                            {
                                continue;
                            }
                            a = new double[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 2*AP.Math.RandomReal()-1;
                                }
                            }
                            k = 1+AP.Math.RandomInteger(n-1);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[0,i_] = a[k,i_];
                            }
                        }
                        rmatrixmakeacopy(ref a, n, n, ref lua);
                        trfac.rmatrixlu(ref lua, n, n, ref p);
                        info = 0;
                        unsetrep(ref rep);
                        matinv.rmatrixinverse(ref a, n, ref info, ref rep);
                        rerrors = rerrors | !rmatrixcheckinversesingular(ref a, n, threshold, info, ref rep);
                        info = 0;
                        unsetrep(ref rep);
                        matinv.rmatrixluinverse(ref lua, ref p, n, ref info, ref rep);
                        rerrors = rerrors | !rmatrixcheckinversesingular(ref lua, n, threshold, info, ref rep);
                    }
                }
            }
        }
        /*************************************************************************
        HPD test
        *************************************************************************/
        private static void testhpdinv(int maxn,
            int passcount,
            double threshold,
            ref bool hpderrors)
        {
            AP.Complex[,] a = new AP.Complex[0,0];
            AP.Complex[,] cha = new AP.Complex[0,0];
            AP.Complex[,] inva = new AP.Complex[0,0];
            AP.Complex[,] invcha = new AP.Complex[0,0];
            bool isupper = new bool();
            int i = 0;
            int j = 0;
            int k = 0;
            int n = 0;
            int pass = 0;
            int taskkind = 0;
            AP.Complex v = 0;
            int info = 0;
            matinv.matinvreport rep = new matinv.matinvreport();
            int i_ = 0;

            
            //
            // General square matrices:
            // * test general solvers
            // * test least squares solver
            //
            for(pass=1; pass<=passcount; pass++)
            {
                for(n=1; n<=maxn; n++)
                {
                    isupper = (double)(AP.Math.RandomReal())>(double)(0.5);
                    
                    //
                    // ********************************************************
                    // WELL CONDITIONED TASKS
                    // ability to find correct solution is tested
                    // ********************************************************
                    //
                    // 1. generate random well conditioned matrix A.
                    // 2. generate random solution vector xe
                    // 3. generate right part b=A*xe
                    // 4. test different methods on original A
                    //
                    matgen.hpdmatrixrndcond(n, 1000, ref a);
                    cmatrixdrophalf(ref a, n, isupper);
                    cmatrixmakeacopy(ref a, n, n, ref cha);
                    if( !trfac.hpdmatrixcholesky(ref cha, n, isupper) )
                    {
                        continue;
                    }
                    cmatrixmakeacopy(ref a, n, n, ref inva);
                    cmatrixmakeacopy(ref cha, n, n, ref invcha);
                    info = 0;
                    unsetrep(ref rep);
                    matinv.hpdmatrixinverse(ref inva, n, isupper, ref info, ref rep);
                    hpderrors = hpderrors | !hpdmatrixcheckinverse(a, inva, isupper, n, threshold, info, ref rep);
                    info = 0;
                    unsetrep(ref rep);
                    matinv.hpdmatrixcholeskyinverse(ref invcha, n, isupper, ref info, ref rep);
                    hpderrors = hpderrors | !hpdmatrixcheckinverse(a, invcha, isupper, n, threshold, info, ref rep);
                    
                    //
                    // ********************************************************
                    // EXACTLY SINGULAR MATRICES
                    // ability to detect singularity is tested
                    // ********************************************************
                    //
                    // 1. generate different types of singular matrices:
                    //    * zero
                    //    * with zero columns
                    //    * with zero rows
                    // 2. test different methods
                    //
                    for(taskkind=0; taskkind<=2; taskkind++)
                    {
                        cunset2d(ref a);
                        if( taskkind==0 )
                        {
                            
                            //
                            // all zeros
                            //
                            a = new AP.Complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j] = 0;
                                }
                            }
                        }
                        if( taskkind==1 )
                        {
                            
                            //
                            // there is zero column
                            //
                            a = new AP.Complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j].x = 2*AP.Math.RandomReal()-1;
                                    a[i,j].y = 2*AP.Math.RandomReal()-1;
                                }
                            }
                            k = AP.Math.RandomInteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,k] = 0*a[i_,k];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[k,i_] = 0*a[k,i_];
                            }
                        }
                        if( taskkind==2 )
                        {
                            
                            //
                            // there is zero row
                            //
                            a = new AP.Complex[n, n];
                            for(i=0; i<=n-1; i++)
                            {
                                for(j=0; j<=n-1; j++)
                                {
                                    a[i,j].x = 2*AP.Math.RandomReal()-1;
                                    a[i,j].y = 2*AP.Math.RandomReal()-1;
                                }
                            }
                            k = AP.Math.RandomInteger(n);
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[k,i_] = 0*a[k,i_];
                            }
                            for(i_=0; i_<=n-1;i_++)
                            {
                                a[i_,k] = 0*a[i_,k];
                            }
                        }
                        info = 0;
                        unsetrep(ref rep);
                        matinv.hpdmatrixcholeskyinverse(ref a, n, isupper, ref info, ref rep);
                        if( info!=-3 & info!=1 )
                        {
                            hpderrors = true;
                        }
                        else
                        {
                            hpderrors = hpderrors | (double)(rep.r1)<(double)(0) | (double)(rep.r1)>(double)(1000*AP.Math.MachineEpsilon);
                            hpderrors = hpderrors | (double)(rep.rinf)<(double)(0) | (double)(rep.rinf)>(double)(1000*AP.Math.MachineEpsilon);
                        }
                    }
                }
            }
        }