Fast matrix multiplication and division for Toeplitz, Hankel and circulant matrices in Julia
Multiplication of large matrices and sqrt, inv, LinearAlgebra.eigvals,
LinearAlgebra.ldiv!, and LinearAlgebra.pinv for circulant matrices
are computed with FFTs.
To be able to use these methods, you have to install and load a package that implements
the AbstractFFTs.jl interface such
as FFTW.jl:
using FFTWIf you perform multiple calculations with FFTs, it can be more efficient to
initialize the required arrays and plan the FFT only once. You can precompute
the FFT factorization with LinearAlgebra.factorize and then use the factorization
for the FFT-based computations.
A Toeplitz matrix has constant diagonals. It can be constructed using
Toeplitz(vc,vr)
Toeplitz{T}(vc,vr)where vc are the entries in the first column and vr are the entries in the first row, where vc[1] must equal vr[1]. For example.
Toeplitz(1:3, [1.,4.,5.])is a sparse representation of the matrix
[ 1.0 4.0 5.0
2.0 1.0 4.0
3.0 2.0 1.0 ]SymmetricToeplitz, Circulant, UpperTriangularToeplitz and LowerTriangularToeplitz only store one vector. By convention, Circulant stores the first column rather than the first row. They are constructed using TYPE(v) where TYPE∈{SymmetricToeplitz, Circulant, UpperTriangularToeplitz, LowerTriangularToeplitz}.
A Hankel matrix has constant anti-diagonals, for example,
[ 1 2 3
2 3 4 ]There are a few ways to construct the above Hankel{Int}:
Hankel([1,2,3,4], (2,3)) # Hankel(v, (h,w))Hankel([1,2,3,4], 2,3) # Hankel(v, h, w)Hankel([1,2], [2,3,4]) # Hankel(vc, vr)
Note that the width is usually useless, since ideally, w=length(v)-h+1. It exists for infinite Hankel matrices. Its existence also means that v could be longer than necessary. Hankel(v), where the size is not given, returns Hankel(v, (l+1)÷2, (l+1)÷2) where l=length(v).
The reverse can transform between Hankel and Toeplitz. It is used to achieve fast Hankel algorithms.
Full list
- ✓ implemented
- ✗ error
- _ fall back to
Matrix
| Toeplitz | Symmetric~ | Circulant | UpperTriangular~ | LowerTriangular~ | Hankel | |
|---|---|---|---|---|---|---|
| getindex | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| .vc | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| .vr | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| size | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| copy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| similar | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| zero | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| real | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| imag | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| fill! | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ |
| conj | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| transpose | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| adjoint | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| tril! | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ |
| triu! | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ |
| tril | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| triu | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| + | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| - | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| scalar mult |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| == | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| issymmetric | ||||||
| istriu | ||||||
| istril | ||||||
| iszero | ✓ | ✓ | ✓ | ✓ | ✓ | |
| isone | ||||||
| diag | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| copyto! | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| reverse | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| broadcast | ||||||
| broadcast! |
Note that scalar multiplication, conj, + and - could be removed once broadcast is implemented.
reverse(Hankel) returns a Toeplitz, while reverse(AbstractToeplitz) returns a Hankel.
| Toeplitz | Symmetric~ | Circulant | UpperTriangular~ | LowerTriangular~ | Hankel | |
|---|---|---|---|---|---|---|
| from AbstractVector | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| from AbstractMatrix | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| from AbstractToeplitz | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| to supertype | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| to Toeplitz | - | ✓ | ✓ | ✓ | ✓ | ✗ |
| to another eltype | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
When constructing Toeplitz from a matrix, the first row and the first column will be considered as vr and vc. Note that vr and vc are copied in construction to avoid the cases where they share memory. If you don't want copying, construct using vectors directly.
When constructing SymmetricToeplitz or Circulant from AbstractMatrix, a second argument shall specify whether the first row or the first column is used. For example, for A = [1 2; 3 4],
SymmetricToeplitz(A,:L)gives[1 3; 3 1], whileSymmetricToeplitz(A,:U)gives[1 2; 2 1].
For backward compatibility and consistency with LinearAlgebra.Symmetric,
SymmetricToeplitz(A) = SymmetricToeplitz(A, :U)
Circulant(A) = Circulant(A, :L)Hankel constructor also accepts the second argument, :L denoting the first column and the last row while :U denoting the first row and the last column.
Symmetric, UpperTriangular and LowerTriangular from LinearAlgebra are also overloaded for convenience.
Symmetric(T::Toeplitz) = SymmetricToeplitz(T)
UpperTriangular(T::Toeplitz) = UpperTriangularToeplitz(T)
LowerTriangular(T::Toeplitz) = LowerTriangularToeplitz(T)TriangularToeplitz is reserved for backward compatibility.
TriangularToeplitz = Union{UpperTriangularToeplitz,LowerTriangularToeplitz}The old interface is implemented by
getproperty(UpperTriangularToeplitz,:uplo) = :U
getproperty(LowerTriangularToeplitz,:uplo) = :LThis type is obsolete and will not be updated for features. Despite that, backward compatibility should be maintained. Codes that were using TriangularToeplitz should still work.
Methods in this section are not exported.
_vr(A::AbstractMatrix) returns the first row as a vector.
_vc(A::AbstractMatrix) returns the first column as a vector.
_vr and _vc are implemented for AbstractToeplitz as well. They are used to merge similar codes for AbstractMatrix and AbstractToeplitz.
_circulate(v::AbstractVector) converts between the vr and vc of a Circulant.
isconcrete(A::Union{AbstractToeplitz,Hankel}) decides whether the stored vector(s) are concrete. It calls Base.isconcretetype.