6.2. Solving systems of linear equations using LU decomposition#

Systems of linear equations often appear in the topics of numerical analysis and numerical solutions to differential equations. Examples include the solution of the stage values of an implicit Runge-Kutta method and the solution to a boundary value problem using the finite-difference method. The methods that are applied to solve systems of linear equations fall into one of two categories: direct methods that use an algebraic approach and indirect methods that use an iterative approach. Here we will look at some common direct methods.

6.2.1. Systems of linear equations#

A system of linear equations with m equations and n unknowns is expressed as

a11x1+a12x2++a1nxn=b1,a21x1+a22x2++a2nxn=b2,am1x1+am2x2++amnxn=bn,

where xi are the unknown variables, aij are coefficients and bi are constant terms. It is often more convenient to express a system of linear equations as a matrix equation

Ax=b,

where A is the coefficient matrix, x is the variable vector and b is the constant vector

A=(a11a12a1na21a22a2nam1am2amn),x=(x1x2xm),b=(b1b2bm).

The solution (if it exists) is the vector x for which the equation Ax=b is satisfied. The solution to a linear system may be one of the following

  • The system has infinitely many solutions. This usually occurs when the number of unknown variables exceeds the number of equations in the system.

  • The system has a single unique solution.

  • The system has no solution. This usually occurs where one equation in the system contradicts another such that not value of the variables could satisfy both.

6.2.2. Solving systems of linear equations using LU decomposition#

Given a system of linear equations of the form Ax=b where LU decomposition has been applied to the coefficient matrix then since A=LU we have

LUx=b.

Let y=Ux then

Ly=b.

L is a lower triangular matrix so the solution of Ly=b is easily calculated using forward substitution. Once y has been calculated the solution to Ux=y is calculated using back substitution.

The advantage of using LU decomposition to solve systems of linear equations is that once the LU decomposition of the coefficient matrix has been calculated it can be used for any values of the constant vector b. This is unlike Gaussian elimination where row reduction will need to be repeated if the values of b change.

Algorithm 6.2 (Solving systems of linear equations using LU decomposition)

Inputs: A system of linear equations expressed using the coefficient matrix A and variable vector b such that Ax=b.

Inputs: The variable vector x.

  • Calculate the LU decomposition of A to determine L and U such that A=LU.

  • For i=1,,n do

    • yibij=1iijyj

  • xnyiunn

  • For i=n1,,1 do

    • xi1uii(yij=i+1nuijxj)

  • Return x=(x1,x2,,xn).

Example 6.2

Solve the following system using LU decomposition

(130241312)(x1x2x3)=(7111).
Solution (click to show)

We saw in the Example 6.1 above that the LU decomposition of the coefficient matrix is

L=(100210311),U=(1300101001).

Solving Ly=b using forward substitution

(100210311)(y1y2y3)=(7111),

gives

y1=7,y2=112y1=2(7)=25,y3=1+3y1+y2=1+3(7)+1(25)=5.

Solving Ux=y using back substitution

(1300101001)(x1x2x3)=(7255),

gives

x3=5,x2=110(25+x3)=110(25+5)=3,x1=70x33x2=7+9=2.

So the solution is x=(2,3,5).

6.2.3. Code#

The code below defines two functions called forward_substitution() and back_substitution() that calculate perform forward and back substitution. These are used to solve the system of linear equations from Example 6.2.

def forward_substitution(L, b):
    n = L.shape[0]
    x = np.zeros(n)
    for i in range(n):
        sum_ = 0
        for j in range(i):
            sum_ += L[i,j] * x[j]
            
        x[i] = (b[i] - sum_) / L[i,i]
    
    return x


def back_substitution(U, b):
    n = U.shape[0]
    x = np.zeros(n)
    x[-1] = b[-1] / U[-1,-1]
    for i in range(n - 2, -1, -1):
        sum_ = 0
        for j in range(i + 1, n):
            sum_ += U[i,j] * x[j]
            
        x[i] = (b[i] - sum_) / U[i,i]
        
    return x


# Define system
A = np.array([[1, 3, 0], 
              [2, -4, -1], 
              [-3, 1, 2]])
b = np.array([-7, 11, 1])

# Calculate LU decomposition
L, U = lu(A)

# Solve system
y = forward_substitution(L, b)
x = back_substitution(U, y)
print(f"x = {x}")
% Define system
A = [1, 3, 0 ;
     2, -4, -1 ;
     -3, 1, 2 ];
b = [-7 ; 11 ; 1];

% Calculate LUP decomposition
P = partial_pivot(A);
[L, U] = lu(P * A);

% Solve system
y = forward_substitution(L, P * b);
x = back_substitution(U, y)

% --------------------------------------------------------------
function x = forward_substitution(L, b)

n = size(L, 1);
x = zeros(n, 1);
for i = 1 : n
    sum_ = 0;
    for j = 1 : i - 1
        sum_ = sum_ + L(i,j) * x(j);
    end
    x(i) = (b(i) - sum_) / L(i,i);
end

end

% --------------------------------------------------------------
function x = back_substitution(U, b)

n = size(U, 1);
x = zeros(n, 1);
x(n) = b(n) / U(n,n);
for i = n - 1 : -1 : 1
    sum_ = 0
    for j = i + 1 : n
        sum_ = sum_ + U(i,j) * x(j);
    end
    x(i) = (b(i) - sum_) / U(i,i);
end

end