diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 19bbd185..5a507da5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 @@ -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 diff --git a/NEWS.md b/NEWS.md index dfbb49ff..7752e71b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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`) diff --git a/Project.toml b/Project.toml index 1c05cfed..e7713e4e 100644 --- a/Project.toml +++ b/Project.toml @@ -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" diff --git a/docs/make.jl b/docs/make.jl index 36d2a9f8..9f24bf60 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -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", diff --git a/docs/src/groups/heisenberg_group.md b/docs/src/groups/heisenberg_group.md new file mode 100644 index 00000000..ccb81fde --- /dev/null +++ b/docs/src/groups/heisenberg_group.md @@ -0,0 +1,7 @@ +# The Heisenberg group + +```@autodocs +Modules = [LieGroups] +Pages = ["groups/heisenberg_group.jl"] +Order = [:type, :function] +``` diff --git a/docs/src/references.bib b/docs/src/references.bib index 690ff696..6cc47adb 100644 --- a/docs/src/references.bib +++ b/docs/src/references.bib @@ -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 diff --git a/src/LieGroups.jl b/src/LieGroups.jl index 11e15c14..1f2a2072 100644 --- a/src/LieGroups.jl +++ b/src/LieGroups.jl @@ -12,6 +12,8 @@ module LieGroups using LinearAlgebra, ManifoldsBase, Manifolds, Random +using ManifoldsBase: RealNumbers + # # # = Compatibility (and a bit of type piracy for now) @@ -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 @@ -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 @@ -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! diff --git a/src/group_operations/multiplication_operation.jl b/src/group_operations/multiplication_operation.jl index 285a0af6..8b37dad7 100644 --- a/src/group_operations/multiplication_operation.jl +++ b/src/group_operations/multiplication_operation.jl @@ -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!( diff --git a/src/groups/heisenberg_group.jl b/src/groups/heisenberg_group.jl new file mode 100644 index 00000000..52ff4cd5 --- /dev/null +++ b/src/groups/heisenberg_group.jl @@ -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 diff --git a/src/groups/power_group.jl b/src/groups/power_group.jl index ccdcbb42..ce042986 100644 --- a/src/groups/power_group.jl +++ b/src/groups/power_group.jl @@ -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} diff --git a/src/groups/product_group.jl b/src/groups/product_group.jl index a27529a8..5599807f 100644 --- a/src/groups/product_group.jl +++ b/src/groups/product_group.jl @@ -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} diff --git a/src/interface.jl b/src/interface.jl index 31036f64..1027be76 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -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 @@ -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) diff --git a/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl index 8b265553..5be9a903 100644 --- a/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl +++ b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl @@ -398,7 +398,7 @@ function test_exp_log( exp!(G, k2, e, X) @test isapprox(G, k1, k2) end - @test is_point(G, k1) + @test is_point(G, k1; error=:error) # exp k1 = exp(G, g, X) if test_mutating @@ -406,7 +406,7 @@ function test_exp_log( exp!(G, k2, g, X) @test isapprox(G, k1, k2) end - @test is_point(G, k1) + @test is_point(G, k1; error=:error) end if test_log # Lie group log @@ -415,8 +415,11 @@ function test_exp_log( Y2 = zero_vector(G, e) log!(G, Y2, e, g) @test isapprox(𝔀, Y1, Y2) + + log!(G, Y2, e, e) + @test isapprox(𝔀, Y2, 0 * Y2) end - @test is_point(𝔀, Y1) + @test is_point(𝔀, Y1; error=:error) @test norm(𝔀, log(G, g, g)) β‰ˆ 0 @test norm(𝔀, log(G, h, h)) β‰ˆ 0 # log @@ -426,7 +429,7 @@ function test_exp_log( log!(G, Y2, g, h) @test isapprox(𝔀, Y1, Y2) end - @test is_point(𝔀, Y1) + @test is_point(𝔀, Y1; error=:error) # or equivalently @test is_vector(G, Y1) @test is_vector(G, Identity(G), Y1) @@ -528,6 +531,28 @@ end # # # --- `I` + +""" + test_injectivity_radius(G::LieGroup; kwargs...) + +Test the function `injectivity_radius`. + +# Keyword arguments + +* `expected=missing`: expected value for global injectivity radius. +""" +function test_injectivity_radius(G::LieGroup; expected=missing) + @testset "injectivity radius" begin + if ismissing(expected) + @test injectivity_radius(G) isa Real + @test injectivity_radius(G) >= 0 + else + @test injectivity_radius(G) == expected + end + end + return nothing +end + """ test_inv_compose(G::LieGroup, g, h, X; kwargs...) @@ -888,6 +913,10 @@ function test_lie_group(G::LieGroup, properties::Dict, expectations::Dict=Dict() test_right=(inv_right_compose in functions), ) end + if (injectivity_radius in functions) + ir = get(expectations, :injectivity_radius, missing) + test_injectivity_radius(G; expected=ir) + end if (inv in functions) test_inv(G, points[1]; test_mutating=mutating) end diff --git a/test/groups/test_general_linear_group.jl b/test/groups/test_general_linear_group.jl index 490440f7..ea7a3e3d 100644 --- a/test/groups/test_general_linear_group.jl +++ b/test/groups/test_general_linear_group.jl @@ -30,7 +30,7 @@ using LieGroupsTestSuite is_identity, lie_bracket, log, - #rand, # requires a fix in Manifolds.jl to have rand on invertible matrices + rand, show, #vee, # requires a fix in Manifolds.jl to have an ONB on invertible matrices ], diff --git a/test/groups/test_heisenberg_group.jl b/test/groups/test_heisenberg_group.jl new file mode 100644 index 00000000..9e9b37fd --- /dev/null +++ b/test/groups/test_heisenberg_group.jl @@ -0,0 +1,60 @@ +using LieGroups, Random, Test + +using ManifoldsBase: β„‚ + +s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +@testset "Heisenberg group" begin + G = HeisenbergGroup(1) + g1, g2, g3 = [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] + + X1, X2, X3 = [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] + + properties = Dict( + :Name => "Heisenberg group", + :Points => [g1, g2, g3], + :Vectors => [X1, X2, X3], + :Rng => Random.MersenneTwister(), + :Functions => [ + adjoint, + compose, + conjugate, + diff_conjugate, + diff_inv, + diff_left_compose, + diff_right_compose, + exp, + hat, + injectivity_radius, + inv, + inv_left_compose, + inv_right_compose, + is_identity, + lie_bracket, + log, + rand, + show, + vee, + ], + ) + expectations = Dict( + :repr => "HeisenbergGroup(1)", + :lie_bracket => X1 * X2 - X2 * X1, + :injectivity_radius => Inf, + ) + test_lie_group(G, properties, expectations) + + @test is_point(G, Identity(G); error=:error) + @test_throws DomainError is_point(G, Identity(AdditionGroupOperation()); error=:error) + + @testset "field parameter" begin + G = HeisenbergGroup(1; parameter=:field) + @test repr(G) == "HeisenbergGroup(1; parameter=:field)" + end +end diff --git a/test/operations/test_addidion_operation.jl b/test/operations/test_addition_operation.jl similarity index 100% rename from test/operations/test_addidion_operation.jl rename to test/operations/test_addition_operation.jl diff --git a/test/runtests.jl b/test/runtests.jl index 8615bc45..23089486 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -18,7 +18,7 @@ end include_test("test_interface.jl") end @testset "Generic Group Operations" begin - include_test("operations/test_addidion_operation.jl") + include_test("operations/test_addition_operation.jl") include_test("operations/test_multiplication_operation.jl") end @testset "Generic Group Actions" begin @@ -32,6 +32,7 @@ end include_test("groups/test_semidirect_product_group.jl") end include_test("groups/test_general_linear_group.jl") + include_test("groups/test_heisenberg_group.jl") include_test("groups/test_translation_group.jl") end include("test_aqua.jl")