Skip to content

Commit

Permalink
add Heisenberg group (#19)
Browse files Browse the repository at this point in the history
* add Heisenberg group

* Update test/groups/test_heisenberg_group.jl

Co-authored-by: Ronny Bergmann <[email protected]>

* those will be in Manifolds.jl

* require new Manifolds.jl

* fix CI

* ?

* tests

* improve coverage

* fix things

* improve coverage

* fix convention

---------

Co-authored-by: Ronny Bergmann <[email protected]>
  • Loading branch information
mateuszbaran and kellertuer authored Jan 2, 2025
1 parent 932a647 commit 5246f30
Show file tree
Hide file tree
Showing 17 changed files with 367 additions and 21 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
julia-version: ["1.6", "1.10", "1.11"]
julia-version: ["lts", "1", "pre"]
os: [ubuntu-latest, macOS-latest]
steps:
- uses: actions/checkout@v4
Expand All @@ -23,7 +23,7 @@ jobs:
- uses: julia-actions/julia-buildpkg@latest
- uses: julia-actions/julia-runtest@latest
- uses: julia-actions/julia-processcoverage@v1
- uses: codecov/codecov-action@v4
- uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./lcov.info
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Everything denoted by “formerly” refers to the previous name in [`Manifolds.
* `LieGroup` (formerly `GroupManifold`) as well as the concrete groups
* `TranslationGroup`
* `GeneralLinearGroup` (formerly `GeneralLinear`)
* `HeisenbergGroup`
* `LeftSemidirectProductLieGroup` (formerly `SemidirectProductGroup`)
* `` (alias for `LeftSemidirectProductGroupOperation` when a `default_left_action(G,H)` is defined for the two groups)
* `PowerLieGroup` (formerly `PowerGroup`)
Expand Down
10 changes: 5 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[compat]
Aqua = "0.8"
LinearAlgebra = "1.6"
Manifolds = "0.10.5"
LinearAlgebra = "1.10"
Manifolds = "0.10.11"
ManifoldsBase = "0.15.20"
Random = "1.10"
RecursiveArrayTools = "2, 3"
Random = "1.6"
Test = "1.6"
julia = "1.6"
Test = "1.10"
julia = "1.10"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ makedocs(;
"Lie groups" => [
"List of Lie groups" => "groups/index.md",
"General Linear" => "groups/general_linear.md",
"Heisenberg" => "groups/heisenberg_group.md",
"Power group" => "groups/power_group.md",
"Product group" => "groups/product_group.md",
"Semidirect product group" => "groups/semidirect_product_group.md",
Expand Down
7 changes: 7 additions & 0 deletions docs/src/groups/heisenberg_group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The Heisenberg group

```@autodocs
Modules = [LieGroups]
Pages = ["groups/heisenberg_group.jl"]
Order = [:type, :function]
```
12 changes: 12 additions & 0 deletions docs/src/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
#
# A
#
#
# B
@book{BinzPods:2008,
AUTHOR = {Biny, E and Pods, S},
PUBLISHER = {American Mathematical Society},
TITLE = {The Geometry of Heisenberg Groups: With Applications in Signal Theory, Optics, Quantization, and Field Quantization},
YEAR = {2008}
}

#
#
# G
Expand Down
19 changes: 18 additions & 1 deletion src/LieGroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ module LieGroups

using LinearAlgebra, ManifoldsBase, Manifolds, Random

using ManifoldsBase: RealNumbers

#
#
# = Compatibility (and a bit of type piracy for now)
Expand Down Expand Up @@ -39,6 +41,20 @@ include("groups/semidirect_product_group.jl")
# Lie groups
include("groups/translation_group.jl")
include("groups/general_linear_group.jl")
include("groups/heisenberg_group.jl")

# explicit method error to avoid stack overflow
for GT in [LieGroup, HeisenbergGroup]
@eval begin
function ManifoldsBase.log!(G::$GT, X, e::Identity, g)
throw(
MethodError(
ManifoldsBase.log!, (typeof(G), typeof(X), typeof(e), typeof(g))
),
)
end
end
end

export LieGroup, LieAlgebra
export PowerLieGroup, ProductLieGroup
Expand Down Expand Up @@ -66,7 +82,7 @@ export GroupAction, GroupOperationAction
#
#
# Specific groups
export TranslationGroup, GeneralLinearGroup
export TranslationGroup, GeneralLinearGroup, HeisenbergGroup

export adjoint, adjoint!, apply, apply!
export base_lie_group, base_manifold
Expand All @@ -92,6 +108,7 @@ export identity_element, identity_element!, is_identity, inv, inv!, diff_inv, di
export lie_bracket, lie_bracket!, log, log!
export manifold_dimension
export norm
export injectivity_radius
export rand, rand!
export switch
export vee, vee!
Expand Down
9 changes: 9 additions & 0 deletions src/group_operations/multiplication_operation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,15 @@ function ManifoldsBase.log!(
copyto!(X, log(g))
return X
end
function ManifoldsBase.log!(
::LieGroup{𝔽,MatrixMultiplicationGroupOperation},
X,
::Identity{MatrixMultiplicationGroupOperation},
::Identity{MatrixMultiplicationGroupOperation},
) where {𝔽}
fill!(X, 0)
return X
end

LinearAlgebra.mul!(q, ::Identity{<:AbstractMultiplicationGroupOperation}, p) = copyto!(q, p)
function LinearAlgebra.mul!(
Expand Down
200 changes: 200 additions & 0 deletions src/groups/heisenberg_group.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@

@doc raw"""
HeisenbergGroup{T} <: AbstractDecoratorManifold{ℝ}
Heisenberg group `HeisenbergGroup(n)` is the group of ``(n+2)×(n+2)`` matrices [BinzPods:2008](@cite)
```math
\begin{bmatrix} 1 & \mathbf{a} & c \\
\mathbf{0} & I_n & \mathbf{b} \\
0 & \mathbf{0} & 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`` and ``c`` is a real number.
The group operation is matrix multiplication.
The left-invariant metric on the manifold is used.
"""
const HeisenbergGroup{T} = LieGroup{
ℝ,MatrixMultiplicationGroupOperation,Manifolds.HeisenbergMatrices{T}
}

function HeisenbergGroup(n::Int; parameter::Symbol=:type)
Hm = Manifolds.HeisenbergMatrices(n; parameter=parameter)
return HeisenbergGroup{typeof(Hm).parameters...}(
Hm, MatrixMultiplicationGroupOperation()
)
end

function _heisenberg_a_view(G::HeisenbergGroup, g)
n = ManifoldsBase.get_parameter(G.manifold.size)[1]
return view(g, 1, 2:(n + 1))
end
function _heisenberg_b_view(G::HeisenbergGroup, g)
n = ManifoldsBase.get_parameter(G.manifold.size)[1]
return view(g, 2:(n + 1), n + 2)
end

@doc raw"""
exp(G::HeisenbergGroup, ::Identity{MatrixMultiplicationGroupOperation}, X)
Lie group exponential for the [`HeisenbergGroup`](@ref) `G` of the vector `X`.
The formula reads
```math
\exp\left(\begin{bmatrix} 0 & \mathbf{a} & c \\
\mathbf{0} & 0_n & \mathbf{b} \\
0 & \mathbf{0} & 0 \end{bmatrix}\right) = \begin{bmatrix} 1 & \mathbf{a} & c + \mathbf{a}⋅\mathbf{b}/2 \\
\mathbf{0} & I_n & \mathbf{b} \\
0 & \mathbf{0} & 1 \end{bmatrix}
```
where ``I_n`` is the ``n×n`` identity matrix, ``0_n`` is the ``n×n`` zero matrix
and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors.
"""
function Base.exp(G::HeisenbergGroup, e::Identity{MatrixMultiplicationGroupOperation}, X)
h = similar(X)
exp!(G, h, e, X)
return h
end

function ManifoldsBase.exp!(
G::HeisenbergGroup, h, ::Identity{MatrixMultiplicationGroupOperation}, X
)
n = ManifoldsBase.get_parameter(G.manifold.size)[1]
copyto!(h, I)
a_view = _heisenberg_a_view(G, X)
b_view = _heisenberg_b_view(G, X)
h[1, 2:(n + 1)] .= a_view
h[2:(n + 1), n + 2] .= b_view
h[1, n + 2] = X[1, n + 2] + dot(a_view, b_view) / 2
return h
end

@doc raw"""
exp(G::HeisenbergGroup, g, X)
Exponential map on the [`HeisenbergGroup`](@ref) `G` with the left-invariant metric.
The expression reads
```math
\exp_{\begin{bmatrix} 1 & \mathbf{a}_p & c_p \\
\mathbf{0} & I_n & \mathbf{b}_p \\
0 & \mathbf{0} & 1 \end{bmatrix}}\left(\begin{bmatrix} 0 & \mathbf{a}_X & c_X \\
\mathbf{0} & 0_n & \mathbf{b}_X \\
0 & \mathbf{0} & 0 \end{bmatrix}\right) =
\begin{bmatrix} 1 & \mathbf{a}_p + \mathbf{a}_X & c_p + c_X + \mathbf{a}_X⋅\mathbf{b}_X/2 + \mathbf{a}_p⋅\mathbf{b}_X \\
\mathbf{0} & I_n & \mathbf{b}_p + \mathbf{b}_X \\
0 & \mathbf{0} & 1 \end{bmatrix}
```
where ``I_n`` is the ``n×n`` identity matrix, ``0_n`` is the ``n×n`` zero matrix
and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors.
"""
function Base.exp(G::HeisenbergGroup, g, X)
h = similar(X)
exp!(G, h, g, X)
return h
end

function ManifoldsBase.exp!(G::HeisenbergGroup, h, g, X)
n = ManifoldsBase.get_parameter(G.manifold.size)[1]
copyto!(h, I)
a_p_view = _heisenberg_a_view(G, g)
b_p_view = _heisenberg_b_view(G, g)
a_X_view = _heisenberg_a_view(G, X)
b_X_view = _heisenberg_b_view(G, X)
h[1, 2:(n + 1)] .= a_p_view .+ a_X_view
h[2:(n + 1), n + 2] .= b_p_view .+ b_X_view
h[1, n + 2] =
g[1, n + 2] + X[1, n + 2] + dot(a_X_view, b_X_view) / 2 + dot(a_p_view, b_X_view)
return h
end

@doc raw"""
injectivity_radius(G::HeisenbergGroup)
Return the injectivity radius on the [`HeisenbergGroup`](@ref) `G`, which is ``∞``.
"""
ManifoldsBase.injectivity_radius(::HeisenbergGroup) = Inf

@doc raw"""
log(G::HeisenbergGroup, g, h)
Compute the logarithmic map on the [`HeisenbergGroup`](@ref) group.
The formula reads
```math
\log_{\begin{bmatrix} 1 & \mathbf{a}_p & c_p \\
\mathbf{0} & I_n & \mathbf{b}_p \\
0 & \mathbf{0} & 1 \end{bmatrix}}\left(\begin{bmatrix} 1 & \mathbf{a}_q & c_q \\
\mathbf{0} & I_n & \mathbf{b}_q \\
0 & \mathbf{0} & 1 \end{bmatrix}\right) =
\begin{bmatrix} 0 & \mathbf{a}_q - \mathbf{a}_p & c_q - c_p + \mathbf{a}_p⋅\mathbf{b}_p - \mathbf{a}_q⋅\mathbf{b}_q - (\mathbf{a}_q - \mathbf{a}_p)⋅(\mathbf{b}_q - \mathbf{b}_p) / 2 \\
\mathbf{0} & 0_n & \mathbf{b}_q - \mathbf{b}_p \\
0 & \mathbf{0} & 0 \end{bmatrix}
```
where ``I_n`` is the ``n×n`` identity matrix, ``0_n`` is the ``n×n`` zero matrix
and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors.
"""
Base.log(::HeisenbergGroup, g, h)

function ManifoldsBase.log!(G::HeisenbergGroup, X, g, h)
n = ManifoldsBase.get_parameter(G.manifold.size)[1]
fill!(X, 0)
a_p_view = _heisenberg_a_view(G, g)
b_p_view = _heisenberg_b_view(G, g)
a_q_view = _heisenberg_a_view(G, h)
b_q_view = _heisenberg_b_view(G, h)
X[1, 2:(n + 1)] .= a_q_view .- a_p_view
X[2:(n + 1), n + 2] .= b_q_view .- b_p_view
pinvq_c = dot(a_p_view, b_p_view) - g[1, n + 2] + h[1, n + 2] - dot(a_p_view, b_q_view)
X[1, n + 2] = pinvq_c - dot(a_q_view - a_p_view, b_q_view - b_p_view) / 2
return X
end

@doc raw"""
log(G::HeisenbergGroup, ::Identity{MatrixMultiplicationGroupOperation}, g)
Lie group logarithm for the [`HeisenbergGroup`](@ref) `G` of the point `g`.
The formula reads
```math
\log\left(\begin{bmatrix} 1 & \mathbf{a} & c \\
\mathbf{0} & I_n & \mathbf{b} \\
0 & \mathbf{0} & 1 \end{bmatrix}\right) =
\begin{bmatrix} 0 & \mathbf{a} & c - \mathbf{a}⋅\mathbf{b}/2 \\
\mathbf{0} & 0_n & \mathbf{b} \\
0 & \mathbf{0} & 0 \end{bmatrix}
```
where ``I_n`` is the ``n×n`` identity matrix, ``0_n`` is the ``n×n`` zero matrix
and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors.
"""
log(G::HeisenbergGroup, ::Identity{MatrixMultiplicationGroupOperation}, g)

function ManifoldsBase.log!(
G::HeisenbergGroup, X, ::Identity{MatrixMultiplicationGroupOperation}, g
)
n = ManifoldsBase.get_parameter(G.manifold.size)[1]
fill!(X, 0)
view_a_X = _heisenberg_a_view(G, X)
view_b_X = _heisenberg_b_view(G, X)
view_a_X .= _heisenberg_a_view(G, g)
view_b_X .= _heisenberg_b_view(G, g)
X[1, n + 2] = g[1, n + 2] - dot(view_a_X, view_b_X) / 2
return X
end
function ManifoldsBase.log!(
::HeisenbergGroup,
X,
::Identity{MatrixMultiplicationGroupOperation},
::Identity{MatrixMultiplicationGroupOperation},
)
fill!(X, 0)
return X
end

function Base.show(
io::IO, ::HeisenbergGroup{ManifoldsBase.TypeParameter{Tuple{n}}}
) where {n}
return print(io, "HeisenbergGroup($(n))")
end
function Base.show(io::IO, G::HeisenbergGroup{Tuple{Int}})
n = ManifoldsBase.get_parameter(G.manifold.size)[1]
return print(io, "HeisenbergGroup($(n); parameter=:field)")
end
6 changes: 6 additions & 0 deletions src/groups/power_group.jl
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@ function ManifoldsBase.log!(
end
return X
end
function ManifoldsBase.log!(
PoG::LieGroup{𝔽,Op,M}, X, ::Identity{Op}, ::Identity{Op}
) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold}
PM = PoG.manifold
return zero_vector!(PM, X, identity_element(PoG))
end

function Base.show(
io::IO, G::LieGroup{𝔽,Op,M}
Expand Down
7 changes: 7 additions & 0 deletions src/groups/product_group.jl
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,13 @@ function ManifoldsBase.log!(
)
return X
end
function ManifoldsBase.log!(
PrG::LieGroup{𝔽,Op,M}, X, ::Identity{Op}, ::Identity{Op}
) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold}
PrM = PrG.manifold
zero_vector!(PrM, X, identity_element(PrG))
return X
end

function Base.show(
io::IO, G::LieGroup{𝔽,Op,M}
Expand Down
10 changes: 3 additions & 7 deletions src/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,9 @@ See also [HilgertNeeb:2012; Definition 9.2.2](@cite).
"""

@doc "$(_doc_exp_id)"
function ManifoldsBase.exp(G::LieGroup, e::Identity, X, t::Number=1)
function ManifoldsBase.exp(
G::LieGroup{𝔽,O}, e::Identity{O}, X, t::Number=1
) where {𝔽,O<:AbstractGroupOperation}
h = identity_element(G)
exp!(G, h, e, X, t)
return h
Expand Down Expand Up @@ -765,12 +767,6 @@ function ManifoldsBase.log(G::LieGroup, e::Identity, g)
return X
end

# explicit method error to avoid stack overflow
@doc "$(_doc_log_id)"
function ManifoldsBase.log!(G::LieGroup, X, e::Identity, g)
throw(MethodError(ManifoldsBase.log!, (typeof(G), typeof(X), typeof(e), typeof(g))))
end

ManifoldsBase.manifold_dimension(G::LieGroup) = manifold_dimension(G.manifold)

ManifoldsBase.norm(G::LieGroup, g, X) = norm(G.manifold, g, X)
Expand Down
Loading

0 comments on commit 5246f30

Please sign in to comment.