diff --git a/ext/LieGroupsRecursiveArrayToolsExt.jl b/ext/LieGroupsRecursiveArrayToolsExt.jl index 19455ee7..cef65fb7 100644 --- a/ext/LieGroupsRecursiveArrayToolsExt.jl +++ b/ext/LieGroupsRecursiveArrayToolsExt.jl @@ -2,10 +2,35 @@ module LieGroupsRecursiveArrayToolsExt using LieGroups using RecursiveArrayTools: ArrayPartition -using StaticArrays using LinearAlgebra +using ManifoldsBase # Implement SE(n) also on an Array Partition +# disable affine check +LieGroups._check_matrix_affine(::ArrayPartition, ::Int; v=1) = nothing + +function ManifoldsBase.submanifold_component( + G::LieGroups.LeftSpecialEuclideanGroup, g::ArrayPartition, ::Val{:Rotation} +) + return ManifoldsBase.submanifold_component(G.manifold, g, 1) +end +function ManifoldsBase.submanifold_component( + G::LieGroups.LeftSpecialEuclideanGroup, g::ArrayPartition, ::Val{:Translation} +) + return ManifoldsBase.submanifold_component(G.manifold, g, 2) +end + +Base.@propagate_inbounds function ManifoldsBase.submanifold_component( + G::LieGroups.RightSpecialEuclideanGroup, g::ArrayPartition, ::Val{:Rotation} +) + return ManifoldsBase.submanifold_component(G.manifold, g, 2) +end +Base.@propagate_inbounds function ManifoldsBase.submanifold_component( + G::LieGroups.RightSpecialEuclideanGroup, g::ArrayPartition, ::Val{:Translation} +) + return ManifoldsBase.submanifold_component(G.manifold, g, 1) +end + # TODO: Implement? # The following three should also work due to the # AbstractProductGroup s implementations diff --git a/src/LieGroups.jl b/src/LieGroups.jl index 6e9a4e19..60d06386 100644 --- a/src/LieGroups.jl +++ b/src/LieGroups.jl @@ -1,9 +1,9 @@ @doc raw""" LieGroups.jl: Lie groups and Lie algebras in Julia. -The package is named after the norwegian mathematician [Sophus Lie](https://en.wikipedia.org/wiki/Sophus_Lie). +The package is named after the norwegian mathematician [Marius Sophus Lie](https://en.wikipedia.org/wiki/Sophus_Lie) (1842–1899). -* 📚 Documentation: [manoptjl.org](https://juliamanifolds.github.io/LieGroups.jl/dev/) +* 📚 Documentation: [juliamanifolds.github.io/LieGroups.jl/](https://juliamanifolds.github.io/LieGroups.jl/) * 📦 Repository: [github.com/JuliaManifolds/LieGroups.jl](https://github.com/JuliaManifolds/LieGroups.jl) * 💬 Discussions: [github.com/JuliaManifolds/LieGroups.jl/discussions](https://github.com/JuliaManifolds/LieGroups.jl/discussions) * 🎯 Issues: [github.com/JuliaManifolds/LieGroups.jl/issues](https://github.com/JuliaManifolds/LieGroups.jl/issues) diff --git a/src/groups/special_euclidean_group.jl b/src/groups/special_euclidean_group.jl index 5e1c0114..a6f276b7 100644 --- a/src/groups/special_euclidean_group.jl +++ b/src/groups/special_euclidean_group.jl @@ -1,4 +1,4 @@ -# for (R, t) +# for (g, t) const LeftSpecialEuclideanGroup{T} = LieGroup{ ℝ, <:LeftSemidirectProductGroupOperation{ @@ -11,7 +11,7 @@ const LeftSpecialEuclideanGroup{T} = LieGroup{ }, } -# for (t, R) +# for (t, g) const RightSpecialEuclideanGroup{T} = LieGroup{ ℝ, <:RightSemidirectProductGroupOperation{ @@ -20,7 +20,7 @@ const RightSpecialEuclideanGroup{T} = LieGroup{ LeftGroupOperationAction, }, <:Manifolds.ProductManifold{ - ℝ,Tuple{<:Manifolds.Rotations{T},<:Manifolds.Euclidean{T,ℝ}} + ℝ,Tuple{<:Manifolds.Euclidean{T,ℝ},<:Manifolds.Rotations{T}} }, } @@ -34,15 +34,28 @@ The special Euclidean group ``$(_math(:SE))(n) = $(_math(:SO))(n) ⋉ $(_math(:T To be precise, the group operation is defined on ``$(_math(:SO))(n) ⋉ $(_math(:T))(n)`` as follows: ```math -(g_1, t_1) ⋅ (g_2, t_2) = (g_1$(_math(:∘))g_2, t_1 + g_1$(_math(:⋅))t_2) +(r_1, t_1) ⋅ (r_2, t_2) = (r_1$(_math(:∘))r_2, t_1 + r_1$(_math(:⋅))t_2), ``` +where ``r_1,r_2 ∈ $(_math(:SO))(n)`` and ``t_1,t_2 ∈ $(_math(:T))(n)`` + Analogously you can write this on elements of ``$(_math(:SO))(n) ⋊ $(_math(:T))(n)`` as ```math -(s_1, h_1) ⋅ (s_2, h_2) = (s_1 + h_1$(_math(:⋅))s_2, h_1$(_math(:∘))h_2) +(t_1, r_1) ⋅ (t_2, r_2) = (t_1 + r_1$(_math(:⋅))s_2, r_1$(_math(:∘))r_2) ``` +Both these cases can be represented in a single matrix in [affine form](https://en.wikipedia.org/wiki/Affine_group#Matrix_representation) + +```math +g = $(_tex(:pmatrix, "r & t", "$(_tex(:vec, "0"))_n^{$(_tex(:transp))} & 1")), +$(_tex(:qquad)) r ∈ $(_math(:SO))(n), t ∈ $(_math(:T))(n), +``` +where ``$(_tex(:vec, "0"))_n ∈ ℝ^n`` denotes the vector containing zeros. + +We refer also in general to elements on ``$(_math(:SE))(n)`` as ``g`` +and their rotation and translation components as ``r`` and ``t``, respectively. + # Constructor SpecialEuclideanGroup(n; variant=:left, kwargs...) SpecialOrthogonalGroup(n; kwargs...) ⋉ TranslationGroup(n; kwargs...) @@ -53,31 +66,36 @@ constructor is equivalent to the second. All keyword arguments in `kwargs...` are passed on to [`Rotations`](@extref `Manifolds.Rotations`) as well. -The default representation for ``$(_math(:SE))(n)`` is in [affine form](https://en.wikipedia.org/wiki/Affine_group#Matrix_representation) - -```math -$(_tex(:pmatrix, "g & t", "$(_tex(:vec, "0"))_n^{$(_tex(:transp))} & 1")), -$(_tex(:qquad)) g ∈ $(_math(:SO))(n), t ∈ $(_math(:T))(n), -``` -where ``$(_tex(:vec, "0"))_n ∈ ℝ^n`` denotes the vector containing zeros. - -Alternatively you can use the `ArrayPartition` from [`RecursiveArrayTools.jl`](https://docs.sciml.ai/RecursiveArrayTools/stable/) -to work on ``(g,t)``. - -Alternatively you can use ``$(_math(:T))(n) ⋊ $(_math(:SO))(n)`` for `ArrayPartition`s ``(t,g)``. -or set in the first constructor `variant=:right` +The default representation for ``$(_math(:SE))(n)`` is the affine form. Alternatively +you can use the `ArrayPartition` from [`RecursiveArrayTools.jl`](https://docs.sciml.ai/RecursiveArrayTools/stable/) to work on ``(r,t)``. +or for ``$(_math(:T))(n) ⋊ $(_math(:SO))(n)`` using the `ArrayPartition`s ``(t,r)``; +which corresponds to setting `variant=:right` in the first constructor. """ const SpecialEuclideanGroup{T} = Union{ <:LeftSpecialEuclideanGroup{T},<:RightSpecialEuclideanGroup{T} } -# For matrices, both should be fine, we clarify this in the access of subcomponents +const SpecialEuclideanOperation = Union{ + <:LeftSemidirectProductGroupOperation{ + <:MatrixMultiplicationGroupOperation, + <:AdditionGroupOperation, + LeftGroupOperationAction, + }, + <:RightSemidirectProductGroupOperation{ + <:AdditionGroupOperation, + <:MatrixMultiplicationGroupOperation, + LeftGroupOperationAction, + }, +} + +# This union we can also use for the matrix case where we do not care function SpecialEuclideanGroup(n; variant=:left, kwargs...) SOn = SpecialOrthogonalGroup(n; kwargs...) Tn = TranslationGroup(n; kwargs...) - variant ∉ [:left, :right] && - error("SE(n) requires a variant ∉ [:left, :right] but you provided $variant") + variant ∉ [:left, :right] && error( + "SE(n) requires a variant ∉ [:left, :right] but you provided `variant=:$variant`", + ) return variant === :left ? SOn ⋉ Tn : Tn ⋊ SOn end @@ -100,23 +118,37 @@ function default_right_action(::TranslationGroup, ::SpecialOrthogonalGroup) return LeftGroupOperationAction() end -function ManifoldsBase.check_point(G::SpecialEuclideanGroup, p; kwargs...) - n = get_parameter(G.manifold[1]) - errs = DomainError[] - # - if !isapprox(p[end, :], [zeros(size(p, 2) - 1)..., 1]; kwargs...) - push!( - errs, - DomainError( - p[end, :], "The last row of $p is not homogeneous, i.e. of form [0,..,0,1]." - ), - ) +function _check_matrix_affine(p, n; v=1, kwargs...) + if !isapprox(p[end, :], [zeros(size(p, 2) - 1)..., v]; kwargs...) + return nothing + DomainError(p[end, :], "The last row of $p is not of form [0,..,0,$v].") end + return nothing +end +# Order in a unified way +function ManifoldsBase.check_point(G::LeftSpecialEuclideanGroup, p; kwargs...) + return _check_point(G, G.manifold[1], G.manifold[2], G.op[1], G.op[2], p; kwargs...) +end +function ManifoldsBase.check_point(G::RightSpecialEuclideanGroup, p; kwargs...) + return _check_point(G, G.manifold[2], G.manifold[1], G.op[2], G.op[1], p; kwargs...) +end + +function _check_point( + G::SpecialEuclideanGroup{T}, Rotn, Rn, op1, op2, p; kwargs... +) where {T} + errs = DomainError[] + n = ManifoldsBase.get_parameter(Rotn.size)[1] + errA = _check_matrix_affine(p, n; v=1, kwargs...) + !isnothing(errA) && push!(errs, errA) # SOn - errS = check_point(G.manifold[1], get_submanifold_component(G, p, 1); kwargs...) + errS = ManifoldsBase.check_point( + Rotn, ManifoldsBase.submanifold_component(G, p, :Rotation); kwargs... + ) !isnothing(errS) && push!(errs, errS) # translate part - errT = check_point(G.manifold[2], get_submanifold_component(G, p, 2); kwargs...) + errT = ManifoldsBase.check_point( + Rn, ManifoldsBase.submanifold_component(G, p, :Translation); kwargs... + ) !isnothing(errT) && push!(errs, errT) if length(errs) > 1 return ManifoldsBase.CompositeManifoldError(errs) @@ -124,60 +156,161 @@ function ManifoldsBase.check_point(G::SpecialEuclideanGroup, p; kwargs...) return length(errs) == 0 ? nothing : first(errs) end -function ManifoldsBase.check_vector(G::SpecialEuclideanGroup, X::AbstractMatrix; kwargs...) - n = get_parameter(G.manifolds[1]) +# Order in a unified way +function ManifoldsBase.check_vector(G::LeftSpecialEuclideanGroup, X; kwargs...) + return _check_vector(G, G.manifold[1], G.manifold[2], G.op[1], G.op[2], X; kwargs...) +end +function ManifoldsBase.check_vector(G::RightSpecialEuclideanGroup, X; kwargs...) + return _check_vector(G, G.manifold[2], G.manifold[1], G.op[2], G.op[1], X; kwargs...) +end + +function _check_vector( + G::SpecialEuclideanGroup{T}, Rotn, Rn, op1, op2, X; kwargs... +) where {T} errs = DomainError[] - # homogeneous - if !isapprox(X[end, :], zeros(size(X, 2)); kwargs...) - push!( - errs, - DomainError( - X[end, :], - "The last row of $X is not homogeneous. Expected a zero-vector, got $(X[end, :]).", - ), - ) - end + n = ManifoldsBase.get_parameter(Rotn.size)[1] + errA = _check_matrix_affine(p, n; v=0, kwargs...) + !isnothing(errA) && push!(errs, errA) # SO(n) part - SOn = LieGroup(G.manifold[1], G.op[1]) - errS = check_vector(SOn, get_submanifold_component(G, X, 1); kwargs...) + SOn = LieGroup(Rotn, op1) + errS = ManifoldsBase.check_vector( + SOn, ManifoldsBase.submanifold_component(G, X, :Rotation); kwargs... + ) !isnothing(errS) && push!(errs, errS) # T(n) part - Tn = LieGroup(G.manifold[2], G.op[2]) - errT = check_vector(Tn, get_submanifold_component(G, X, 2); kwargs...) + Tn = LieGroup(Rn, G.op2) + errT = ManifoldsBase.check_vector( + Tn, ManifoldsBase.submanifold_component(G, X, :Translation); kwargs... + ) !isnothing(errT) && push!(errs, errT) - (length(errs) > 1) && (return CompositeManifoldError(errs)) + (length(errs) > 1) && (return ManifoldsBase.CompositeManifoldError(errs)) return length(errs) == 0 ? nothing : first(errs) end -function compose!( - ::SpecialEuclideanGroup, x::AbstractMatrix, p::AbstractMatrix, q::AbstractMatrix +function _compose!( + ::SpecialEuclideanGroup, k::AbstractMatrix, g::AbstractMatrix, h::AbstractMatrix ) - copyto!(x, p * q) - return x + mul!(k, g, h) + return k end +# +# +# We overwrite the `submanifold_component` a bit different, since they are not ordered +# as (1) and (2) but more semantically as :Rotation and :Translation, we also use that here + +@inline function ManifoldsBase.submanifold_component(G::SpecialEuclideanGroup, g, s::Symbol) + return ManifoldsBase.submanifold_component(G, g, Val(s)) +end Base.@propagate_inbounds function ManifoldsBase.submanifold_component( - G::SpecialEuclideanGroup, p, ::Val{1} + G::SpecialEuclideanGroup, p, ::Val{:Rotation} ) - n = get_parameter(G.manifold[1]) + n = ManifoldsBase.get_parameter(G.manifold[1].size)[1] return view(p, 1:n, n + 1) end Base.@propagate_inbounds function ManifoldsBase.submanifold_component( - G::SpecialEuclideanGroup, p, ::Val{2} + G::SpecialEuclideanGroup, p, ::Val{:Translation} ) - n = _get_parameter(G) + n = ManifoldsBase.get_parameter(G.manifold[1].size)[1] return view(p, 1:n, 1:n) end -function ManifoldsBase.submanifold_components(G::SpecialOrthogonalGroup, p::AbstractMatrix) - n = _get_parameter(G) - @assert size(p) == (n + 1, n + 1) - @inbounds t = submanifold_component(G, p, Val(1)) - @inbounds R = submanifold_component(G, p, Val(2)) - return (t, R) +_doc_inv_SEn = """ + inv(G::SpecialEuclideanGroup, g) + inv(G::SpecialEuclideanGroup, h, g) + +Compute the inverse element of a ``g ∈ $(_math(:SE))(n)`` from the [`SpecialEuclideanGroup`](@ref)`(n)`. + +In affine form, ``g = $(_tex(:pmatrix, "r & t", "$(_tex(:vec, "0"))_n^{$(_tex(:transp))} & 1"))``, +where ``$(_tex(:vec, "0"))_n ∈ ℝ^n`` denotes the vector containing zeros. + +The inverse reads + +```math +g^{-1} = $(_tex(:pmatrix, "r^{$(_tex(:transp))} & -r^{$(_tex(:transp))}t", "$(_tex(:vec, "0"))_n^{$(_tex(:transp))} & 1")). +``` + +This computation can be done in-place of `h`. +""" + +@doc "$(_doc_inv_SEn)" +Base.inv(G::SpecialEuclideanGroup, g) + +@doc "$(_doc_inv_SEn)" +function inv!(G::SpecialEuclideanGroup, h, g) + rg = submanifold_component(G, g, :Rotation) + tg = submanifold_component(G, g, :Translation) + rh = submanifold_component(G, h, :Rotation) + th = submanifold_component(G, h, :Translation) + copyto!(rh, transpose(rg)) + copyto!(th, -rh * tg) + return h +end +function inv!(G::SpecialEuclideanGroup, q, ::Identity{<:SpecialEuclideanOperation}) + return identity_element!(G, q) +end + +function ManifoldsBase.isapprox( + G::SpecialEuclideanGroup, g::AbstractMatrix, h::AbstractMatrix; kwargs... +) + return isapprox(g, h; kwargs...) +end + +function Random.rand!( + rng::AbstractRNG, + G::SpecialEuclideanGroup, + pX::AbstractMatrix; + vector_at=nothing, + kwargs..., +) + SOn, Tn = _SOn_and_Tn(G) + if vector_at === nothing # for points -> pass to manifold + rand!( + rng, + SOn, + submanifold_component(G, pX, :Rotation); + vector_at=vector_at, + kwargs..., + ) + rand!( + rng, + Tn, + submanifold_component(G, pX, :Translation); + vector_at=vector_at, + kwargs..., + ) + else # for tangent vectors -> subset the vector_at as well. + rand!( + rng, + SOn, + submanifold_component(G, pX, :Rotation); + vector_at=submanifold_component(G, vector_at, :Rotation), + kwargs..., + ) + rand!( + rng, + Tn, + submanifold_component(G, pX, :Translation); + vector_at=submanifold_component(G, vector_at, :Translation), + kwargs..., + ) + end + return pX +end + +function _SOn_and_Tn(G::LeftSpecialEuclideanGroup) + return map(LieGroup, G.manifold.manifolds, G.op.operations) +end +function _SOn_and_Tn(G::RightSpecialEuclideanGroup) + A = map(LieGroup, G.manifold.manifolds, G.op.operations) + return A[2], A[1] end function Base.show(io::IO, G::SpecialEuclideanGroup) size = Manifolds.get_parameter(G.manifold[2].size)[1] return print(io, "SpecialEuclideanGroup($(size))") end +function Base.show(io::IO, G::RightSpecialEuclideanGroup) + size = Manifolds.get_parameter(G.manifold[2].size)[1] + return print(io, "SpecialEuclideanGroup($(size); variant=:right)") +end diff --git a/src/interface.jl b/src/interface.jl index 69d1292c..422b0075 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -821,8 +821,6 @@ end ManifoldsBase.manifold_dimension(G::LieGroup) = manifold_dimension(G.manifold) -ManifoldsBase.norm(G::LieGroup, g, X) = norm(G.manifold, g, X) - ManifoldsBase.project!(G::LieGroup, h, g) = project!(G.manifold, h, g) # Since tangent vectors are always in the Lie algebra – do project always on TeG function ManifoldsBase.project!(G::LieGroup, Y, g, X) diff --git a/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl index ae299505..54c76bfa 100644 --- a/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl +++ b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl @@ -527,7 +527,6 @@ function test_hat_vee( c = ismissing(expected_value) ? zeros(manifold_dimension(G)) : expected_value Y1 = hat(G, c) @test is_vector(G, g, Y1) - ismissing(expected_value) && @test norm(G, g, Y1) ≈ 0 !ismissing(expected_value) && @test isapprox(𝔤, X, Y1) if test_mutating Y2 = zero_vector(𝔤) diff --git a/test/groups/test_special_euclidean_group.jl b/test/groups/test_special_euclidean_group.jl index 7d331cf3..ec6a62c5 100644 --- a/test/groups/test_special_euclidean_group.jl +++ b/test/groups/test_special_euclidean_group.jl @@ -4,16 +4,16 @@ s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") !(s in LOAD_PATH) && (push!(LOAD_PATH, s)) using LieGroupsTestSuite -begin +@testset "Special Euclidean" begin G = SpecialEuclideanGroup(2) - g1 = ArrayPartition(1 / sqrt(2) * [1.0 1.0; -1.0 1.0], [1.0, 0.0]) - g2 = ArrayPartition([0.0 -1.0; 1.0 0.0], [0.0, 1.0]) - g3 = ArrayPartition([1.0 0.0; 0.0 1.0], [1.0, 1.0]) - X1 = ArrayPartition([0.0 -0.23; 0.23 0.0], [0.0, 1.0]) - X2 = ArrayPartition([0.0 0.30; -0.20 0.0], [1.0, 1.0]) - X3 = ArrayPartition([9.0 0.1; -0.1 0.0], [1.0, 0.0]) - properties = Dict( - :Name => "The special Euclidean group", + g1 = 1 / sqrt(2) .* [1.0 1.0 sqrt(2); -1.0 1.0 0.0; 0.0 0.0 sqrt(2)] + g2 = 1 / sqrt(2) .* [0.0 -1.0 0.0; 1.0 0.0 1.0; 0.0 0.0 1.0] + g3 = [1.0 0.0 1.0; 0.0 1.0 1.0; 0.0 0.0 1.0] + X1 = [0.0 -0.23 0.0; 0.23 0.0 1.0; 0.0 0.0 0.0] + X2 = [0.0 0.30 1.0; -0.20 0.0 1.0; 0.0 0.0 0.0] + X3 = [0.0 0.1 1.0; -0.1 0.0 0.0; 0.0 0.0 0.0] + propertiesL = Dict( + :Name => "The special Euclidean group (affine matrices)", :Points => [g1, g2, g3], :Vectors => [X1, X2, X3], :Rng => Random.MersenneTwister(), @@ -38,28 +38,70 @@ begin vee, ], ) - expectations = Dict( + expectationsL = Dict( :repr => "SpecialEuclideanGroup(2)", #:diff_inv => -X1, #:diff_left_compose => X1, #:diff_right_compose => X1, #:lie_bracket => zero(X1), ) - test_lie_group(G, properties, expectations) + test_lie_group(G, propertiesL, expectationsL) + + GL = SpecialEuclideanGroup(2) + gL1 = ArrayPartition(1 / sqrt(2) * [1.0 1.0; -1.0 1.0], [1.0, 0.0]) + gL2 = ArrayPartition([0.0 -1.0; 1.0 0.0], [0.0, 1.0]) + gL3 = ArrayPartition([1.0 0.0; 0.0 1.0], [1.0, 1.0]) + XL1 = ArrayPartition([0.0 -0.23; 0.23 0.0], [0.0, 1.0]) + XL2 = ArrayPartition([0.0 0.30; -0.20 0.0], [1.0, 1.0]) + XL3 = ArrayPartition([9.0 0.1; -0.1 0.0], [1.0, 0.0]) + propertiesL = Dict( + :Name => "The special Euclidean group (left variant)", + :Points => [gL1, gL2, gL3], + :Vectors => [XL1, XL2, XL3], + :Rng => Random.MersenneTwister(), + :Functions => [ + # adjoint, + compose, + # conjugate, + # diff_inv, + # diff_left_compose, + # diff_right_compose, + # exp, + hat, + identity_element, + inv, + # inv_left_compose, + # inv_right_compose, + # is_identity, + # lie_bracket, + # log, + rand, + show, + vee, + ], + ) + expectationsL = Dict( + :repr => "SpecialEuclideanGroup(2)", + #:diff_inv => -X1, + #:diff_left_compose => X1, + #:diff_right_compose => X1, + #:lie_bracket => zero(X1), + ) + test_lie_group(GL, propertiesL, expectationsL) # # # Right variant - H = TranslationGroup(2) ⋊ SpecialOrthogonalGroup(2) - h1 = ArrayPartition([1.0, 0.0], 1 / sqrt(2) * [1.0 1.0; -1.0 1.0]) - h2 = ArrayPartition([0.0, 1.0], [0.0 -1.0; 1.0 0.0]) - h3 = ArrayPartition([1.0, 1.0], [1.0 0.0; 0.0 1.0]) - Y1 = ArrayPartition([0.0, 1.0], [0.0 0.23; -0.23 0.0]) - Y2 = ArrayPartition([1.0, 1.0], [0.0 0.3; -0.3 1.0]) - Y3 = ArrayPartition([1.0, 0.0], [9.0 -0.1; 0.1 0.0]) - properties2 = Dict( + GR = TranslationGroup(2) ⋊ SpecialOrthogonalGroup(2) + gR1 = ArrayPartition([1.0, 0.0], 1 / sqrt(2) * [1.0 1.0; -1.0 1.0]) + gR2 = ArrayPartition([0.0, 1.0], [0.0 -1.0; 1.0 0.0]) + gR3 = ArrayPartition([1.0, 1.0], [1.0 0.0; 0.0 1.0]) + XR1 = ArrayPartition([0.0, 1.0], [0.0 0.23; -0.23 0.0]) + XR2 = ArrayPartition([1.0, 1.0], [0.0 0.3; -0.3 1.0]) + XR3 = ArrayPartition([1.0, 0.0], [9.0 -0.1; 0.1 0.0]) + propertiesR = Dict( :Name => "The special Euclidean group (right variant)", - :Points => [h1, h2, h3], - :Vectors => [Y1, Y2, Y3], + :Points => [gR1, gR2, gR3], + :Vectors => [XR1, XR2, XR3], :Rng => Random.MersenneTwister(), :Functions => [ # adjoint, @@ -71,7 +113,7 @@ begin # exp, hat, identity_element, - # inv, + inv, # inv_left_compose, # inv_right_compose, # is_identity, @@ -82,12 +124,12 @@ begin vee, ], ) - expectations2 = Dict( - :repr => "RightSemidirectProductLieGroup(TranslationGroup(2; field=ℝ), SpecialOrthogonalGroup(2), LeftGroupOperationAction())", + expectationsR = Dict( + :repr => "SpecialEuclideanGroup(2; variant=:right)", #:diff_inv => -X1, #:diff_left_compose => X1, #:diff_right_compose => X1, #:lie_bracket => zero(X1), ) - test_lie_group(H, properties2, expectations2) + test_lie_group(GR, propertiesR, expectationsR) end