protected override IterativeStatistics SolveInternal(int maxIterations, Func <IVector> zeroVectorInitializer) { iteration = 0; Preconditioner.SolveLinearSystem(residual, precondResidual); // d0 = s0 = inv(M) * r0 //direction.CopyFrom(precondResidual); //Preconditioner.SolveLinearSystem(residual, direction); UpdateDirectionVector(precondResidual, direction); // q0 = A * d0 Matrix.Multiply(direction, matrixTimesDirection); DirectionTimesMatrixTimesDirection = direction.DotProduct(matrixTimesDirection); // Update the direction vectors cache ReorthoCache.StoreDirectionData(this); // δnew = δ0 = r0 * s0 = r0 * d0 resDotPrecondRes = residual.DotProduct(direction); // The convergence strategy must be initialized immediately after the first r and r*inv(M)*r are computed. Convergence.Initialize(this); Stagnation.StoreInitialError(Convergence.EstimateResidualNormRatio(this)); // This is also used as output double residualNormRatio = double.NaN; // α0 = (d0 * r0) / (d0 * q0) = (s0 * r0) / (d0 * (A * d0)) stepSize = resDotPrecondRes / DirectionTimesMatrixTimesDirection; for (iteration = 1; iteration < maxIterations; ++iteration) { // x = x + α * d solution.AxpyIntoThis(direction, stepSize); // Normally the residual vector is updated as: r = r - α * q. However corrections might need to be applied. residualUpdater.UpdateResidual(this, residual); // s = inv(M) * r Preconditioner.SolveLinearSystem(residual, precondResidual); // δold = δnew resDotPrecondResOld = resDotPrecondRes; // δnew = r * s resDotPrecondRes = residual.DotProduct(precondResidual); /// At this point we can check if CG has converged and exit, thus avoiding the uneccesary operations that follow. residualNormRatio = Convergence.EstimateResidualNormRatio(this); //Debug.WriteLine($"Reorthogonalized PCG iteration = {iteration}: residual norm ratio = {residualNormRatio}"); Stagnation.StoreNewError(residualNormRatio); bool hasStagnated = Stagnation.HasStagnated(); if (residualNormRatio <= ResidualTolerance) { return(new IterativeStatistics { AlgorithmName = name, HasConverged = true, HasStagnated = false, NumIterationsRequired = iteration + 1, ResidualNormRatioEstimation = residualNormRatio }); } if (hasStagnated) { return(new IterativeStatistics { AlgorithmName = name, HasConverged = false, HasStagnated = true, NumIterationsRequired = iteration + 1, ResidualNormRatioEstimation = residualNormRatio }); } // Update the direction vector using previous cached direction vectors. UpdateDirectionVector(precondResidual, direction); // q = A * d Matrix.Multiply(direction, matrixTimesDirection); DirectionTimesMatrixTimesDirection = direction.DotProduct(matrixTimesDirection); // Update the direction vectors cache ReorthoCache.StoreDirectionData(this); // α = (d * r) / (d * q) = (d * r) / (d * (A * d)) stepSize = direction.DotProduct(residual) / DirectionTimesMatrixTimesDirection; } // We reached the max iterations before PCG converged return(new IterativeStatistics { AlgorithmName = name, HasConverged = false, HasStagnated = false, NumIterationsRequired = maxIterations, ResidualNormRatioEstimation = residualNormRatio }); }