/// <summary>
        /// Preconditioned Conjugate Gradient Method <see cref="SolveConjugateGradient(Microsoft.Msagl.Core.Layout.ProximityOverlapRemoval.ConjugateGradient.SparseMatrix,Microsoft.Msagl.Core.Layout.ProximityOverlapRemoval.ConjugateGradient.Vector,Microsoft.Msagl.Core.Layout.ProximityOverlapRemoval.ConjugateGradient.Vector,int,double)"/>
        /// Preconditioner: Jacobi Preconditioner.
        /// This method should generally be preferred, due to its faster convergence.
        /// </summary>
        /// <param name="A"></param>
        /// <param name="b"></param>
        /// <param name="x"></param>
        /// <param name="iMax"></param>
        /// <param name="epsilon"></param>
        /// <returns></returns>
        public static Vector SolvePrecondConjugateGradient(SparseMatrix A, Vector b, Vector x, int iMax, double epsilon)
        {
            //create Jacobi (diagonal) preconditioner
            // M=diagonal(A), M^-1=1/M(i,i), for i=0,...,n
            // since M^-1 is only applied to vectors, we can just keep the diagonals as a vector
            Vector Minv = A.DiagonalPreconditioner();

            Vector r        = b - (A * x);
            Vector d        = Minv.CompProduct(r);
            double deltaNew = r * d;

            double normRes  = Math.Sqrt(r * r) / A.NumRow;
            double normRes0 = normRes;
            int    i        = 0;

            while ((i++) < iMax && normRes > epsilon * normRes0)
            {
                Vector q     = A * d;
                double alpha = deltaNew / (d * q);
                x.Add(alpha * d);
                // correct residual to avoid error accumulation of floating point computation, other methods are possible.
//                if (i == Math.Max(50, (int)Math.Sqrt(A.NumRow)))
//                    r = b - (A*x);
//                else
                r.Sub(alpha * q);
                Vector s        = Minv.CompProduct(r);
                double deltaOld = deltaNew;
                normRes  = Math.Sqrt(r * r) / A.NumRow;
                deltaNew = r * s;
                double beta = deltaNew / deltaOld;
                d = s + beta * d;
            }
            return(x);
        }