Skip to content

Commit

Permalink
Heisenberg matrices (#775)
Browse files Browse the repository at this point in the history
* Heisenberg matrices

* add docs page

* more testing and addressing review
  • Loading branch information
mateuszbaran authored Dec 16, 2024
1 parent b62ce19 commit c140918
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 0 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.10.9] - unreleased

### Added

* The manifold `HeisenbergMatrices` as the underlying manifold of `HeisenbergGroup`.

### Changed

* `about.md` now also lists contributors of manifolds and a very short history of the package.
Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ makedocs(;
"Generalized Grassmann" => "manifolds/generalizedgrassmann.md",
"Grassmann" => "manifolds/grassmann.md",
"Hamiltonian" => "manifolds/hamiltonian.md",
"Heisenberg matrices" => "manifolds/heisenberg.md",
"Hyperbolic space" => "manifolds/hyperbolic.md",
"Hyperrectangle" => "manifolds/hyperrectangle.md",
"Invertible matrices" => "manifolds/invertible.md",
Expand Down
7 changes: 7 additions & 0 deletions docs/src/manifolds/heisenberg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Heisenberg matrices

```@autodocs
Modules = [Manifolds]
Pages = ["manifolds/HeisenbergMatrices.jl"]
Order = [:type, :function]
```
2 changes: 2 additions & 0 deletions src/Manifolds.jl
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ include("manifolds/FlagOrthogonal.jl")
include("manifolds/FlagStiefel.jl")
include("manifolds/GeneralizedGrassmann.jl")
include("manifolds/GeneralizedStiefel.jl")
include("manifolds/HeisenbergMatrices.jl")
include("manifolds/Hyperbolic.jl")
include("manifolds/Hyperrectangle.jl")
include("manifolds/InvertibleMatrices.jl")
Expand Down Expand Up @@ -639,6 +640,7 @@ export Euclidean,
Grassmann,
HamiltonianMatrices,
HeisenbergGroup,
HeisenbergMatrices,
Hyperbolic,
Hyperrectangle,
InvertibleMatrices,
Expand Down
142 changes: 142 additions & 0 deletions src/manifolds/HeisenbergMatrices.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
@doc raw"""
HeisenbergMatrices{T} <: AbstractDecoratorManifold{𝔽}
Heisenberg matrices `HeisenbergMatrices(n)` is the manifold of ``(n+2)×(n+2)`` matrices [BinzPods:2008](@cite)
```math
\begin{bmatrix} 1 & \mathbf{a} & c \\
\mathbf{0}_n & I_n & \mathbf{b} \\
0 & \mathbf{0}_n^\mathrm{T} & 1 \end{bmatrix}
```
where ``I_n`` is the ``n×n`` unit matrix, ``\mathbf{a}`` is a row vector of length ``n``,
``\mathbf{b}`` is a column vector of length ``n``, ``\mathbf{0}_n`` is the column zero vector
of length ``n``, and ``c`` is a real number.
It is a submanifold of [`Euclidean`](@ref)`(n+2, n+2)` and the manifold of the
[`HeisenbergGroup`](@ref).
# Constructor
HeisenbergMatrices(n::Int; parameter::Symbol=:type)
Generate the manifold of ``(n+2)×(n+2)`` Heisenberg matrices.
"""
struct HeisenbergMatrices{T} <: AbstractDecoratorManifold{ℝ}
size::T
end

function HeisenbergMatrices(n::Int; parameter::Symbol=:type)
size = wrap_type_parameter(parameter, (n,))
return HeisenbergMatrices{typeof(size)}(size)
end

function active_traits(f, ::HeisenbergMatrices, args...)
return merge_traits(IsEmbeddedSubmanifold())
end

function check_point(M::HeisenbergMatrices, p; kwargs...)
n = get_parameter(M.size)[1]
if !isone(p[1, 1])
return DomainError(
p[1, 1],
"The matrix $(p) does not lie on $(M), since p[1, 1] is not equal to 1.",
)
end
if !isone(p[n + 2, n + 2])
return DomainError(
p[n + 2, n + 2],
"The matrix $(p) does not lie on $(M), since p[n+2, n+2] is not equal to 1.",
)
end
if !iszero(p[2:(n + 2), 1])
return DomainError(
norm(iszero(p[2:(n + 2), 1])),
"The matrix $(p) does not lie on $(M), since p[2:(n + 2), 1] is not equal to 0.",
)
end
if !iszero(p[n + 2, 1:(n + 1)])
return DomainError(
norm(iszero(p[n + 2, 1:(n + 1)])),
"The matrix $(p) does not lie on $(M), since p[n + 2, 1:(n+1)] is not equal to 0.",
)
end
if !isapprox(I, p[2:(n + 1), 2:(n + 1)])
return DomainError(
det(p[2:(n + 1), 2:(n + 1)] - I),
"The matrix $(p) does not lie on $(M), since p[2:(n+1), 2:(n+1)] is not an identity matrix.",
)
end

return nothing
end

function check_vector(M::HeisenbergMatrices, p, X; kwargs...)
n = get_parameter(M.size)[1]
if !iszero(X[1, 1])
return DomainError(
X[1, 1],
"The matrix $(X) does not lie in the tangent space of $(M), since X[1, 1] is not equal to 0.",
)
end
if !iszero(X[n + 2, n + 2])
return DomainError(
X[n + 2, n + 2],
"The matrix $(X) does not lie in the tangent space of $(M), since X[n+2, n+2] is not equal to 0.",
)
end
if !iszero(X[2:(n + 2), 1:(n + 1)])
return DomainError(
norm(X[2:(n + 2), 1:(n + 1)]),
"The matrix $(X) does not lie in the tangent space of $(M), since X[2:(n + 2), 1:(n + 1)] is not a zero matrix.",
)
end
return nothing
end

embed(::HeisenbergMatrices, p) = p
embed(::HeisenbergMatrices, p, X) = X

function get_embedding(::HeisenbergMatrices{TypeParameter{Tuple{n}}}) where {n}
return Euclidean(n + 2, n + 2)
end
function get_embedding(M::HeisenbergMatrices{Tuple{Int}})
n = get_parameter(M.size)[1]
return Euclidean(n + 2, n + 2; parameter=:field)
end

"""
is_flat(::HeisenbergMatrices)
Return true. [`HeisenbergMatrices`](@ref) is a flat manifold.
"""
is_flat(M::HeisenbergMatrices) = true

"""
manifold_dimension(M::HeisenbergMatrices)
Return the dimension of [`HeisenbergMatrices`](@ref)`(n)`, which is equal to ``2n+1``.
"""
manifold_dimension(M::HeisenbergMatrices) = 2 * get_parameter(M.size)[1] + 1

function Base.show(io::IO, ::HeisenbergMatrices{TypeParameter{Tuple{n}}}) where {n}
return print(io, "HeisenbergMatrices($(n))")
end
function Base.show(io::IO, M::HeisenbergMatrices{Tuple{Int}})
n = get_parameter(M.size)[1]
return print(io, "HeisenbergMatrices($(n); parameter=:field)")
end

@doc raw"""
Y = Weingarten(M::HeisenbergMatrices, p, X, V)
Weingarten!(M::HeisenbergMatrices, Y, p, X, V)
Compute the Weingarten map ``\mathcal W_p`` at `p` on the [`HeisenbergMatrices`](@ref) `M`
with respect to the tangent vector ``X \in T_p\mathcal M`` and the normal vector
``V \in N_p\mathcal M``.
Since this a flat space by itself, the result is always the zero tangent vector.
"""
Weingarten(::HeisenbergMatrices, p, X, V)

Weingarten!(::HeisenbergMatrices, Y, p, X, V) = fill!(Y, 0)
47 changes: 47 additions & 0 deletions test/manifolds/heisenberg_matrices.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using LinearAlgebra, Manifolds, ManifoldsBase, Test

@testset "Heisenberg matrices" begin
M = HeisenbergMatrices(1)
@test repr(M) == "HeisenbergMatrices(1)"
@test is_flat(M)

pts = [
[1.0 2.0 3.0; 0.0 1.0 -1.0; 0.0 0.0 1.0],
[1.0 4.0 -3.0; 0.0 1.0 3.0; 0.0 0.0 1.0],
[1.0 -2.0 1.0; 0.0 1.0 1.1; 0.0 0.0 1.0],
]
Xpts = [
[0.0 2.0 3.0; 0.0 0.0 -1.0; 0.0 0.0 0.0],
[0.0 4.0 -3.0; 0.0 0.0 3.0; 0.0 0.0 0.0],
[0.0 -2.0 1.0; 0.0 0.0 1.1; 0.0 0.0 0.0],
]

@test check_point(M, [0.0 2.0 3.0; 0.0 1.0 -1.0; 0.0 0.0 1.0]) isa DomainError
@test check_point(M, [1.0 2.0 3.0; 0.0 1.0 -1.0; 1.0 0.0 1.0]) isa DomainError
@test check_point(M, [1.0 2.0 3.0; 0.0 2.0 -1.0; 0.0 0.0 1.0]) isa DomainError
@test check_point(M, [1.0 2.0 3.0; 0.0 1.0 -1.0; 0.0 0.0 2.0]) isa DomainError
@test check_point(M, [1.0 2.0 3.0; 0.0 1.0 -1.0; 0.0 1.0 1.0]) isa DomainError
@test check_point(M, [1.0 2.0 3.0; 1.0 1.0 -1.0; 0.0 0.0 1.0]) isa DomainError

@test check_vector(M, pts[1], [1.0 2.0 3.0; 0.0 0.0 -1.0; 0.0 0.0 0.0]) isa DomainError
@test check_vector(M, pts[1], [0.0 2.0 3.0; 1.0 0.0 -1.0; 0.0 0.0 0.0]) isa DomainError
@test check_vector(M, pts[1], [0.0 2.0 3.0; 0.0 0.0 -1.0; 0.0 0.0 2.0]) isa DomainError

test_manifold(
M,
pts;
parallel_transport=true,
test_injectivity_radius=true,
test_musical_isomorphisms=false,
)

@test all(
iszero,
Weingarten(M, pts[1], Xpts[1], [1.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]),
)
@testset "field parameter" begin
G = HeisenbergMatrices(1; parameter=:field)
@test typeof(get_embedding(G)) === Euclidean{Tuple{Int,Int},ℝ}
@test repr(G) == "HeisenbergMatrices(1; parameter=:field)"
end
end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ end
include_test("manifolds/generalized_stiefel.jl")
include_test("manifolds/grassmann.jl")
include_test("manifolds/hamiltonian.jl")
include_test("manifolds/heisenberg_matrices.jl")
include_test("manifolds/hyperbolic.jl")
include_test("manifolds/hyperrectangle.jl")
include_test("manifolds/invertible_matrices.jl")
Expand Down

2 comments on commit c140918

@mateuszbaran
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register

Release notes:

Added

  • The manifold HeisenbergMatrices as the underlying manifold of HeisenbergGroup.

Changed

  • about.md now also lists contributors of manifolds and a very short history of the package.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while trying to register: Version 0.10.8 already exists

Please sign in to comment.