Routine Name: Conjugate Gradient Method for Solving Linear Systems
Author: Kyle Hovey
Language: C++
Description/Purpose:
The Conjugate Gradient method utilizes the vector gradient of a generalized potential function for a linear system \(A\vec{x} = \vec{b}\). This method works well for elliptic systems as we can guarantee a global minima. We reach successive minimizers along the direction of the instantaneous gradient of our potential function by finding the length we need to step in that direction to reach a valley.
This method resulted in only a few (usually less than 10) iterations to completion, whereas matrix separation techniques I have used before took hundreds of iterations on average.
Input:
The matrix and the output of the linear system.
Output:
A column vector as the solution of the linear system
Usage/Example:
#include "../../matrix/src/matrix/matrix.h"
#include <iostream>
#include <vector>
using Mtx = Matrix::Matrix<double>;
int main() {
Mtx A({
{ -2, 1, 0, 0 },
{ 1, -2, 1, 0 },
{ 0, 1, -2, 1 },
{ 0, 0, 1, -2 }
});
Mtx _x({ {1},{2},{3},{4} });
auto b = A * _x;
auto x = Mtx::solve(A, b, Matrix::Solve::ConjugateGradient);
std::cout << x << std::endl;
return EXIT_SUCCESS;
}
Output:
1
2
3
4
Implementation/Code:
template <typename T>
Matrix<T> Matrix<T>::solve(
const Matrix<T>& A,
const Matrix<T>& b,
const Solve::Method& method
) {
// Only work for square matrices
if (!A.isSquare()) {
throw std::domain_error("Input matrix must be square.");
}
// Get size of matrix
const auto m = std::get<0>(A.getSize());
if (method == Solve::Jacobi) {
/** Code Omitted **/
} else if (method == Solve::GaussSiedel) {
/** Code Omitted **/
} else if (method == Solve::ConjugateGradient) {
auto x = b;
auto r = b - (A * x);
auto p = r;
for (uint i = 0; i < 500; ++i) {
const double alpha =
Matrix::innerProduct(r, r) / Matrix::innerProduct(p, A * p);
x = x + (alpha * p);
const auto lastR = r;
r = r - (alpha * A * p);
if (Matrix::vNorm(r - lastR, 2) < 0.001) { break; }
const double beta =
Matrix::innerProduct(r, r) / Matrix::innerProduct(lastR, lastR);
p = r + (beta * p);
}
return x;
} else if (method == Solve::Thompson) {
/** Code Omitted **/
} else if (method == Solve::LU) {
/** Code Omitted **/
}
return Matrix<T>(m);
}