private static void TestPosDefSparseSystem()
        {
            var A         = Matrix.CreateFromArray(SparsePosDef10by10.Matrix);
            var b         = Vector.CreateFromArray(SparsePosDef10by10.Rhs);
            var xExpected = Vector.CreateFromArray(SparsePosDef10by10.Lhs);

            var builder = new ReorthogonalizedPcg.Builder();

            builder.ResidualTolerance     = 1E-7;
            builder.MaxIterationsProvider = new PercentageMaxIterationsProvider(1.0);
            var    pcg                = builder.Build();
            var    M                  = new JacobiPreconditioner(A.GetDiagonalAsArray());
            Vector xComputed          = Vector.CreateZero(A.NumRows);
            IterativeStatistics stats = pcg.Solve(A, M, b, xComputed, true, () => Vector.CreateZero(b.Length));

            comparer.AssertEqual(xExpected, xComputed);
        }
        private static void TestNearbyProblems(double noiseWidth, int maxIterations, int numRhsVectors)
        {
            int order   = SymmPosDef10by10.Order;
            var A       = Matrix.CreateFromArray(SymmPosDef10by10.Matrix);
            var builder = new ReorthogonalizedPcg.Builder();

            builder.ResidualTolerance     = 1E-6;
            builder.MaxIterationsProvider = new PercentageMaxIterationsProvider(1.0);
            builder.Convergence           = new RhsNormalizedConvergence();
            var pcg = builder.Build();
            var M   = new JacobiPreconditioner(A.GetDiagonalAsArray());

            // Initial run
            Vector x0                  = Vector.CreateWithValue(order, 1);
            Vector x0Expected          = x0.Copy();
            Vector b0                  = A * x0Expected;
            Vector x0Computed          = Vector.CreateZero(A.NumRows);
            IterativeStatistics stats0 = pcg.Solve(A, M, b0, x0Computed, true, () => Vector.CreateZero(order));

            Debug.WriteLine($"Initial run: iterations = {stats0.NumIterationsRequired}");
            comparer.AssertEqual(x0Expected, x0Computed);

            // Subsequent runs
            int seed = 345;

            for (int i = 0; i < numRhsVectors; ++i)
            {
                Vector dx        = Vector.CreateFromArray(RandomMatrices.CreateRandomVector(order, seed));
                Vector xExpected = x0 + noiseWidth * dx;
                Vector b         = A * xExpected;

                pcg.Clear();                 //TODO: preferably do not call this.
                //pcg.ReorthoCache.Clear();

                Vector xComputed          = Vector.CreateZero(A.NumRows);
                IterativeStatistics stats = pcg.Solve(A, M, b, xComputed, true, () => Vector.CreateZero(b.Length));
                Debug.WriteLine($"Subsequent run: iterations = {stats.NumIterationsRequired}");
                comparer.AssertEqual(xExpected, xComputed);
                Assert.InRange(stats.NumIterationsRequired, 1, maxIterations);
            }
        }
        //[Fact]
        private static void InvestigateNoiseStagnation()
        {
            double noiseWidth = 100;

            int order = 100;
            //var A = Matrix.CreateFromArray(MultiDiagonalMatrices.CreateSymmetric(order, new int[] { 0, 1, 5, 7, 12 }));
            var valueOfEachDiagonal = new Dictionary <int, double>();

            valueOfEachDiagonal[0] = 10.0;
            valueOfEachDiagonal[1] = 4.0;
            valueOfEachDiagonal[5] = 3.0;
            var A = Matrix.CreateFromArray(MultiDiagonalMatrices.CreateSymmetric(order, valueOfEachDiagonal));
            var M = new IdentityPreconditioner();
            //var M = new JacobiPreconditioner(A.GetDiagonalAsArray());

            // Method A: Regular PCG
            var pcgBuilder = new PcgAlgorithm.Builder();

            pcgBuilder.ResidualTolerance     = 1E-15;
            pcgBuilder.MaxIterationsProvider = new FixedMaxIterationsProvider(50);
            var pcg = pcgBuilder.Build();

            // Method B: Reorthogonalized PCG, but without keeping direction vectors from previous solutions.
            var pcgReorthoRestartBuilder = new ReorthogonalizedPcg.Builder();

            pcgReorthoRestartBuilder.ResidualTolerance     = 1E-15;
            pcgReorthoRestartBuilder.MaxIterationsProvider = new FixedMaxIterationsProvider(50);
            var pcgReorthoRestart = pcgReorthoRestartBuilder.Build();

            // Method C: Reorthogonalized PCG, where the second solution will reuse direction vectors from the first
            var pcgReorthoBuilder = new ReorthogonalizedPcg.Builder();

            pcgReorthoBuilder.ResidualTolerance     = 1E-15;
            pcgReorthoBuilder.MaxIterationsProvider = new FixedMaxIterationsProvider(50);
            var pcgReortho = pcgReorthoBuilder.Build();

            // Initial rhs
            Vector x0         = Vector.CreateWithValue(order, 1);
            Vector x0Expected = x0.Copy();
            Vector b0         = A * x0Expected;

            Vector xA = Vector.CreateZero(A.NumRows);
            IterativeStatistics statsA = pcg.Solve(A, M, b0, xA, true, () => Vector.CreateZero(order));

            Debug.WriteLine($"Initial run - method A: iterations = {statsA.NumIterationsRequired}");

            Vector xB = Vector.CreateZero(A.NumRows);
            IterativeStatistics statsB = pcgReorthoRestart.Solve(A, M, b0, xB, true, () => Vector.CreateZero(order));

            Debug.WriteLine($"Initial run - method B iterations = {statsB.NumIterationsRequired}");

            Vector xC = Vector.CreateZero(A.NumRows);
            IterativeStatistics statsC = pcgReortho.Solve(A, M, b0, xC, true, () => Vector.CreateZero(order));

            Debug.WriteLine($"Initial run - method C: iterations = {statsC.NumIterationsRequired}");

            // Perturbed rhs
            int    seed = 345;
            Vector dx   = Vector.CreateFromArray(RandomMatrices.CreateRandomVector(order, seed));

            Vector x1Expected = x0 + noiseWidth * dx;
            Vector b1         = A * x1Expected;

            xA     = Vector.CreateZero(A.NumRows);
            statsA = pcg.Solve(A, M, b1, xA, true, () => Vector.CreateZero(order));
            Debug.WriteLine($"2nd run, noise = {noiseWidth} - method A: iterations = {statsA.NumIterationsRequired}");

            xB = Vector.CreateZero(A.NumRows);
            pcgReorthoRestart.ReorthoCache.Clear();
            statsB = pcgReorthoRestart.Solve(A, M, b1, xB, true, () => Vector.CreateZero(order));
            Debug.WriteLine($"2nd run, noise = {noiseWidth} - method B iterations = {statsB.NumIterationsRequired}");

            xC     = Vector.CreateZero(A.NumRows);
            statsC = pcgReortho.Solve(A, M, b1, xC, true, () => Vector.CreateZero(order));
            Debug.WriteLine($"2nd run, noise = {noiseWidth} - method C: iterations = {statsC.NumIterationsRequired}");
        }
        //[Fact]
        private static void InvestigatePFetiDPCoarseProblem2D()
        {
            int order = PFetiDPCoarseProblem2D.Order;
            var A     = Matrix.CreateFromArray(PFetiDPCoarseProblem2D.MatrixScc);
            var M     = new IdentityPreconditioner();

            // Method A: Regular PCG
            var pcgBuilder = new PcgAlgorithm.Builder();

            pcgBuilder.ResidualTolerance     = 1E-20;
            pcgBuilder.MaxIterationsProvider = new FixedMaxIterationsProvider(50);
            var pcg = pcgBuilder.Build();

            // Method B: Reorthogonalized PCG, but without keeping direction vectors from previous solutions.
            var pcgReorthoRestartBuilder = new ReorthogonalizedPcg.Builder();

            pcgReorthoRestartBuilder.ResidualTolerance     = 1E-20;
            pcgReorthoRestartBuilder.MaxIterationsProvider = new FixedMaxIterationsProvider(50);
            var pcgReorthoRestart = pcgReorthoRestartBuilder.Build();

            // Method C: Reorthogonalized PCG, where the second solution will reuse direction vectors from the first
            var pcgReorthoBuilder = new ReorthogonalizedPcg.Builder();

            pcgReorthoBuilder.ResidualTolerance     = 1E-20;
            pcgReorthoBuilder.MaxIterationsProvider = new FixedMaxIterationsProvider(50);
            var pcgReortho = pcgReorthoBuilder.Build();

            // Initial rhs
            var b         = Vector.CreateFromArray(PFetiDPCoarseProblem2D.RhsVectors[0]);
            var xExpected = Vector.CreateFromArray(PFetiDPCoarseProblem2D.SolutionVectors[0]);

            Vector xA = Vector.CreateZero(A.NumRows);
            IterativeStatistics statsA = pcg.Solve(A, M, b, xA, true, () => Vector.CreateZero(order));

            Assert.True(xExpected.Equals(xA, 1E-10));
            Debug.WriteLine($"Initial run - method A: iterations = {statsA.NumIterationsRequired}");

            Vector xB = Vector.CreateZero(A.NumRows);
            IterativeStatistics statsB = pcgReorthoRestart.Solve(A, M, b, xB, true, () => Vector.CreateZero(order));

            Assert.True(xExpected.Equals(xB, 1E-10));
            Debug.WriteLine($"Initial run - method B iterations = {statsB.NumIterationsRequired}");

            Vector xC = Vector.CreateZero(A.NumRows);
            IterativeStatistics statsC = pcgReortho.Solve(A, M, b, xC, true, () => Vector.CreateZero(order));

            Assert.True(xExpected.Equals(xC, 1E-10));
            Debug.WriteLine($"Initial run - method C: iterations = {statsC.NumIterationsRequired}");

            // Next rhs
            b         = Vector.CreateFromArray(PFetiDPCoarseProblem2D.RhsVectors[1]);
            xExpected = Vector.CreateFromArray(PFetiDPCoarseProblem2D.SolutionVectors[1]);

            xA     = Vector.CreateZero(A.NumRows);
            statsA = pcg.Solve(A, M, b, xA, true, () => Vector.CreateZero(order));
            Assert.True(xExpected.Equals(xA, 1E-10));
            Debug.WriteLine($"Initial run - method A: iterations = {statsA.NumIterationsRequired}");

            xB = Vector.CreateZero(A.NumRows);
            pcgReorthoRestart.ReorthoCache.Clear();
            statsB = pcgReorthoRestart.Solve(A, M, b, xB, true, () => Vector.CreateZero(order));
            Assert.True(xExpected.Equals(xB, 1E-10));
            Debug.WriteLine($"Initial run - method B iterations = {statsB.NumIterationsRequired}");

            xC     = Vector.CreateZero(A.NumRows);
            statsC = pcgReortho.Solve(A, M, b, xC, true, () => Vector.CreateZero(order));
            Assert.True(xExpected.Equals(xC, 1E-10));
            Debug.WriteLine($"Initial run - method C: iterations = {statsC.NumIterationsRequired}");
        }