Skew-Hermitian matrices

SkewHermitian(A) wraps an existing matrix A, which must already be skew-Hermitian, in the SkewHermitian type (a subtype of AbstractMatrix), which supports fast specialized operations noted below. You can use the function isskewhermitian(A) to check whether A is skew-Hermitian (A == -A').

SkewHermitian(A) requires that A == -A' and throws an error if it is not. Alternatively, you can use the funcition skewhermitian(A) to take the skew-Hermitian part of A, defined by (A - A')/2, and wrap it in a SkewHermitian view. The function skewhermitian!(A) does the same operation in-place on A (overwriting A with its skew-Hermitian part).

Here is a basic example to initialize a SkewHermitian matrix:

julia> using SkewLinearAlgebra, LinearAlgebra

julia> A = [0 2 -7 4; -2 0 -8 3; 7 8 0 1;-4 -3 -1 0]
3×3 Matrix{Int64}:
  0  2 -7  4
 -2  0 -8  3
  7  8  0  1
  -4 -3 -1 0

julia> isskewhermitian(A)
true

julia> A = SkewHermitian(A)
4×4 SkewHermitian{Int64, Matrix{Int64}}:
  0   2  -7  4
 -2   0  -8  3
  7   8   0  1
 -4  -3  -1  0

Basic linear-algebra operations are supported:

julia> tr(A)
0

julia> det(A)
81.0

julia> inv(A)
4×4 SkewHermitian{Float64, Matrix{Float64}}:
  0.0        0.111111  -0.333333  -0.888889
 -0.111111   0.0        0.444444   0.777778
  0.333333  -0.444444   0.0        0.222222
  0.888889  -0.777778  -0.222222   0.0

julia> x=[1;2;3;4]
4-element Vector{Int64}:
 1
 2
 3
 4

julia> A\x
4-element Vector{Float64}:
 -4.333333333333334
  4.333333333333334
  0.3333333333333336
 -1.3333333333333333

A SkewHermitian matrix A is simply a wrapper around an underlying matrix (in which both the upper and lower triangles are stored, despite the redundancy, to support fast matrix operations). You can extract this underlying matrix with the Julia parent(A) function (this does not copy the data: mutating the parent will modify A). Alternatively, you can copy the data to an ordinary Matrix (2d Array) with Matrix(A).

Operations on SkewHermitian

The SkewHermitian type supports the basic operations for any Julia AbstractMatrix (indexing, iteration, multiplication, addition, scaling, and so on). Matrix–matrix and matrix–vector multiplications are performed using the underlying parent matrix, so they are fast.

We try to preserve the SkewHermitian wrapper, if possible. For example, adding two SkewHermitian matrices or scaling by a real number yields another SkewHermitian matrix. Similarly for real(A), conj(A), inv(A), or -A.

The package provides several optimized methods for SkewHermitian matrices, based on the functions defined by the Julia LinearAlgebra package:

  • Tridiagonal reduction: hessenberg
  • Eigensolvers: eigen, eigvals (also schur, svd, svdvals)
  • Trigonometric functions:exp, cis,cos,sin,sinh,cosh,sincos

We also define the following new functions for real skew-symmetric matrices only:

Skew-Hermitian tridiagonal matrices

In the special case of a tridiagonal skew-Hermitian matrix, many calculations can be performed very quickly, typically with $O(n)$ operations for an $n\times n$ matrix. Such optimizations are supported by the SkewLinearAlgebra package using the SkewHermTridiagonal matrix type.

A complex tridiagonal skew-Hermitian matrix is of the form:

\[A=\left(\begin{array}{ccccc} id_{1} & -e_{1}^{*}\\ e_{1} & id_{2} & -e_{2}^{*}\\ & e_{2} & id_{3} & \ddots\\ & & \ddots & \ddots & -e_{n-1}^{*}\\ & & & e_{n-1} & id_{n} \end{array}\right)=-A^{*}\]

with purely imaginary diagonal entries $id_k$. This is represented in the SkewLinearAlgebra package by calling the SkewHermTridiagonal(ev,dvim) constructor, where ev is the (complex) vector of $n-1$ subdiagonal entries $e_k$ and dvim is the (real) vector of $n$ diagonal imaginary parts $d_k$:

julia> SkewHermTridiagonal([1+2im,3+4im],[5,6,7])
3×3 SkewHermTridiagonal{Complex{Int64}, Vector{Complex{Int64}}, Vector{Int64}}:
 0+5im  -1+2im     ⋅
 1+2im   0+6im  -3+4im
   ⋅     3+4im   0+7im

In the case of a real matrix, the diagonal entries are zero, and the matrix takes the form:

\[\text{real }A=\left(\begin{array}{ccccc} 0 & -e_{1}\\ e_{1} & 0 & -e_{2}\\ & e_{2} & 0 & \ddots\\ & & \ddots & \ddots & -e_{n-1}\\ & & & e_{n-1} & 0 \end{array}\right)=-A^{T}\]

In this case, you need not store the zero diagonal entries, and can simply call SkewHermTridiagonal(ev) with the real vector ev of the $n-1$ subdiagonal entries:

julia> A = SkewHermTridiagonal([1,2,3])
4×4 SkewHermTridiagonal{Int64, Vector{Int64}, Nothing}:
 ⋅  -1   ⋅   ⋅
 1   ⋅  -2   ⋅
 ⋅   2   ⋅  -3
 ⋅   ⋅   3   ⋅

(Notice that zero values that are not stored (“structural zeros”) are shown as a .)

A SkewHermTridiagonal matrix can also be converted to the LinearAlgebra.Tridiagonal type in the Julia standard library:

julia> Tridiagonal(A)
4×4 Tridiagonal{Int64, Vector{Int64}}:
 0  -1   ⋅   ⋅
 1   0  -2   ⋅
 ⋅   2   0  -3
 ⋅   ⋅   3   0

which may support a wider range of linear-algebra functions, but does not optimized for the skew-Hermitian structure.

Operations on SkewHermTridiagonal

The SkewHermTridiagonal type is modeled on the LinearAlgebra.SymTridiagonal type in the Julia standard library) and supports typical matrix operations (indexing, iteration, scaling, and so on).

The package provides several optimized methods for SkewHermTridiagonal matrices, based on the functions defined by the Julia LinearAlgebra package:

  • Matrix-vector A*x and dot(x,A,y) products; also solves A\x (via conversion to Tridiagonal)
  • Eigensolvers: eigen, eigvals (also svd, svdvals)
  • Trigonometric functions:exp, cis,cos,sin,sinh,cosh,sincos

Types Reference

SkewLinearAlgebra.SkewHermitianType
SkewHermitian(A) <: AbstractMatrix

Construct a SkewHermitian view of the skew-Hermitian matrix A (A == -A'), which allows one to exploit efficient operations for eigenvalues, exponentiation, and more.

Takes "ownership" of the matrix A. See also skewhermitian, which takes the skew-hermitian part of A, and skewhermitian!, which does this in-place, along with isskewhermitian which checks whether A == -A'.

source
SkewLinearAlgebra.SkewHermTridiagonalType
SkewHermTridiagonal(ev::V, dvim::Vim) where {V <: AbstractVector, Vim <: AbstractVector{<:Real}}

Construct a skewhermitian tridiagonal matrix from the subdiagonal (ev) and the imaginary part of the main diagonal (dvim). The result is of type SkewHermTridiagonal and provides efficient specialized eigensolvers, but may be converted into a regular matrix with convert(Array, _) (or Array(_) for short).

Examples

julia> ev = complex.([7, 8, 9] , [7, 8, 9])
3-element Vector{Complex{Int64}}:
 7 + 7im
 8 + 8im
 9 + 9im
 julia> dvim =  [1, 2, 3, 4]
 4-element Vector{Int64}:
  1
  2
  3
  4
julia> SkewHermTridiagonal(ev, dvim)
4×4 SkewHermTridiagonal{Complex{Int64}, Vector{Complex{Int64}}, Vector{Int64}}:
 0+1im -7+7im  0+0im  0+0im
 7-7im  0+2im -8+8im  0+0im
 0+0im -8+8im  0+3im -9+9im
 0+0im  0+0im  9+9im  0+4im
source
SkewHermTridiagonal(A::AbstractMatrix)

Construct a skewhermitian tridiagonal matrix from first subdiagonal and main diagonal of the skewhermitian matrix A.

Examples

julia> A = [1 2 3; 2 4 5; 3 5 6]
3×3 Matrix{Int64}:
 1  2  3
 2  4  5
 3  5  6
julia> SkewHermTridiagonal(A)
3×3 SkewHermTridiagonal{Int64, Vector{Int64}}:
 0 -2  0
 2  0 -5
 0  5  0
source