Direct Methods and Matrix Factorizations
Big-O Notation
For a function \(f(N)\) and a positive constant \(C\), we say \(f(N)\) is \(O(g(N))\), if there exist positive constants \(C\) and \(N_0\) such that:
\[ 0 \leq f(N) \leq C \cdot g(N) \quad \text{for all } N \geq N_0 \]
Ask yourself whether the following is a computationally expensive operation as the matrix size increases
Machine Epsilon
For a given datatype, \(\epsilon\) is defined as \(\epsilon = \min_{\delta > 0} \left\{ \delta : 1 + \delta > 1 \right\}\)
machine epsilon for float64 = 2.220446049250313e-16
1 + eps/2 == 1? true
machine epsilon for float32 = 1.1920929e-7
5×5 Matrix{Float64}:
1.29099 -0.327957 0.0416667 -0.00537634 0.000672043
-0.163978 1.31183 -0.166667 0.0215054 -0.00268817
0.0208333 -0.166667 1.29167 -0.166667 0.0208333
-0.00268817 0.0215054 -0.166667 1.31183 -0.163978
0.000672043 -0.00537634 0.0416667 -0.327957 1.29099
Besides sparsity/storage, the real loss is you miss out on algorithms. For example, lets setup the benchmarking code
N = 1000
b = rand(N)
A = Tridiagonal([fill(0.1, N - 2); 0.2], fill(0.8, N), [0.2; fill(0.1, N - 2)])
A_sparse = sparse(A) # sparse but losing tridiagonal structure
A_dense = Array(A) # dropping the sparsity structure, dense 1000x1000
# benchmark solution to system A x = b
benchmark_solve(A, b)
benchmark_solve(A_sparse, b)
benchmark_solve(A_dense, b);
A\b for typeof(A) = Tridiagonal{Float64, Vector{Float64}}
16.600 μs (8 allocations: 47.72 KiB)
A\b for typeof(A) = SparseMatrixCSC{Float64, Int64}
439.200 μs (80 allocations: 1.03 MiB)
A\b for typeof(A) = Matrix{Float64}
14.231 ms (4 allocations: 7.64 MiB)
\[ \begin{aligned} U x &= b\\ U &\equiv \begin{bmatrix} 3 & 1 \\ 0 & 2 \\ \end{bmatrix}, \quad b = \begin{bmatrix} 7 \\ 2 \\ \end{bmatrix} \end{aligned} \]
Solving bottom row for \(x_2\)
\[ 2 x_2 = 2,\quad x_2 = 1 \]
Move up a row, solving for \(x_1\), substituting for \(x_2\)
\[ 3 x_1 + 1 x_2 = 7,\quad 3 x_1 + 1 \times 1 = 7,\quad x_1 = 2 \]
Generalizes to many rows. For \(L\) it is “forward substitution”
\
and functions such as inv
Af \ b = [1.567631093835083, -1.8670423474177864, -0.7020922312927874, 1.0653095651070625]
inv(Af) * b = [1.5676310938350828, -1.8670423474177873, -0.7020922312927873, 1.0653095651070625]
4-element Vector{Float64}:
1.5676310938350828
-1.8670423474177873
-0.7020922312927873
1.0653095651070625
solve
algorithm (without more structure)inv
factorize
is the best it can do given symmetric structureb = rand(N)
factorize(A) |> typeof
cholesky(A) \ b # use the factorization to solve
benchmark_solve(A, b)
benchmark_solve(A_dense, b)
@btime cholesky($A, check = false) \ $b;
A\b for typeof(A) = Symmetric{Float64, Matrix{Float64}}
4.027 ms (8 allocations: 2.16 MiB)
A\b for typeof(A) = Matrix{Float64}
8.041 ms (4 allocations: 1.92 MiB)
3.510 ms (3 allocations: 1.91 MiB)
\[ A = Q \Lambda Q^{-1} \]
\[ \mathbb P \{ X(t + \Delta) = j \,|\, X(t) \} = \begin{cases} q_{ij} \Delta + o(\Delta) & i \neq j\\ 1 + q_{ii} \Delta + o(\Delta) & i = j \end{cases} \]
\[ Q = \begin{bmatrix} -0.1 & 0.1 & 0 & 0 & 0 & 0\\ 0.1 &-0.2 & 0.1 & 0 & 0 & 0\\ 0 & 0.1 & -0.2 & 0.1 & 0 & 0\\ 0 & 0 & 0.1 & -0.2 & 0.1 & 0\\ 0 & 0 & 0 & 0.1 & -0.2 & 0.1\\ 0 & 0 & 0 & 0 & 0.1 & -0.1\\ \end{bmatrix} \]
The \(Q\) is the infinitesimal generator of the stochastic process.
Let \(\pi(t) \in \mathbb{R}^N\) with \(\pi_i(t) \equiv \mathbb{P}[X_t = i\,|\,X_0]\)
Then the probability distribution evolution (Fokker-Planck or KFE), is \[ \frac{d}{dt} \pi(t) = \pi(t) Q,\quad \text{ given }\pi(0) \]
Or, often written as \(\frac{d}{dt} \pi(t) = Q^{\top} \cdot \pi(t)\), i.e. in terms of the “adjoint” of the linear operator \(Q\)
A steady state is then a solution to \(Q^{\top} \cdot \bar{\pi} = 0\)
Lambda = [-0.3732050807568874, -0.29999999999999993, -0.19999999999999998, -0.09999999999999995, -0.026794919243112274, 0.0]
6-element Vector{Float64}:
0.16666666666666657
0.16666666666666657
0.1666666666666667
0.16666666666666682
0.16666666666666685
0.16666666666666663
\[ \rho v = r + Q v \]
typeof(rho * I - Q) = Tridiagonal{Float64, Vector{Float64}}
6-element Vector{Float64}:
38.15384615384615
57.23076923076923
84.92307692307693
115.07692307692311
142.76923076923077
161.84615384615384