versioninfo()
Julia Version 1.1.0 Commit 80516ca202 (2019-01-21 21:24 UTC) Platform Info: OS: macOS (x86_64-apple-darwin14.5.0) CPU: Intel(R) Core(TM) i7-6920HQ CPU @ 2.90GHz WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-6.0.1 (ORCJIT, skylake) Environment: JULIA_EDITOR = code
We consider computer algorithms for solving linear equations $\mathbf{A} \mathbf{x} = \mathbf{b}$, a ubiquitous task in statistics.
Idea: turning original problem into an easy one, e.g., triangular system.
To solve $\mathbf{A} \mathbf{x} = \mathbf{b}$, where $\mathbf{A} \in \mathbb{R}^{n \times n}$ is lower triangular
$$ \begin{pmatrix} a_{11} & 0 & \cdots & 0 \\ a_{21} & a_{22} & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ a_{n1} & a_{n2} & \cdots & a_{nn} \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \\ \vdots \\ x_n \end{pmatrix} = \begin{pmatrix} b_1 \\ b_2 \\ \vdots \\ b_n \end{pmatrix}. $$$1 + 3 + 5 + \cdots + (2n-1) = n^2$ flops.
$\mathbf{A}$ can be accessed by row ($ij$ loop) or column ($ji$ loop).
To solve $\mathbf{A} \mathbf{x} = \mathbf{b}$, where $\mathbf{A} \in \mathbb{R}^{n \times n}$ is upper triangular
$$
\begin{pmatrix}
a_{11} & \cdots & a_{1,n-1} & a_{1n} \\
\vdots & \ddots & \vdots & \vdots \\
0 & \cdots & a_{n-1,n-1} & a_{n-1,n} \\
0 & 0 & 0 & a_{nn}
\end{pmatrix}
\begin{pmatrix}
x_1 \\ \vdots \\ x_{n-1} \\ x_n
\end{pmatrix} = \begin{pmatrix}
b_1 \\ \vdots \\ b_{n-1} \\ b_n
\end{pmatrix}.
$$
$n^2$ flops.
$\mathbf{A}$ can be accessed by row ($ij$ loop) or column ($ji$ loop).
using LinearAlgebra, Random
Random.seed!(123) # seed
n = 5
A = randn(n, n)
b = randn(n)
# a random matrix
A
5×5 Array{Float64,2}: 1.19027 -0.664713 -0.339366 0.368002 -0.979539 2.04818 0.980968 -0.843878 -0.281133 0.260402 1.14265 -0.0754831 -0.888936 -0.734886 -0.468489 0.459416 0.273815 0.327215 -0.71741 -0.880897 -0.396679 -0.194229 0.592403 -0.77507 0.277726
Al = LowerTriangular(A) # does not create extra matrix
5×5 LowerTriangular{Float64,Array{Float64,2}}: 1.19027 ⋅ ⋅ ⋅ ⋅ 2.04818 0.980968 ⋅ ⋅ ⋅ 1.14265 -0.0754831 -0.888936 ⋅ ⋅ 0.459416 0.273815 0.327215 -0.71741 ⋅ -0.396679 -0.194229 0.592403 -0.77507 0.277726
fieldnames(typeof(Al))
(:data,)
Al.data
5×5 Array{Float64,2}: 1.19027 -0.664713 -0.339366 0.368002 -0.979539 2.04818 0.980968 -0.843878 -0.281133 0.260402 1.14265 -0.0754831 -0.888936 -0.734886 -0.468489 0.459416 0.273815 0.327215 -0.71741 -0.880897 -0.396679 -0.194229 0.592403 -0.77507 0.277726
# same data
pointer(Al.data), pointer(A)
(Ptr{Float64} @0x000000010f12c170, Ptr{Float64} @0x000000010f12c170)
Al \ b # dispatched to BLAS function for triangular solve
5-element Array{Float64,1}: 1.28031359532452 -4.485407033333146 5.326125412123139 0.4468195081389211 -3.091688163812573
# or use BLAS wrapper directly
BLAS.trsv('L', 'N', 'N', A, b)
5-element Array{Float64,1}: 1.28031359532452 -4.485407033333146 5.326125412123139 0.446819508138921 -3.091688163812573
?BLAS.trsv
trsv(ul, tA, dA, A, b)
Return the solution to A*x = b
or one of the other two variants determined by [tA
](@ref stdlib-blas-trans) and [ul
](@ref stdlib-blas-uplo). [dA
](@ref stdlib-blas-diag) determines if the diagonal values are read or are assumed to be all ones.
Eigenvalues of a triangular matrix $\mathbf{A}$ are diagonal entries $\lambda_i = a_{ii}$.
Determinant $\det(\mathbf{A}) = \prod_i a_{ii}$.
The product of two upper (lower) triangular matrices is upper (lower) triangular.
The inverse of an upper (lower) triangular matrix is upper (lower) triangular.
The product of two unit upper (lower) triangular matrices is unit upper (lower) triangular.
The inverse of a unit upper (lower) triangular matrix is unit upper (lower) triangular.
LowerTriangular, UnitLowerTriangular, UpperTriangular, UnitUpperTriangular.
using BenchmarkTools, LinearAlgebra, Random
Random.seed!(123) # seed
A = randn(1000, 1000);
# if we don't tell Julia it's triangular: O(n^3) complexity
# tril(A) returns a full triangular matrix, same as Matlab
@benchmark eigvals(tril($A))
# if we tell Julia it's triangular: O(n) complexity
@benchmark eigvals(LowerTriangular($A))
BenchmarkTools.Trial: memory estimate: 7.94 KiB allocs estimate: 1 -------------- minimum time: 1.676 μs (0.00% GC) median time: 2.282 μs (0.00% GC) mean time: 3.285 μs (28.33% GC) maximum time: 4.328 ms (99.91% GC) -------------- samples: 10000 evals/sample: 10
# if we don't tell Julia it's triangular: O(n^3) complexity
# tril(A) returns a full triangular matrix, same as Matlab
@benchmark det(tril($A))
BenchmarkTools.Trial: memory estimate: 7.64 MiB allocs estimate: 4 -------------- minimum time: 2.024 ms (0.00% GC) median time: 2.134 ms (0.00% GC) mean time: 2.428 ms (19.43% GC) maximum time: 45.379 ms (96.47% GC) -------------- samples: 2051 evals/sample: 1
# if we tell Julia it's triangular: O(n) complexity
@benchmark det(LowerTriangular($A))
BenchmarkTools.Trial: memory estimate: 7.95 KiB allocs estimate: 2 -------------- minimum time: 2.025 μs (0.00% GC) median time: 2.595 μs (0.00% GC) mean time: 4.003 μs (29.86% GC) maximum time: 4.951 ms (99.90% GC) -------------- samples: 10000 evals/sample: 9
@benchmark det(LowerTriangular(A))
BenchmarkTools.Trial: memory estimate: 7.97 KiB allocs estimate: 3 -------------- minimum time: 2.189 μs (0.00% GC) median time: 2.858 μs (0.00% GC) mean time: 4.186 μs (29.45% GC) maximum time: 5.243 ms (99.89% GC) -------------- samples: 10000 evals/sample: 9
A = rand(5, 5)
5×5 Array{Float64,2}: 0.505563 0.490008 0.364867 0.064762 0.537664 0.372991 0.1636 0.301699 0.614327 0.131543 0.00596027 0.43842 0.607055 0.342615 0.0390959 0.216016 0.891323 0.820768 0.449411 0.31552 0.944432 0.446089 0.967155 0.00461037 0.275575
LowerTriangular(A)
5×5 LowerTriangular{Float64,Array{Float64,2}}: 0.505563 ⋅ ⋅ ⋅ ⋅ 0.372991 0.1636 ⋅ ⋅ ⋅ 0.00596027 0.43842 0.607055 ⋅ ⋅ 0.216016 0.891323 0.820768 0.449411 ⋅ 0.944432 0.446089 0.967155 0.00461037 0.275575
LinearAlgebra.UnitLowerTriangular(A)
5×5 UnitLowerTriangular{Float64,Array{Float64,2}}: 1.0 ⋅ ⋅ ⋅ ⋅ 0.372991 1.0 ⋅ ⋅ ⋅ 0.00596027 0.43842 1.0 ⋅ ⋅ 0.216016 0.891323 0.820768 1.0 ⋅ 0.944432 0.446089 0.967155 0.00461037 1.0