Beispiel #1
0
        /// <summary>
        /// Preconditioned variant
        /// Similar to non-preconditioned version, this can suffer if one solution converges
        /// much slower than others, as we can't skip matrix multiplies in that case.
        /// </summary>
        public bool SolvePreconditioned()
        {
            Iterations = 0;
            if (B == null || MultiplyF == null || PreconditionMultiplyF == null)
            {
                throw new Exception("SparseSymmetricCGMultipleRHS.SolvePreconditioned(): Must set B and MultiplyF and PreconditionMultiplyF!");
            }

            int NRHS = B.Length;

            if (NRHS == 0)
            {
                throw new Exception("SparseSymmetricCGMultipleRHS.SolvePreconditioned(): Need at least one RHS vector in B");
            }

            int n = B[0].Length;

            R  = BufferUtil.AllocNxM(NRHS, n);
            P  = BufferUtil.AllocNxM(NRHS, n);
            AP = BufferUtil.AllocNxM(NRHS, n);
            Z  = BufferUtil.AllocNxM(NRHS, n);

            if (X == null || UseXAsInitialGuess == false)
            {
                if (X == null)
                {
                    X = BufferUtil.AllocNxM(NRHS, n);
                }

                for (int j = 0; j < NRHS; ++j)
                {
                    Array.Clear(X[j], 0, n);
                    Array.Copy(B[j], R[j], n);
                }
            }
            else
            {
                // hopefully is X is a decent initialization...
                InitializeR(R);
            }

            // [RMS] for convergence test?
            double[] norm = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                norm[j] = BufferUtil.Dot(B[j], B[j]);
            }

            double[] root1 = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                root1[j] = Math.Sqrt(norm[j]);
            }


            // r_0 = b - A*x_0
            MultiplyF(X, R);
            for (int j = 0; j < NRHS; ++j)
            {
                for (int i = 0; i < n; ++i)
                {
                    R[j][i] = B[j][i] - R[j][i];
                }
            }

            // z0 = M_inverse * r_0
            PreconditionMultiplyF(R, Z);

            // p0 = z0
            for (int j = 0; j < NRHS; ++j)
            {
                Array.Copy(Z[j], P[j], n);
            }

            // compute initial R*Z
            double[] RdotZ_k = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                RdotZ_k[j] = BufferUtil.Dot(R[j], Z[j]);
            }

            double[] alpha_k   = new double[NRHS];
            double[] beta_k    = new double[NRHS];
            bool[]   converged = new bool[NRHS];
            var      rhs       = Interval1i.Range(NRHS);

            int iter = 0;

            while (iter++ < MaxIterations)
            {
                // convergence test
                bool done = true;
                for (int j = 0; j < NRHS; ++j)
                {
                    if (converged[j] == false)
                    {
                        double root0 = Math.Sqrt(RdotZ_k[j]);
                        if (root0 <= ConvergeTolerance * root1[j])
                        {
                            converged[j] = true;
                        }
                    }
                    if (converged[j] == false)
                    {
                        done = false;
                    }
                }
                if (done)
                {
                    break;
                }

                MultiplyF(P, AP);

                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        alpha_k[j] = RdotZ_k[j] / BufferUtil.Dot(P[j], AP[j]);
                    }
                });

                // x_k+1 = x_k + alpha_k * p_k
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        BufferUtil.MultiplyAdd(X[j], alpha_k[j], P[j]);
                    }
                });

                // r_k+1 = r_k - alpha_k * A * p_k
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        BufferUtil.MultiplyAdd(R[j], -alpha_k[j], AP[j]);
                    }
                });

                // z_k+1 = M_inverse * r_k+1
                PreconditionMultiplyF(R, Z);

                // beta_k = (z_k+1 * r_k+1) / (z_k * r_k)
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        beta_k[j] = BufferUtil.Dot(Z[j], R[j]) / RdotZ_k[j];
                    }
                });

                // can do these in parallel but improvement is minimal

                // p_k+1 = z_k+1 + beta_k * p_k
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        for (int i = 0; i < n; ++i)
                        {
                            P[j][i] = Z[j][i] + beta_k[j] * P[j][i];
                        }
                    }
                });

                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        RdotZ_k[j] = BufferUtil.Dot(R[j], Z[j]);
                    }
                });
            }


            //System.Console.WriteLine("{0} iterations", iter);
            Iterations = iter;
            return(iter < MaxIterations);
        }
Beispiel #2
0
        /// <summary>
        /// standard CG solve
        /// </summary>
        public bool Solve()
        {
            Iterations = 0;
            if (B == null || MultiplyF == null)
            {
                throw new Exception("SparseSymmetricCGMultipleRHS.Solve(): Must set B and MultiplyF!");
            }

            int NRHS = B.Length;

            if (NRHS == 0)
            {
                throw new Exception("SparseSymmetricCGMultipleRHS.Solve(): Need at least one RHS vector in B");
            }

            int size = B[0].Length;

            // Based on the algorithm in "Matrix Computations" by Golum and Van Loan.
            R = BufferUtil.AllocNxM(NRHS, size);
            P = BufferUtil.AllocNxM(NRHS, size);
            W = BufferUtil.AllocNxM(NRHS, size);

            if (X == null || UseXAsInitialGuess == false)
            {
                if (X == null)
                {
                    X = BufferUtil.AllocNxM(NRHS, size);
                }

                for (int j = 0; j < NRHS; ++j)
                {
                    Array.Clear(X[j], 0, size);
                    Array.Copy(B[j], R[j], size);
                }
            }
            else
            {
                // hopefully is X is a decent initialization...
                InitializeR(R);
            }

            // [RMS] these were inside loop but they are constant!
            double[] norm = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                norm[j] = BufferUtil.Dot(B[j], B[j]);
            }

            double[] root1 = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                root1[j] = Math.Sqrt(norm[j]);
            }

            // The first iteration.
            double[] rho0 = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                rho0[j] = BufferUtil.Dot(R[j], R[j]);
            }

            // [RMS] If we were initialized w/ constraints already satisfied,
            //   then we are done! (happens for example in mesh deformations)
            bool[] converged  = new bool[NRHS];
            int    nconverged = 0;

            for (int j = 0; j < NRHS; ++j)
            {
                converged[j] = rho0[j] < (ConvergeTolerance * root1[j]);
                if (converged[j])
                {
                    nconverged++;
                }
            }
            if (nconverged == NRHS)
            {
                return(true);
            }

            for (int j = 0; j < NRHS; ++j)
            {
                Array.Copy(R[j], P[j], size);
            }

            MultiplyF(P, W);

            double[] alpha = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                alpha[j] = rho0[j] / BufferUtil.Dot(P[j], W[j]);
            }

            for (int j = 0; j < NRHS; ++j)
            {
                BufferUtil.MultiplyAdd(X[j], alpha[j], P[j]);
            }

            for (int j = 0; j < NRHS; ++j)
            {
                BufferUtil.MultiplyAdd(R[j], -alpha[j], W[j]);
            }

            double[] rho1 = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                rho1[j] = BufferUtil.Dot(R[j], R[j]);
            }

            double[] beta = new double[NRHS];

            var rhs = Interval1i.Range(NRHS);

            // The remaining iterations.
            int iter;

            for (iter = 1; iter < MaxIterations; ++iter)
            {
                bool done = true;
                for (int j = 0; j < NRHS; ++j)
                {
                    if (converged[j] == false)
                    {
                        double root0 = Math.Sqrt(rho1[j]);
                        if (root0 <= ConvergeTolerance * root1[j])
                        {
                            converged[j] = true;
                        }
                    }
                    if (converged[j] == false)
                    {
                        done = false;
                    }
                }
                if (done)
                {
                    break;
                }

                for (int j = 0; j < NRHS; ++j)
                {
                    beta[j] = rho1[j] / rho0[j];
                }

                UpdateP(P, beta, R, converged);

                MultiplyF(P, W);

                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        alpha[j] = rho1[j] / BufferUtil.Dot(P[j], W[j]);
                    }
                });

                // can do all these in parallel, but improvement is minimal
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        BufferUtil.MultiplyAdd(X[j], alpha[j], P[j]);
                    }
                });
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        rho0[j] = rho1[j];
                        rho1[j] = BufferUtil.MultiplyAdd_GetSqrSum(R[j], -alpha[j], W[j]);
                    }
                });
            }

            //System.Console.WriteLine("{0} iterations", iter);
            Iterations = iter;
            return(iter < MaxIterations);
        }