Path: blob/master/A.1 Solution of linear equations.ipynb
1675 views
Numerical algorithms by examples
Contents
The problem we want to solve in this notebook is very simple: where A is a square matrix and b is a vector of dimension . Our goal is to find the !
It is well known that the solution of this problem is simply: however, inverting a matrix is not (in general) a good idea. Matrix inversion is a slow operation and there are plenty of algorithms that permit to solve the matrix equation with no matrix inversion.
I will review the main methods by examples. Let us choose the following values of , and :
The matrix A of this example is invertible! We can check it is a full rank matrix and calculate (just for curiosity) its determinant.
The first method I'm presenting, is forbidden!! So... read it and then forget it.
LU decomposition
For more info about the theory of the LU decomposition, have a look at the wiki page link
Let us have a look at the LU decomposition. The matrix A is decomposed into the product of three matrices: where L and U are lower and upper triangular matrices, and P is a permutation matrix, which, reorders the rows of L@U.
If you are not familiar with these concepts, they will be immediately clear after seeing them in practice. Scipy offers 3 modules:
Ok... But what is the Permutation matrix? For more information on this subject have a look at the wiki link
We can see that the product L@U is almost equal to A, but the rows have a different order. For this reason we need to multiply it by P which produces the desired permutation.
Let's have a deeper look. The coordinates of the ones in the matrix P indicate the relation between the rows of A and the rows of L@U:
(0,1) The first row of A corresponds to the second row of L@U;
(1,3) the second row of A correspond to the fourth of L@U;
(2,0) the third row of A corresponds to the first of L@U;
(3,2) the fourth of A corresponds to the third of L@U.
This is clear by looking at the columns of the matrix np.where(P==1)
.
The previous code is useful to obtain the decomposition. Now we can solve the problem
using both forward (for L) and backward (for U) substitution. Since permutation matrices are orthogonal matrices (i.e., ) the inverse matrix exists and can be written as and there is no need to perform a matrix inversion.
The method solve_triangular performs the forward/backward substitution (it is a wrapper of the LAPACK function trtrs). We can write:
and
and solve it:
Another LU solver
A faster method to solve the linear equation using the LU decomposition is to use the lu_factor method (it is a wrapper of the LAPACK function getrf). It returns two matrices
The first, LU, is a matrix from which it is possible to derive easily the L and U matrices
The second, piv, corrsponds to the pivot indices of the permutation matrix P: In the matrix P, changing the row i with the row piv[i] (for all ), produces the identity matrix. The same permutation transforms A into L@U.
Solution
The solution of the linear system of equation can be obtained by feeding the output of lu_factor into the method lu_solve (which is a wrapper of the LAPACK function getrf).
Jacobi method
This is an iterative method. The idea is to write the matrix as the sum of a diagonal matrix D and a "reminder" R (a matrix with only zeros in the diagonal).
Using this idea, we can start with a guess solution and then iterate until .
The Jacobi and Gauss–Seidel methods converge if the matrix A is strictly diagonally dominant:
where are the entries of A.
For more info on the Jacobi method have a look at the wiki page link.
New example:
Since the matrix A in the previous example is not diagonally dominant, let us define a new A, and therefore a new b as well. I like x=[1,2,3,4].
Comment
Notice that we do not need to invert the matrix D. Since D is a diagonal matrix, we can just invert the elements of the diagonal. This is a much more efficient method.
Gauss-Seidel method
This method is very similar to the Jacobi method. It is again an iterative method. The idea is to write the matrix as the sum of a lower triangular matrix L and a strictly upper triangular matrix U.
Like in the Jacobi method we look for an approximate solution , and iterate until .
Again, given the specific form of the matrices L and U, we don't need to invert any matrix. We will just use the forward substitution.
For more info on the Gauss-Seidel method have a look at the wiki page link.
Let us consider the same A,x,b as in the previous example:
Comment:
The Gauss-Seidel algorithm converges much faster than the Jacobi method (15 iterations agains 56).
SOR (successive over-relaxation)
This is another improvement of the previous methods. Let's write the matrix as the sum of a strictly lower triangular matrix L, a strictly upper triangular matrix U and a diagonal matrix D.
As in the previous methods, given the specific form of the matrices, we don't need to invert any matrix. The iterative method is the following:
For more info on the SOR method have a look at the wiki page link.
By changing the relaxation parameter we can see how the number of iterations changes. The relaxation must satisfy . It seems that in this case the optimal value of is 1 and the minimum number of iterations is 15. The Gauss-Seidel algorithm is a special case of the SOR algorithm, with w=1!
Thomas algorithm
The Thomas algorithm is a simplified form of Gaussian elimination that is used to solve tridiagonal systems of equations. Have a look at the wiki page: TDMA
The idea is simply to transform the matrix A into a triagonal matrix and then use backward substitution.
Here we consider the single coefficients of the linear system instead of performing matrix operations like in the previous algorithms.
The algorithm we implement is the following (taken from wikipedia):
and then we perform backward substitution:
Let us introduce a new matrix A wich is tridiagonal. Of course we like to keep the x = [1,2,3,4]
Ok, I know the previous code is not very efficient!
I just wanted to prove that the algorithms work.
In order to show how slow is python code compared to routines that call optimized C/fortran, I wrote a wrapper for the LAPACK function dgtsv which implements the Thomas algorithm. I called this function "Thomas". You can see that it is about 8 times much faster than the function I wrote above.
On this topic, I wrote an entire notebook about code optimization and speed up. (notebook A2)