Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

Iterative method to solve linear system

Note: Most of the code are copied from others. Not wrtitten by me.

[]: https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/ “Julia docs”
[]: https://jermwatt.github.io/machine_learning_refined/notes/16_Linear_algebra/16_5_Norms.html “norm”

Prerequisite

Norms of Vectors and Matrices

image-20231121160928540

image-20231121161021061

Examples

image-20231121163639682

image-20231121163658693

image-20231121163713769

image-20231121163734695

image-20231121164304761

Matrix norm:

image-20231121164543925

Calculate the norm:

using LinearAlgebra
A = ##input matrix/vector
println(norm(A))

Eigenvalues and Eigenvectors => Spectral radius

image-20231121164918046

image-20231121165138203

Jacobi method

See: https://www.foopiew.com/spectral-radius-jacobi-method/

Pre: determine the spectral radius

using LinearAlgebra
A = [4 1 2; 3 5 1; 1 1 3];
D = Diagonal(A); # Diagonal matrix
R = A - D; # Off-diagonal matrix
G = -inv(D)*R; # Iteration matrix
eigenvalues=eigvals(G) # Compute the eigenvalues
spectral_radius = maximum(abs.(eigenvalues)) # Compute the spectral radius
println("Spectral Radius: ", spectral_radius)

Solve

using LinearAlgebra
using Plots

# ==============================
# Jacobi linear solver algorithm
# ==============================
function jacobi_solve(A::Matrix{Float64}, b::Vector{Float64}, 
    x0::Vector{Float64}, tol::Float64,
    zeroTolerance::Float64, max_iters::Int)
    
    # Number of unknowns
    # ------------------
    n = length(b)
    
    # Check for near-zero diagonal entries
    # ------------------------------------
    if any(abs.(diag(A)) .< zeroTolerance)
        error("Near-zero diagonal entry found in the matrix.")
    end

    # Solution vector initialisation
    # ------------------------------
    x = copy(x0)
    x_new = zeros(n)

    # Plot vector initialisation
    # --------------------------
    errors = Float64[]
    
    # Perform iteration
    # -----------------
    for iter = 1:max_iters
        # Loop rows
        for i = 1:n
            x_new[i] = (b[i] - dot(A[i, :], x) + A[i, i] * x[i]) / A[i, i]
        end
        
        # Evaluate error
        err = norm(x_new - x)
        push!(errors, err)  # Store the current error

        # Check for convergence
        if err < tol
            return x_new, iter, errors
        end

        # Update the solution vector
        x .= x_new
    end
    
    error("Maximum number of iterations reached without convergence.")
end

# =============
# Problem setup
# =============

# A = [4.0 1.0 2.0; 3.0 5.0 1.0; 1.0 1.0 3.0]
# b = [1.0; -2.0; 0.0]

## the following matrix is from the turorial 

A = [3.0 1.0 -1.0; 2.0 4.0 1.0; -1.0 2.0 5.0];
b = [4.0; 1.0; 1.0]

x0 = zeros(length(b)) # Initial guess for solution vector 

# ==================
# Numerical settings
# ==================
tolerance = 1E-8
zeroTolerance = 1E-12
iterMax = 500

# =====
# Solve
# =====
try
    x, niter, errors = jacobi_solve(A, b, x0, tolerance, zeroTolerance, iterMax)
    println("Converged in $niter iterations. Solution: $x")

    # Plotting error vs iteration number
    plot(1:niter, errors, yscale=:log10, xlabel="Iteration Number", ylabel="Error (log scale)", title="Convergence of Jacobi Method", legend=false, line=:line, marker=:circle)
    
catch error
    println(error)
end

Gauss Seidel method

See:https://www.foopiew.com/linear-solver-gauss-seidel-method/

Pre: determine the spectral radius

## for computing the spectral radius

using LinearAlgebra

A = [4 1 2; 3 5 1; 1 1 3];
D = Diagonal(A); # Diagonal part
L = tril(A, -1); # Strictly lower triangle part
U = triu(A, 1); # Strictly upper triangle part
G = -inv(L+D)*U; # Iteration Matrix
eigenvalues=eigvals(G) # Compute the eigenvalues
spectral_radius = maximum(abs.(eigenvalues)) # Compute the spectral radius

println("Spectral Radius: ", spectral_radius)

Solve

using LinearAlgebra
using Plots

# ====================================
# Gauss-Seidel linear solver algorithm
# ====================================
function gauss_seidel_solve(A::Matrix{Float64}, b::Vector{Float64}, x0::Vector{Float64}, tol::Float64, zeroTolerance::Float64, max_iters::Int)
    
    # Number of unknowns
    # ------------------
    n = length(b)
    
    # Check for near-zero diagonal entries
    # ------------------------------------
    if any(abs.(diag(A)) .< zeroTolerance)
        error("Near-zero diagonal entry found in the matrix.")
    end

    # Solution vector initialisation
    # ------------------------------
    x = copy(x0)

    # Plot vector initialisation
    # --------------------------
    errors = Float64[]
    
    # Perform iteration
    # -----------------
    for iter = 1:max_iters
        x_old = copy(x)
        
        # Loop rows
        for i = 1:n
            sum1 = dot(A[i, 1:i-1], x[1:i-1])
            sum2 = dot(A[i, i+1:end], x_old[i+1:end])
            x[i] = (b[i] - sum1 - sum2) / A[i, i]
        end
        
        # Evaluate error
        err = norm(x - x_old)
        push!(errors, err)  # Store the current error

        # Check for convergence
        if err < tol
            return x, iter, errors
        end
    end
    
    error("Maximum number of iterations reached without convergence.")
end


# =============
# Problem setup
# =============
# A = [4 1 2; 3 5 1; 1 1 3];
# b = [1.0; -2.0; 0.0]

A = [3 1 -1; 2 4 1; -1 2 5];
b = [4.0; 1.0; 1.0]
# Processing matrix and vectors 
A = convert(Matrix{Float64}, A) # Ensure they are not integers
x0 = zeros(length(b)) # Initial guess for solution Vector

# ==================
# Numerical settings
# ==================
tolerance = 1E-8
zeroTolerance = 1E-12
iterMax = 500

# =====
# Solve
# =====
try
    x, niter, errors = gauss_seidel_solve(A, b, x0, tolerance, zeroTolerance, iterMax)
    println("Converged in $niter iterations. Solution: $x")

    # Plotting error vs iteration number
    plot(1:niter, errors, yscale=:log10, xlabel="Iteration Number", ylabel="Error (log scale)", title="Convergence of Gauss-Seidel Method", legend=false, line=:line, marker=:circle, xticks=1:niter)
    
catch error
    println(error)
end

Successive over-relaxation method

See:https://www.foopiew.com/linear-solver-successive-over/

1

using LinearAlgebra
using Plots

# ==================================================
# Successive over-relaxation linear solver algorithm
# ==================================================
function SOR_solve(A::Matrix{Float64}, b::Vector{Float64}, x0::Vector{Float64}, ω::Float64, tol::Float64, zeroTolerance::Float64, max_iters::Int)
    
    # Number of unknowns
    # ------------------
    n = length(b)
    
    # Check for near-zero diagonal entries
    # ------------------------------------
    if any(abs.(diag(A)) .< zeroTolerance)
        error("Near-zero diagonal entry found in the matrix.")
    end

    # Solution vector initialisation
    # ------------------------------
    x = copy(x0)

    # Plot vector initialisation
    # --------------------------
    errors = Float64[]
    
    # Perform iteration
    # -----------------
    for iter = 1:max_iters
        x_old = copy(x)
        
        # Loop rows
        for i = 1:n
            sum1 = dot(A[i, 1:i-1], x[1:i-1])
            sum2 = dot(A[i, i+1:end], x_old[i+1:end])
            x[i] = (1-ω)*x[i] + ω*(b[i] - sum1 - sum2) / A[i, i]
        end
        
        # Evaluate error
        err = norm(x - x_old)
        push!(errors, err)  # Store the current error

        # Check for convergence
        if err < tol
            return x, iter, errors
        end
    end
    
    error("Maximum number of iterations reached without convergence.")
end


# =============
# Problem setup
# =============
A = [4 1 2; 3 5 1; 1 1 3];
b = [1.0; -2.0; 0.0]

# Processing matrix and vectors 
A = convert(Matrix{Float64}, A) # Ensure they are not integers
x0 = zeros(length(b)) # Initial guess for solution Vector

# ==================
# Numerical settings
# ==================
ω=1.05
tolerance = 1E-8
zeroTolerance = 1E-12
iterMax = 500

# =====
# Solve
# =====
try
    x, niter, errors = SOR_solve(A, b, x0, ω, tolerance, zeroTolerance, iterMax)
    println("Converged in $niter iterations. Solution: $x")

    # Plotting error vs iteration number
    plot(1:niter, errors, yscale=:log10, xlabel="Iteration Number", ylabel="Error (log scale)", title="Convergence of SOR Method", legend=false, line=:line, marker=:circle)
    
catch error
    println(error)
end

2

using LinearAlgebra
using Plots

# ==================================================
# Successive over-relaxation linear solver algorithm
# ==================================================
function SOR_solve(A::Matrix{Float64}, b::Vector{Float64}, 
    x0::Vector{Float64}, ω::Float64, tol::Float64, 
    zeroTolerance::Float64, max_iters::Int)
    
    # Number of unknowns
    # ------------------
    n = length(b)
    
    # Check for near-zero diagonal entries
    # ------------------------------------
    if any(abs.(diag(A)) .< zeroTolerance)
        error("Near-zero diagonal entry found in the matrix.")
    end

    # Solution vector initialisation
    # ------------------------------
    x = copy(x0)

    # Plot vector initialisation
    # --------------------------
    errors = Float64[]
    
    # Perform iteration
    # -----------------
    for iter = 1:max_iters
        x_old = copy(x)
        
        # Loop rows
        for i = 1:n
            sum1 = dot(A[i, 1:i-1], x[1:i-1])
            sum2 = dot(A[i, i+1:end], x_old[i+1:end])
            x[i] = (1-ω)*x[i] + ω*(b[i] - sum1 - sum2) / A[i, i]
        end
        
        # Evaluate error
        err = norm(x - x_old)
        push!(errors, err)  # Store the current error

        # Check for convergence
        if err < tol
            return x, iter, errors
        end
    end
    
    error("Maximum number of iterations reached without convergence.")
end


# =============
# Problem setup
# =============
A = [4 1 2; 3 5 1; 1 1 3];
b = [1.0; -2.0; 0.0]

# Processing matrix and vectors 
A = convert(Matrix{Float64}, A) # Ensure they are not integers
x0 = zeros(length(b)) # Initial guess for solution Vector

# ==================
# Numerical settings
# ==================
tolerance = 1E-8
zeroTolerance = 1E-12
iterMax = 500

# =====
# Solve
# =====
ω_list = range(start=0.1, step=0.05, stop=1.6)
niter_list = Float64[]
for iω in eachindex(ω_list)
    ω=ω_list[iω]
    try
        x, niter, errors = SOR_solve(A, b, x0, ω, tolerance, zeroTolerance, iterMax)
        println("Converged in $niter iterations. Solution: $x")
        push!(niter_list, niter)  # Store the number of iterations to converge
    catch error
        println(error)
    end
end


 # Plot ω-niter
 plot(ω_list, niter_list, xlabel="Relaxation Factor \\omega", ylabel="Number of Iterations to Converge", title="Effect of \\omega in SOR Method", legend=false, line=:line, marker=:circle)

评论