cyqlone develop
Fast, parallel and vectorized solver for linear systems with optimal control structure.
Loading...
Searching...
No Matches
solve-block-tridiagonal.py
1import sys
2import numpy as np
3import numpy.linalg as la
4from scipy.io import loadmat
5
6# Load the data generated by solve-block-tridiagonal.cpp
7data = loadmat("block_tridiagonal_system.mat")
8# Data is stored in Fortran order, so transpose
9M = np.moveaxis(data["M"], -1, 0) # (n, n, N) -> (N, n, n)
10K = np.moveaxis(data["K"], -1, 0) # (n, n, N) -> (N, n, n)
11b = np.moveaxis(data["b"], -1, 0) # (n, 1, N) -> (N, n, 1)
12x = np.moveaxis(data["x"], -1, 0) # (n, 1, N) -> (N, n, 1)
13N, n, _ = M.shape
14
15# Construct the full block tridiagonal system as a dense matrix for verification
16A = np.zeros((N * n, N * n))
17for i in range(N):
18 A[i * n : (i + 1) * n, i * n : (i + 1) * n] = np.tril(M[i]) + np.tril(M[i], -1).T
19 A[((i + 1) % N) * n : ((i + 1) % N + 1) * n, i * n : (i + 1) * n] = K[i]
20 A[i * n : (i + 1) * n, ((i + 1) % N) * n : ((i + 1) % N + 1) * n] = K[i].T
21
22# Report the condition number and the residual of the solution
23print(f"Number of blocks: {N}")
24print(f"Block size: {n}")
25print(f"Number of variables: {N * n}")
26print(f"Number of nonzeros: {N * n * (n + 1) // 2 + N * n * n}")
27σA = la.eigvalsh(A)
28nrmA = max(σA)
29condA = nrmA / min(σA)
30res = la.norm(A @ x.ravel() - b.ravel())
31nrmx, nrmb = la.norm(x.ravel()), la.norm(b.ravel())
32err = res / (nrmA * nrmx + nrmb)
33print(f"Condition number κ(A) = {condA:.17e}")
34print(f"Relative residual ‖Ax-b‖/‖b‖ = {res / nrmb:.17e}")
35print(f" ‖Ax-b‖")
36print(f"Backward error η = ────────── = {err:.17e}")
37print(f" ‖A‖‖x‖+‖b‖")
38
39sys.exit(0 if err < 10 * np.finfo(M.dtype).eps * N * n else 1)