Linear systems

Contents

In this lab we tackle the problem of solving linear systems of equations

$$ \begin{array}{cll} a_{11}x_{1}+a_{12}x_{2}+\ldots+a_{1n}x_{n} & = & b_{1},\\ a_{21}x_{1}+a_{22}x_{2}+\ldots+a_{2n}x_{n} & = & b_{2},\\ \vdots\\ a_{n1}x_{1}+a_{n2}x_{2}+\ldots+a_{nn}x_{n} & = & b_{n}. \end{array} $$

We assume that the matrix $A=(a_{ij})$ is non-singular, i.e. det$(A)$ does not vanish, and therefore, the system has a unique solution.

With from __future__ import division we force the result of division with / to be always float. If we do not include this, the division of two integers with / would be an integer.

We also import the modules numpy and pprint. The pprint module provides a capability to “pretty-print” Python data.

In [1]:
from __future__ import division

import numpy as np
import pprint

Direct methods

Forward substitution

For the system $$ L\mathbf{x}=\mathbf{b}, $$ with $L$ of the form $$ L=\left(\begin{array}{ccccc} l_{11} & 0 & 0 & \ldots & 0\\ l_{21} & l_{22} & 0 & \ldots & 0\\ l_{31} & l_{32} & l_{33} & \ldots & 0\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ l_{n1} & l_{n2} & l_{n3} & \ldots & l_{nn} \end{array}\right) $$ the $i$-th equation is, for $i=1,2,\ldots,n$, $$ l_{i1}x_{1}+l_{i2}x_{2}+\cdots+l_{ii}x_{i}=b_{i}. $$

Therefore, for $i=1,2,\ldots,n$, if $l_{ii}$ does not vanish, then $$ x_{i}=\frac{b_{i}-l_{i1}x_{1}-\cdots-l_{ii-1}x_{i-1}}{l_{ii}}= \frac{1}{l_{ii}}\left(b_{i}-\sum_{j=1}^{i-1}l_{ij}x_{j}\right). $$

Algorithm

  • If $l_{11}=0$, exit
  • Else, set $x_{1}=\dfrac{b_{1}}{l_{11}}$
  • For $i=2,\ldots,n$
  • If $l_{ii}= 0$, exit.

  • Else, set $$ x_{i}=\frac{1}{l_{ii}}\left(b_{i}-\sum_{j=1}^{i-1}l_{ij}x_{j}\right). $$


Exercise 1

Write a function subs_forward(L,b) whose output $x$ is the solution of the system $Lx=b$, being L a lower triangular matriz and b a column matrix.

Use it to solve the system

$$\left(\begin{array}{rrr} 2 & 0 & 0\\ 1 & 2 & 0\\ 1 & 1 & 2 \end{array}\right) \left(\begin{array}{c} x_1\\ x_2\\ x_3 \end{array}\right)= \left(\begin{array}{r} 2\\ -1\\ 0 \end{array}\right) $$

Check the solution computing $Lx$ and comparing it with $b$.

In [2]:
# define matrices A and b using numpy arrays 
L = np.array([[2, 0, 0], [1, 2, 0], [1, 1, 2]])
b = np.array([2, -1, 0])

Check that the function also works well with the $Lx=b$ system with matrices $L$ and $b$ generated as follows

In [3]:
n = 5
np.random.seed(1)           # for the random numbers to be repeatable
L = np.random.random((n,n)) # generate a random nxn matrix
L = np.tril(L)              # remove elements over diagonal
b = np.random.random((n,))  # generate a random vector of dimension n

print 'L'
pprint.pprint(L)
print 'b' 
pprint.pprint(b)
L
array([[ 0.417022  ,  0.        ,  0.        ,  0.        ,  0.        ],
       [ 0.09233859,  0.18626021,  0.        ,  0.        ,  0.        ],
       [ 0.41919451,  0.6852195 ,  0.20445225,  0.        ,  0.        ],
       [ 0.67046751,  0.4173048 ,  0.55868983,  0.14038694,  0.        ],
       [ 0.80074457,  0.96826158,  0.31342418,  0.69232262,  0.87638915]])
b
array([ 0.89460666,  0.08504421,  0.03905478,  0.16983042,  0.8781425 ])
In [4]:
%run Exercise1.py
-------------FIRST SYSTEM-------------

 x
array([ 1, -1,  0])

 Lx
array([ 2, -1,  0])

 b
array([ 2, -1,  0])

-------------SECOND SYSTEM-------------

 x
array([ 0.23583128,  2.1439431 , -2.98378361,  8.17298166, -7.18403614])

 Lx
array([ 0.09834683,  0.42110763,  0.95788953,  0.53316528,  0.69187711])

 b
array([ 0.09834683,  0.42110763,  0.95788953,  0.53316528,  0.69187711])

Backward substitution

Similarly, for the system

$$ U\mathbf{x}=\mathbf{b}' $$

with $U$ of the form

$$ U=\left(\begin{array}{ccccc} u_{11} & u_{12} & u_{13} & \ldots & u_{1n}\\ 0 & u_{22} & u_{23} & \ldots & u_{2n}\\ 0 & 0 & u_{33} & \ldots & u_{3n}\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 0 & 0 & 0 & 0 & u_{nn} \end{array}\right) $$

the $i$-th equation is, for $i=1,2,\ldots,n$,

$$ u_{ii}x_{i}+u_{ii+1}x_{i+1}+\cdots+u_{in}x_{n}=b_{i} $$

Therefore, for $i=1,2,\ldots,n$, if $l_{ii}$ does not vanish then

$$ x_{i}=\frac{b_{i}-u_{ii+1}x_{i+1}-\cdots-u_{in}x_{n}}{u_{ii}}= \frac{1}{u_{ii}}\left(b_{i}-\sum_{j=i+1}^{n}u_{ij}x_{j}\right), $$

Algorithm

  • If $u_{11}=0$, exit
  • Else, set $x_{n}=\dfrac{b_{n}}{u_{nn}}$
  • For $i=n-1,\ldots,1$
    • If $u_{ii}=0$, exit
    • Else, set
$$ x_{i}=\frac{1}{u_{ii}}\left(b_{i}-\sum_{j=i+1}^{n}u_{ij}x_{j}\right). $$

Exercise 2

Write a function subs_backward(U,b) whose output $x$ is the solution of the system $Ux=b$, being U a upper triangular matriz and b a column matrix.

Use it to solve the system

$$\left(\begin{array}{rrr} 2 & 1 & 1\\ 0 & 2 & 1\\ 0 & 0 & 2 \end{array}\right) \left(\begin{array}{c} x_1\\ x_2\\ x_3 \end{array}\right)= \left(\begin{array}{r} 9\\ 4\\ 4 \end{array}\right) $$
  • Check the solution computing $Ux$ and comparing it with $b$.
  • Check that the function also works well with the $Ux=b$ system with matrices $L$ and $b$ generated as follows
In [5]:
n = 5
np.random.seed(2)           # for the random numbers to be repeatable
U = np.random.random((n,n)) # generate a random nxn matrix
U = np.triu(U)              # remove elements under diagonal
b = np.random.random((n,))  # generate a random vector of dimension n

print 'U'
pprint.pprint(U)
print 'b' 
pprint.pprint(b)
U
array([[ 0.4359949 ,  0.02592623,  0.54966248,  0.43532239,  0.4203678 ],
       [ 0.        ,  0.20464863,  0.61927097,  0.29965467,  0.26682728],
       [ 0.        ,  0.        ,  0.13457995,  0.51357812,  0.18443987],
       [ 0.        ,  0.        ,  0.        ,  0.84656149,  0.07964548],
       [ 0.        ,  0.        ,  0.        ,  0.        ,  0.12715997]])
b
array([ 0.59674531,  0.226012  ,  0.10694568,  0.22030621,  0.34982629])
In [6]:
%run Exercise2.py
-------------FIRST SYSTEM-------------

 x
array([3, 1, 2])

 Ux
array([9, 4, 4])

 b
array([9, 4, 4])

-------------SECOND SYSTEM-------------

 x
array([  2.08436952e+00,   6.53605274e+00,  -2.98103215e+00,
         1.41246173e-03,   2.75107237e+00])

 Ux
array([ 0.59674531,  0.226012  ,  0.10694568,  0.22030621,  0.34982629])

 b
array([ 0.59674531,  0.226012  ,  0.10694568,  0.22030621,  0.34982629])

Gaussian elimination

Idea of Gauss' method is to transform the original system into an upper triangular system with the same solution. To do this, we may use the following operations:

  • We add a row multiplied by a constant to other $f_i\rightarrow f_i +\lambda f_i,\quad i\ne j$
  • We can interchange rows.

Once the matrix is triangular we can solve the system with backward elimination.

In general, it is necessary to allow the swapping of rows, but we will code first the algorithm without it.

Algorithm

  • For $k=2,\ldots,n$
    • If $a_{kk}=0$, exit
    • Else, for $i=k+1,\ldots,n,\quad \quad b_{i}=b_{i}-\dfrac{a_{ik}}{a_{kk}}b_{k}. $
      • For $i=k+1,\ldots,n, \quad a_{ij}=a_{ij}-\dfrac{a_{ik}}{a_{kk}}a_{ik}. $

Exercise 3

Write a function triang(A,b) whose output are the transformed upper triangular matrix U and vector c of a given matrix A and right hand side of the system b.

Use it for the matrices of the system $Ax=b$

$$\left(\begin{array}{rrr} 2 & 1 & 1\\ 1 & 2 & 1\\ 1 & 1 & 2 \end{array}\right) \left(\begin{array}{c} x_1\\ x_2\\ x_3 \end{array}\right)= \left(\begin{array}{r} 2\\ 4\\ 6 \end{array}\right) $$
  • Print the matrices U and c.
  • Solve the equivalent system $Ux=c$ and check the solution computing $Ax$ and comparing it with $b$.
  • Check that the function also works well with the $Ax=b$ system with matrices $A$ and $b$ generated as follows
In [7]:
n = 5
np.random.seed(3)           # for the random numbers to be repeatable
A = np.random.random((n,n)) # generate a random nxn matrix
b = np.random.random((n,))  # generate a random vector of dimension n

print '\n A \n'
pprint.pprint(A)
print '\n b \n' 
pprint.pprint(b)
 A 

array([[ 0.5507979 ,  0.70814782,  0.29090474,  0.51082761,  0.89294695],
       [ 0.89629309,  0.12558531,  0.20724288,  0.0514672 ,  0.44080984],
       [ 0.02987621,  0.45683322,  0.64914405,  0.27848728,  0.6762549 ],
       [ 0.59086282,  0.02398188,  0.55885409,  0.25925245,  0.4151012 ],
       [ 0.28352508,  0.69313792,  0.44045372,  0.15686774,  0.54464902]])

 b 

array([ 0.78031476,  0.30636353,  0.22195788,  0.38797126,  0.93638365])
In [8]:
%run Exercise3.py
-------------TRIANGULARIZATION-------------

 U
array([[ 2.        ,  1.        ,  1.        ],
       [ 0.        ,  1.5       ,  0.5       ],
       [ 0.        ,  0.        ,  1.33333333]])

 c
array([ 2.,  3.,  4.])

-------------FIRST SYSTEM-------------

 x
array([-1.,  1.,  3.])

 Ax
array([ 2.,  4.,  6.])

 b
array([ 2.,  4.,  6.])

-------------SECOND SYSTEM-------------

 x
array([ 0.94759503,  2.02175037,  0.70006481,  1.30635509, -2.28937578])

 Ax
array([ 0.78031476,  0.30636353,  0.22195788,  0.38797126,  0.93638365])

 b
array([ 0.78031476,  0.30636353,  0.22195788,  0.38797126,  0.93638365])

LU decomposition

We express $A$ as a matrix multiplication of two matrices: a Lower triangular matrix, $L$, and an Upper triangular matrix, $U$,

$$ A=LU. $$

Consider the system

$$ A\mathbf{x}=\mathbf{b}, $$

where $A$ admits the $LU$ decomposition. Then

$$ A\mathbf{x}=\mathbf{b}\Longleftrightarrow LU\mathbf{x}=\mathbf{b} $$

Solving $Ax=b$ is done in two steps

  • Solve $L\mathbf{y}=\mathbf{b}$, by forward substitution, to get $\mathbf{y}$.
  • Solve $U\mathbf{x}=\mathbf{y}$, by backward substitution, to get $\mathbf{x}$.

For the time being, we are not considering swapping rows.


Exercise 4

Use the $LU$ decomposition of $A$ to solve the system $Ax=b$, with

$$ A=\left(\begin{array}{rrr} 2 & 1 & 1\\ 1 & 2 & 2\\ 1 & 1 & 2 \end{array}\right)\quad\quad b=\left(\begin{array}{c} 2\\ 4\\ 6 \end{array}\right) $$
  • Create a function LU(A) that gives the $LU$ decomposition of $A$.
  • Obtain and print the matrices $L$ and $U$.
  • Use the subs_backward and subs_forward functions to solve the resulting systems.
  • Check the solution computing $Ax$ and comparing it with $b$.
  • Check that the function also works well with the $Ax=b$ system with matrices $A$ and $b$ generated as follows
In [9]:
n = 5
np.random.seed(4)           # for the random numbers to be repeatable
A = np.random.random((n,n)) # generate a random nxn matrix
b = np.random.random((n,))  # generate a random vector of dimension n

print '\n A \n'
pprint.pprint(A)
print '\n b \n' 
pprint.pprint(b)
 A 

array([[ 0.96702984,  0.54723225,  0.97268436,  0.71481599,  0.69772882],
       [ 0.2160895 ,  0.97627445,  0.00623026,  0.25298236,  0.43479153],
       [ 0.77938292,  0.19768507,  0.86299324,  0.98340068,  0.16384224],
       [ 0.59733394,  0.0089861 ,  0.38657128,  0.04416006,  0.95665297],
       [ 0.43614665,  0.94897731,  0.78630599,  0.8662893 ,  0.17316542]])

 b 

array([ 0.07494859,  0.60074272,  0.16797218,  0.73338017,  0.40844386])
In [11]:
%run Exercise4.py
U 

array([[ 2.        ,  1.        ,  1.        ],
       [ 0.        ,  1.5       ,  0.5       ],
       [ 0.        ,  0.        ,  1.33333333]])
L 

array([[ 1.        ,  0.        ,  0.        ],
       [ 0.5       ,  1.        ,  0.        ],
       [ 0.5       ,  0.33333333,  1.        ]])

-------------FIRST SYSTEM-------------

 x
array([-1.,  1.,  3.])

 Ax
array([ 2.,  4.,  6.])

 b
array([ 2.,  4.,  6.])

-------------SECOND SYSTEM-------------

 x
array([ 2.72783402,  0.59462753, -2.25221917,  0.34238196, -0.70146043])

 Ux
array([ 0.52790882,  0.93757158,  0.52169612,  0.10819338,  0.15822341])

 b
array([ 0.52790882,  0.93757158,  0.52169612,  0.10819338,  0.15822341])

Iterative methods

Iterative methods are, in general, more efficient than direct methods for solving sparse large systems of linear equations.

We rewrite the problem as

$$ A\mathbf{x}=\mathbf{b}\Longleftrightarrow\mathbf{x}=B\mathbf{x}+\mathbf{c} $$

where $B$ is a matrix of order $n$ and $c$ is a column vector of dimension $n$.

Then, if $\mathbf{x}^{(0)}$ is an initial guess, for $k=1,2,\ldots$

$$ \mathbf{x}^{(k)}=B\mathbf{x}^{(k-1)}+\mathbf{c} $$

Method of Jacobi

Algorithm

  • Choose an initial guess $\mathbf{x}^{(0)}$
  • For $k=1,2,\ldots,MaxIter$
    • For $i=1,2,\ldots,n$ compute $$ x_{i}^{(k)}=\frac{1}{a_{ii}}(b_{i}-\sum_{j=1}^{i-1}a_{ij}x_{j}^{(k-1)}-\sum_{j=i+1}^{n}a_{ij}x_{j}^{(k-1)}) $$
    • If the stopping criterium is satisfied, take $\mathbf{x}^{(k)}$ as approximated solution

Exercise 5

Write a function jacobi(A,b,tol) to solve $Ax=b$ by Jacobi's method.

  • Solve $$ \begin{cases} 5x_{1}+x_{2}-x_{3}-x_{4} & =1\\ x_{1}+4x_{2}-x_{3}+x_{4} & =1\\ x_{1}+x_{2}-5x_{3}-x_{4} & =1\\ x_{1}+x_{2}+x_{3}-4x_{4} & =1 \end{cases} $$ by Jacobi's method, for an absolute tolerance of tol = 1.e-6 using norm 2. That is, we will use as stopping criterium np.linalg.norm(x-xp) < tol, being x the approximate solution in the actual iteration and xp the approximate solution in the previous iteration. Take as initial guess $\mathbf{x^{(0)}=0}.$
  • As output, give the solution x and the number of iterations num_iter.
  • Check that the solution is good comparing it with the solution xs = np.linalg.solve(A,b).
  • Check that the function also works well with the $Ax=b$ system with matrices $A$ and $b$ generated as follows
In [12]:
n = 5
np.random.seed(5)         
A = np.random.random((n,n)) + 2*np.eye(n) 
b = np.random.random((n,))  

print '\n A \n'
pprint.pprint(A)
print '\n b \n' 
pprint.pprint(b)
 A 

array([[ 2.22199317,  0.87073231,  0.20671916,  0.91861091,  0.48841119],
       [ 0.61174386,  2.76590786,  0.51841799,  0.2968005 ,  0.18772123],
       [ 0.08074127,  0.7384403 ,  2.44130922,  0.15830987,  0.87993703],
       [ 0.27408646,  0.41423502,  0.29607993,  2.62878791,  0.57983781],
       [ 0.5999292 ,  0.26581912,  0.28468588,  0.25358821,  2.32756395]])

 b 

array([ 0.1441643 ,  0.16561286,  0.96393053,  0.96022672,  0.18841466])
In [13]:
%run Exercise5.py
-------------FIRST SYSTEM-------------

num_iter =  17

x 
array([ 0.09374986,  0.24999988, -0.09374986, -0.18749994])

xs 
array([ 0.09375,  0.25   , -0.09375, -0.1875 ])

-------------SECOND SYSTEM-------------

num_iter =  40

x 
array([-0.10353572, -0.02483661,  0.37396675,  0.33155212,  0.02860954])

xs 
array([-0.1035359 , -0.02483673,  0.37396662,  0.33155201,  0.02860942])

Method of Gauss-Seidel

Algorithm

  • Choose an initial guess $\mathbf{x}^{(0)}$
  • For $k=1,2,\ldots,MaxIter$
    • For $i=1,2,\ldots,n$ compute $$ x_{i}^{(k)}=\frac{1}{a_{ii}}(b_{i}-\sum_{j=1}^{i-1}a_{ij}x_{j}^{(k)}-\sum_{j=i+1}^{n}a_{ij}x_{j}^{(k-1)}) $$
    • If the stopping criterium is satisfied, take $\mathbf{x}^{(k)}$ as approximated solution

Exercise 6

Write a function gauss_seidel(A,b,tol) to solve $Ax=b$ by Gauss-Seidel's method.

  • Solve the same systems as in the previous exercise using Gauss-Seidel's method with the same tolerance and stopping criterium.
  • Print the same outputs as in the previous exercise and compare the number of iterations.
In [14]:
%run Exercise6.py
-------------FIRST SYSTEM-------------

num_iter =  7

x 
array([ 0.09375005,  0.24999998, -0.09375003, -0.1875    ])

xs 
array([ 0.09375,  0.25   , -0.09375, -0.1875 ])

-------------SECOND SYSTEM-------------

num_iter =  9

x 
array([-0.10353581, -0.02483669,  0.37396662,  0.331552  ,  0.02860939])

xs 
array([-0.1035359 , -0.02483673,  0.37396662,  0.33155201,  0.02860942])