diff --git a/NEWS.md b/NEWS.md index 9a91f7fe..dfbb49ff 100644 --- a/NEWS.md +++ b/NEWS.md @@ -15,16 +15,28 @@ Everything denoted by “formerly” refers to the previous name in [`Manifolds. * `LieGroup` (formerly `GroupManifold`) as well as the concrete groups * `TranslationGroup` * `GeneralLinearGroup` (formerly `GeneralLinear`) + * `LeftSemidirectProductLieGroup` (formerly `SemidirectProductGroup`) + * `⋉` (alias for `LeftSemidirectProductGroupOperation` when a `default_left_action(G,H)` is defined for the two groups) + * `PowerLieGroup` (formerly `PowerGroup`) + * `PowerGroupOperation` to internally avoid ambiguities. Since the constructor always expects a Lie group, this is only necessary internally + * `ProductLieGroup` (formerly `ProductGroup`) + * `RightSemidirectProductLieGroup` + * `⋊` (alias for `RightSemidirectProductGroupOperation` when a `default_right_action(G,H)` is defined for the two groups) * `AbstractGroupOperation` as well as its concrete subtypes * `AdditionGroupOperation` (formerly `AdditionOperation`) + * `MultiplicationGroupOperation` (formerly `MultiplicationOperation`) + * `PowerGroupOperation` (formerly the Lie group was stored inside a power manifold) + * `ProductGroupOperation` (formerly the Lie groups were stored inside a product manifold) + * `LeftSemidirectProductGroupOperation` (this was formerly only implicitly stored in the `SemidirectProductGroup`) + * `RightSemidirectProductGroupOperation` * `AbstractGroupActionType` with its 2 specific (new) abstract subtypes * `AbstractLeftGroupActionType` * `AbstractRightGroupActionType` * For the group operation actions there are now - * `LeftGroupOperation` (formerly `LeftForwardAction`) - * `RightGroupOperation` (formerly `RightBackwardAction`) - * `InverseLeftGroupOperation` (formerly `RightForwardAction`) - * `InverseRightGroupOperation` (formerly `LeftBackwardAction`) + * `LeftGroupOperationAction` (formerly `LeftForwardAction`) + * `RightGroupOperationAction` (formerly `RightBackwardAction`) + * `InverseLeftGroupOperationAction` (formerly `RightForwardAction`) + * `InverseRightGroupOperationAction` (formerly `LeftBackwardAction`) * `LieAlgebraOrthogonalBasis` (replaces `VeeOrthogonalBasis`, which is still available in `ManifoldsBase.jl`) * `Identity` * `apply`and `apply!` diff --git a/Project.toml b/Project.toml index 95840f20..1c05cfed 100644 --- a/Project.toml +++ b/Project.toml @@ -14,6 +14,7 @@ Aqua = "0.8" LinearAlgebra = "1.6" Manifolds = "0.10.5" ManifoldsBase = "0.15.20" +RecursiveArrayTools = "2, 3" Random = "1.6" Test = "1.6" julia = "1.6" @@ -21,7 +22,8 @@ julia = "1.6" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Manifolds = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e" +RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "Aqua", "Manifolds"] +test = ["Test", "Aqua", "Manifolds", "RecursiveArrayTools"] diff --git a/docs/make.jl b/docs/make.jl index fdaf427d..36d2a9f8 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -125,6 +125,9 @@ makedocs(; "Lie groups" => [ "List of Lie groups" => "groups/index.md", "General Linear" => "groups/general_linear.md", + "Power group" => "groups/power_group.md", + "Product group" => "groups/product_group.md", + "Semidirect product group" => "groups/semidirect_product_group.md", "Translation group" => "groups/translation.md", ], "Interfaces" => [ diff --git a/docs/src/groups/power_group.md b/docs/src/groups/power_group.md new file mode 100644 index 00000000..ac8efefe --- /dev/null +++ b/docs/src/groups/power_group.md @@ -0,0 +1,7 @@ +# The power Lie group + +```@autodocs +Modules = [LieGroups] +Pages = ["groups/power_group.jl"] +Order = [:type, :function] +``` \ No newline at end of file diff --git a/docs/src/groups/product_group.md b/docs/src/groups/product_group.md new file mode 100644 index 00000000..3587ea8c --- /dev/null +++ b/docs/src/groups/product_group.md @@ -0,0 +1,7 @@ +# The product Lie group + +```@autodocs +Modules = [LieGroups] +Pages = ["groups/product_group.jl"] +Order = [:type, :function] +``` \ No newline at end of file diff --git a/docs/src/groups/semidirect_product_group.md b/docs/src/groups/semidirect_product_group.md new file mode 100644 index 00000000..06cf0861 --- /dev/null +++ b/docs/src/groups/semidirect_product_group.md @@ -0,0 +1,7 @@ +# The semidirect product Lie group + +```@autodocs +Modules = [LieGroups] +Pages = ["groups/semidirect_product_group.jl"] +Order = [:type, :function] +``` \ No newline at end of file diff --git a/docs/src/tutorials/transition.md b/docs/src/tutorials/transition.md index 71728583..9269b8d7 100644 --- a/docs/src/tutorials/transition.md +++ b/docs/src/tutorials/transition.md @@ -22,12 +22,15 @@ The list is alphabetical, but first lists types, then functions | `Manifolds.jl` | `LieGroups.jl` | Comment | |:---------- |:---------- |:-------------- | | `AdditionOperation` | [`AdditionGroupOperation`](@ref) | | -| `LeftForwardAction` | [`LeftGroupOperation`](@ref) -| `RightBackwardAction` | [`RightGroupOperation`](@ref) | | -| `LeftBackwardAction` | [`InverseRightGroupOperation`](@ref) | note that this is now also aa [`AbstractLeftGroupActionType`](@ref) | +| `LeftForwardAction` | [`LeftGroupOperationAction`](@ref) +| `RightBackwardAction` | [`RightGroupOperationAction`](@ref) | | +| `LeftBackwardAction` | [`InverseRightGroupOperationAction`](@ref) | note that this is now also aa [`AbstractLeftGroupActionType`](@ref) | | | [`LieAlgebra`](@ref)`(G)` | new alias to emphasize its manifold- and vector structure as well as for a few dispatch methods. | | `GroupManifold(M, op)` | [`LieGroup`](@ref)`(M, op)` | | -| `RightForwardAction` | [`InverseLeftGroupOperation`](@ref) | note that this is an [`AbstractRightGroupActionType`](@ref) | +| `PowerGroup(M)` | [`PowerLieGroup`](@ref)`(G,n)` | The main change is, that the constructor now requires a Lie group to build the power Lie group; This also allows for `G^n`. The other two former constructors for nested and nested-replacing are no longer necessary. `PowerLieGroup` behaves exactly the same as [`PowerManifold`](@extref `ManifoldsBase.PowerManifold`). | +| `ProductGroup(M)` | [`ProductLieGroup`](@ref)`(G, H)` | The main change is, that the constructor now requires two Lie groups to build their product. This also allows for the short hand `G×H` to generate this product. | +| `SemidirectProductGroup(G, H, a)` | [`LeftSemidirectProductLieGroup`](@ref)`(G, H, a)` | While this staid the same, there is now also the [`default_left_action`](@ref)`(G,H)`. When this agrees with `a` you can use the short hand `G⋉H` to generate this semidirect product. Analogously there now also exists the [`RightSemidirectProductLieGroup`](@ref)`(G,H)` with[`default_left_action`](@ref)`(G,H)` that allows for the short cut `G⋊H` | +| `RightForwardAction` | [`InverseLeftGroupOperationAction`](@ref) | note that this is an [`AbstractRightGroupActionType`](@ref) | | `adjoint` | [`adjoint`](@ref) | now implemented with a default, when you provide [`diff_conjugate!`](@ref). | `apply_diff` | [`diff_apply`](@ref) | modifiers (diff) come first, consistent with [`ManifoldsDiff.jl`](https://juliamanifolds.github.io/ManifoldDiff.jl/stable/) | | `apply_diff_group` | [`diff_group_apply`](@ref) | modifiers (diff/group) come first, consistent with [`ManifoldsDiff.jl`](https://juliamanifolds.github.io/ManifoldDiff.jl/stable/) | @@ -41,12 +44,17 @@ The list is alphabetical, but first lists types, then functions | `log(G, g, h)` | `log(`[`base_manifold`](@ref base_manifold(G::LieGroup))`(G), g, h)` | you can now access the previous defaults on the internal manifold whenever they do not agree with the invariant one | | `log_inv(G, g, h)` | [`log`](@ref log(G::LieGroup, g, h))`(G, g, h)` | the logarithmic map invariant to the group operation is the default on Lie groups here | | `log_lie(G, g)` | [`log`](@ref log(G::LieGroup, e::Identity, g))`(G, `[`Identity`](@ref)`(G), g)` | the (matrix) logarithm is now the one at the identity, since there it agrees with the invariant one | -| `switch_direction(A)` | [`inv`](@ref inv(::AbstractGroupAction))`(A)` | switches from an action to its inverse action (formerly the direction forward/backward, sometimes even left/right, do not confuse with the side left/right). | -| `switch_side(A)` | [`switch`](@ref switch(::AbstractGroupAction))`(A)` | switches from a left action to its corresponding right action. | +| `switch_direction(A)` | [`inv`](@ref inv(::GroupAction))`(A)` | switches from an action to its inverse action (formerly the direction forward/backward, sometimes even left/right, do not confuse with the side left/right). | +| `switch_side(A)` | [`switch`](@ref switch(::GroupAction))`(A)` | switches from a left action to its corresponding right action. | | `translate(G, g, h)` | [`compose`](@ref)`(G, g, h)` | unified to `compose` | | `translate_diff(G, g, X, c)` | [`diff_left_compose`](@ref)`(G, g, h, X)`, [`diff_right_compose`](@ref)`(G, g, h, X)` | for compose ``g∘h`` the functions now specify whether the derivative is taken w.r.t. to the left (`g`) or right (`h`) argument | |`VeeOrthogonalBasis` | [`LieAlgebraOrthogonalBasis`](@ref) | | -# Notable changes +# Further notable changes -* The [`GeneralLinearGroup`](@ref) (formerly `GeneralLinear`) switched to using its Lie algebra to represent tangent vectors. +1. The [`GeneralLinearGroup`](@ref) (formerly `GeneralLinear`) switched to using its Lie algebra to represent tangent vectors. +2. Formerly, both a power of LieGroups as well as a LieGroup on the power manifold was possible. This is now unified to the latter, + the operation for power manifolds can hence stay the same as for the single manifold. +3. Formerly, product manifolds were stored as a [`ProductManifold`](@extref) of Lie groups and an indicator for the group operation, that the direct product should be used. This is switched to (as for the last point) internally only store a [`ProductManifold`](@extref) as well as a (new) [`ProductGroupOperation`](@ref) that specifies one group operation for every factor. +4. both the last two points achieve one unified modelling aspect of Lie groups: They are now always a Riemannian manifold `M` together with a group operation `op`, +but a Lie group does not store another Lie group (or product of them) internally. diff --git a/docs/styles/config/vocabularies/LieGroups/accept.txt b/docs/styles/config/vocabularies/LieGroups/accept.txt index 043f7a7c..a670565e 100644 --- a/docs/styles/config/vocabularies/LieGroups/accept.txt +++ b/docs/styles/config/vocabularies/LieGroups/accept.txt @@ -1,8 +1,8 @@ -julia -Julia +[jJ]ulia LieGroups.jl Lie algebra Lie algebras Lie group Lie groups -Riemannian \ No newline at end of file +Riemannian +semidirect \ No newline at end of file diff --git a/src/LieGroups.jl b/src/LieGroups.jl index 1b7d8ee8..11e15c14 100644 --- a/src/LieGroups.jl +++ b/src/LieGroups.jl @@ -32,12 +32,19 @@ include("group_operations/multiplication_operation.jl") include("group_actions/group_action_interface.jl") include("group_actions/group_operation_action.jl") +# Meta Lie groups +include("groups/power_group.jl") +include("groups/product_group.jl") +include("groups/semidirect_product_group.jl") # Lie groups include("groups/translation_group.jl") include("groups/general_linear_group.jl") export LieGroup, LieAlgebra +export PowerLieGroup, ProductLieGroup +export LeftSemidirectProductLieGroup, RightSemidirectProductLieGroup export LieAlgebraOrthogonalBasis +export ×, ^, ⋉, ⋊ # # # Group Operations @@ -45,16 +52,17 @@ export AbstractGroupOperation, Identity export AdditionGroupOperation export AbstractMultiplicationGroupOperation export MatrixMultiplicationGroupOperation +export ProductGroupOperation +export LeftSemidirectProductGroupOperation, RightSemidirectProductGroupOperation # # # Group Actions -export AbstractGroupActionType, AbstractGroupAction +export AbstractGroupActionType export AbstractLeftGroupActionType, AbstractRightGroupActionType -export LeftGroupOperation, RightGroupOperation -export InverseLeftGroupOperation, InverseRightGroupOperation -export GroupOperationAction - +export LeftGroupOperationAction, RightGroupOperationAction +export InverseLeftGroupOperationAction, InverseRightGroupOperationAction +export GroupAction, GroupOperationAction # # # Specific groups @@ -63,7 +71,9 @@ export TranslationGroup, GeneralLinearGroup export adjoint, adjoint!, apply, apply! export base_lie_group, base_manifold export compose, compose! -export det, +export default_left_action, + default_right_action, + det, diff_apply, diff_apply!, diff_group_apply, diff --git a/src/group_actions/group_action_interface.jl b/src/group_actions/group_action_interface.jl index 73d3eb79..badef955 100644 --- a/src/group_actions/group_action_interface.jl +++ b/src/group_actions/group_action_interface.jl @@ -5,7 +5,7 @@ @doc """ AbstractGroupActionType -An abstract supertype for group actions. +An abstract supertype for group action types, which are used within a [`GroupAction`](@ref). """ abstract type AbstractGroupActionType end @@ -85,51 +85,58 @@ $(_note_action_argument_order) """ abstract type AbstractRightGroupActionType <: AbstractGroupActionType end -@doc """ - AbstractGroupAction{T<:AbstractGroupActionType, L<:LieGroup, M<:AbstractManifold} +""" + GroupAction{T<:GroupActionType, L<:LieGroup, M<:AbstractManifold} -An abstract group action of [`AbstractGroupActionType`](@ref) `T` of a [`LieGroup`](@ref) of type `L` -acting on an $(_link(:AbstractManifold)) of type `M`. +Specify a group action of [`AbstractGroupActionType`](@ref) `T` of a [`LieGroup`](@ref) `G` acting on `M`. -See [HilgertNeeb:2012; Section 9.1.3](@cite) for more details. -""" -abstract type AbstractGroupAction{ - T<:AbstractGroupActionType,L<:LieGroup,M<:AbstractManifold -} end +Let ``$(_math(:M))`` be a $(_link(:AbstractManifold)) and ``$(_math(:G))`` be a [`LieGroup`](@ref) +with group operation ``$(_math(:∘))``. -function base_lie_group end -@doc """ - base_lie_group(A::AbstractGroupAction) +A (smooth) action of the group ``$(_math(:G))`` on the manifold ``$(_math(:M))`` is a map -Return the [`LieGroup`](@ref) of the [`AbstractGroupAction`](@ref) -specifying the action. -""" -base_lie_group(::AbstractGroupAction) +```math +σ: $(_math(:G)) × $(_math(:M)) → $(_math(:M)) +``` -@doc """ - base_manifold(A::AbstractGroupAction) +with the properties + +* ``σ($(_math(:e)), p) = p`` holds for all ``p ∈ $(_math(:M))`` +* ``σ(g, σ(h, p)) = σ(g$(_math(:∘))h, p)`` holds for all ``g,h ∈ $(_math(:G))``, ``p ∈ $(_math(:M))`` + + +# Fields + +* `type::T`: The type of the group action. +* `group::L`: The group acting. +* `manifold::M`: The manifold the group acts upon. + +See [HilgertNeeb:2012; Section 9.1.3](@cite) for more details. -Return the $(_link(:AbstractManifold)) the group action acts upon. """ -ManifoldsBase.base_manifold(::AbstractGroupAction) +struct GroupAction{T<:AbstractGroupActionType,L<:LieGroup,M<:ManifoldsBase.AbstractManifold} + type::T + group::L + manifold::M +end # # # Functions _doc_apply = """ - apply(A::AbstractGroupAction{T, L, M}, g, p) - apply!(A::AbstractGroupAction{T, L, M}, q, g, p) + apply(A::GroupAction{T, L, M}, g, p) + apply!(A::GroupAction{T, L, M}, q, g, p) Apply the group action induced by ``g ∈ $(_math(:G))`` to ``p ∈ $(_math(:M))``, where the kind of group action is indicated by the [`AbstractGroupActionType`](@ref) `T`. -This can be perfomed in-place of `q`. +This can be performed in-place of `q`. """ # function apply end # un-comment the preceding line and remove this, once GroupManifolds no longer exists in Manifolds.jl @doc "$(_doc_apply)" -function apply(A::AbstractGroupAction, g, p) +function apply(A::GroupAction, g, p) q = allocate_result(base_manifold(A), apply, g, p) apply!(A, q, g, p) return q @@ -138,11 +145,43 @@ end # Define `function apply! end` here as well # un-comment (remove this comment) when removing this function from Manifolds.jl @doc "$(_doc_apply)" -apply!(A::AbstractGroupAction, q, g, p) +apply!(A::GroupAction, q, g, p) + +function base_lie_group end +@doc """ + base_lie_group(A::GroupAction) + +Return the [`LieGroup`](@ref) of the [`GroupAction`](@ref) +specifying the action. +""" +base_lie_group(A::GroupAction) = A.group + +@doc """ + base_manifold(A::GroupAction) + +Return the $(_link(:AbstractManifold)) the group action acts upon. +""" +ManifoldsBase.base_manifold(A::GroupAction) = A.manifold + +function default_left_action end +""" + default_left_action(G::LieGroup, M::AbstractManifold) + +Return the default left action for a Lie group `G` acting on a manifold `M`. +""" +default_left_action(N::LieGroup, M::AbstractManifold) + +function default_right_action end +""" + default_right_action(G::LieGroup, M::AbstractManifold) + +Return the default right action for a Lie group `G` acting on a manifold `M`. +""" +default_right_action(N::LieGroup, M::AbstractManifold) _doc_diff_apply = """ - diff_apply(A::AbstractGroupAction{T, L, M}, g, p, X) - diff_apply!(A::AbstractGroupAction{T, L, M}, Y, g, p, X) + diff_apply(A::GroupAction{T, L, M}, g, p, X) + diff_apply!(A::GroupAction{T, L, M}, Y, g, p, X) Compute the differential ``D_p σ_g(p): T_p$(_math(:M)) → T_{σ_g(p)}$(_math(:M))``, where for a left group action we have ``σ_g(p) = σ(g,p)``, for a right action ``σ_g(p) = σ(p, g)``. @@ -150,7 +189,7 @@ where for a left group action we have ``σ_g(p) = σ(g,p)``, for a right action function diff_apply end @doc "$(_doc_diff_apply)" -function diff_apply(A::AbstractGroupAction, g, p, X) +function diff_apply(A::GroupAction, g, p, X) Y = allocate_result(base_manifold(A), apply_diff_group, p, g, X) diff_apply!(A, Y, g, p, X) return Y @@ -158,11 +197,11 @@ end function diff_apply! end @doc "$(_doc_diff_apply)" -diff_apply!(A::AbstractGroupAction, q, g, p) +diff_apply!(A::GroupAction, q, g, p) _doc_diff_group_apply = """ - diff_group_apply(A::AbstractGroupAction{T, L, M}, g, p, X) - diff_group_apply!(A::AbstractGroupAction{T, L, M}, Y, g, p, X) + diff_group_apply(A::GroupAction{T, L, M}, g, p, X) + diff_group_apply!(A::GroupAction{T, L, M}, Y, g, p, X) Compute the differential ``D_g σ_g(p): $(_math(:𝔤)) → $(_math(:𝔤))``, where we use the short hand notation ``σ_p(g) = σ(g,p)`` for a left action, @@ -171,7 +210,7 @@ and for a right action ``σ_p(g) = σ(p, g)``. function diff_group_apply end @doc "$(_doc_diff_group_apply)" -function diff_group_apply(A::AbstractGroupAction, g, p, X) +function diff_group_apply(A::GroupAction, g, p, X) Y = allocate_result(base_manifold(A), apply, g, p, X) diff_group_apply!(A, Y, g, p, X) return Y @@ -179,33 +218,38 @@ end function diff_group_apply! end @doc "$(_doc_diff_group_apply)" -diff_group_apply!(A::AbstractGroupAction, q, g, p) +diff_group_apply!(A::GroupAction, q, g, p) @doc """ - inv(A::AbstractGroupAction{T}) + inv(A::GroupAction{T}) -Return the tuple representing the inverse of an [`AbstractGroupAction`](@ref) of [`AbstractGroupActionType`](@ref) `T`. +Return the [`GroupAction`](@ref) representing the inverse of an [`GroupAction`](@ref) of [`AbstractGroupActionType`](@ref) `T`. This is usually done by returning the group action with the inverse type of `T`. """ -Base.inv(::AbstractGroupAction) +Base.inv(A::GroupAction) = GroupAction(inv(A.type), A.group, A.manifold) -@doc """ - inv(T::AbstractGroupActionType) +""" + inv(::AbstractGroupActionType) -Return the type representing the inverse of an [`AbstractGroupActionType`](@ref). +return the inverse group operation action, that is, use the type representing the +inverse operation. """ Base.inv(::AbstractGroupActionType) +function Base.show(io::IO, A::GroupAction) + return print(io, "GroupAction($(A.type), $(A.group), $(A.manifold))") +end + function switch end @doc """ - switch(A::AbstractGroupAction{T}) + switch(A::GroupAction{T}) -Return the group operation action representing the similar [`AbstractGroupAction`](@ref) of [`AbstractGroupActionType`](@ref) `T` +Return the group operation action representing the similar [`GroupAction`](@ref) of [`AbstractGroupActionType`](@ref) `T` but acting from the other side. It switches left to right and vice versa. This is done by returning the group action with the “switched” type of `T`. """ -switch(::AbstractGroupAction) +switch(A::GroupAction) = GroupAction(switch(A.type), A.group, A.manifold) @doc """ switch(T::AbstractGroupActionType) diff --git a/src/group_actions/group_operation_action.jl b/src/group_actions/group_operation_action.jl index 6796bb5c..e2719252 100644 --- a/src/group_actions/group_operation_action.jl +++ b/src/group_actions/group_operation_action.jl @@ -1,5 +1,5 @@ """ - LeftGroupOperation <: AbstractLeftGroupActionType + LeftGroupOperationAction <: AbstractLeftGroupActionType A type for the [`AbstractLeftGroupActionType`](@ref) when acting on the group itself from the left, that is @@ -8,29 +8,29 @@ from the left, that is σ_h(g) = σ(h,g) = h$(_math(:∘))g ``` -for its inverse ``(σ_h)^{-1}`` see [`InverseLeftGroupOperation`](@ref). +for its inverse ``(σ_h)^{-1}`` see [`InverseLeftGroupOperationAction`](@ref). """ -struct LeftGroupOperation <: AbstractLeftGroupActionType end +struct LeftGroupOperationAction <: AbstractLeftGroupActionType end """ - RightGroupOperation <: AbstractRightGroupActionType + RightGroupOperationAction <: AbstractRightGroupActionType A type for the [`AbstractLeftGroupActionType`](@ref) when acting on the group itself -gfrom the right +from the right. ```math σ_h(g) = σ(h,g) = g$(_math(:∘))h ``` -for its inverse ``(σ_h)^{-1}`` see [`InverseRightGroupOperation`](@ref). +for its inverse ``(σ_h)^{-1}`` see [`InverseRightGroupOperationAction`](@ref). """ -struct RightGroupOperation <: AbstractLeftGroupActionType end +struct RightGroupOperationAction <: AbstractLeftGroupActionType end """ - InverseLeftGroupOperation <: AbstractRightGroupActionType + InverseLeftGroupOperationAction <: AbstractRightGroupActionType A type for the [`AbstractLeftGroupActionType`](@ref) when acting on the group itself -given by the inverse of a [`LeftGroupOperation`](@ref) ``σ_h`` as +given by the inverse of a [`LeftGroupOperationAction`](@ref) ``σ_h`` as ```math τ_h(g) $(_tex(:def)) σ_h^{-1}(g) = σ(h^{-1},g) = h^{-1}$(_math(:∘))g @@ -41,18 +41,18 @@ properties yield that is is an [`AbstractRightGroupActionType`](@ref), since $(_note(:LeftInverseActionIsRight)) -for its inverse ``(σ_h)^{-1}`` see [`InverseLeftGroupOperation`](@ref). +for its inverse ``(σ_h)^{-1}`` see [`InverseLeftGroupOperationAction`](@ref). !!! note Some literature also calls this by itself _the_ right group operation action. """ -struct InverseLeftGroupOperation <: AbstractRightGroupActionType end +struct InverseLeftGroupOperationAction <: AbstractRightGroupActionType end """ - InverseRightGroupOperation <: AbstractLeftGroupActionType + InverseRightGroupOperationAction <: AbstractLeftGroupActionType A type for the [`AbstractLeftGroupActionType`](@ref) when acting on the group itself -given by the inverse of a [`RightGroupOperation`](@ref) ``σ_h`` as +given by the inverse of a [`RightGroupOperationAction`](@ref) ``σ_h`` as ```math τ_h(g) $(_tex(:def)) σ_h^{-1}(g) = σ(h^{-1},g) = g$(_math(:∘))h^{-1} @@ -63,183 +63,119 @@ properties yield that is is an [`AbstractLeftGroupActionType`](@ref), since $(_note(:RightInverseActionIsLeft)) -for its inverse ``(σ_h)^{-1}`` see [`InverseLeftGroupOperation`](@ref). +for its inverse ``(σ_h)^{-1}`` see [`InverseLeftGroupOperationAction`](@ref). """ -struct InverseRightGroupOperation <: AbstractLeftGroupActionType end +struct InverseRightGroupOperationAction <: AbstractLeftGroupActionType end """ - GroupOperationAction{T<:AbstractLeftGroupActionType,G<:LieGroup} <: AbstractGroupAction{T,G,G} - -The [`AbstractGroupAction`](@ref). + GroupOperationAction(action::AbstractGroupActionType, group::LieGroup) +Return a [`GroupAction`](@ref) for an [`AbstractGroupActionType`](@ref) `action` +representing the group operation as an action of the group on itself. """ -struct GroupOperationAction{T<:AbstractGroupActionType,G<:LieGroup} <: - AbstractGroupAction{T,G,G} - type::T - group::G +function GroupOperationAction(action::AbstractGroupActionType, G::LieGroup) + return GroupAction(action, G, G) +end +function Base.show( + io::IO, GA::GroupAction{A,G,G} +) where {A<:AbstractGroupActionType,G<:LieGroup} + return print(io, "GroupOperationAction($(GA.type), $(GA.group))") end -base_lie_group(A::GroupOperationAction) = A.group - -ManifoldsBase.base_manifold(A::GroupOperationAction) = A.group - -_doc_apply_groupop = """ - apply(A::GroupOperationAction, g, h) - apply!(A::GroupOperationAction, k, g, h) - -apply the stored group operation action, using [`compose`](@ref) ``$(_math(:∘))``. -this can be done in-place of `k`. -""" - -@doc "$(_doc_apply_groupop)" -apply(A::GroupOperationAction, g, h) - -@doc "$(_doc_apply_groupop)" -apply!(A::GroupOperationAction, k, g, h) - -function apply!(A::GroupOperationAction{LeftGroupOperation}, k, g, h) +function apply!(A::GroupAction{LeftGroupOperationAction}, k, g, h) return compose!(A.group, k, g, h) #apply/compose g from left end -function apply!(A::GroupOperationAction{RightGroupOperation}, k, g, h) +function apply!(A::GroupAction{RightGroupOperationAction}, k, g, h) return compose!(A.group, k, h, g) #apply/compose g from right end -function apply!(A::GroupOperationAction{InverseLeftGroupOperation}, k, g, h) +function apply!(A::GroupAction{InverseLeftGroupOperationAction}, k, g, h) return inv_left_compose!(A.group, k, g, h) #apply/compose inv(g) from left end -function apply!(A::GroupOperationAction{InverseRightGroupOperation}, k, g, h) +function apply!(A::GroupAction{InverseRightGroupOperationAction}, k, g, h) return inv_right_compose!(A.group, k, g, h) #apply/compose inv(g) from right end -_doc_diff_apply_groupop = """ - - diff_apply(A::GroupOperationAction, g, p, X) - -For the group operation action ``σ_g(p)``, compute the differential -``D_p σ_g(p): T_p$(_math(:G)) → T_{σ_g(p)}$(_math(:G))``, that is - -* for the [`LeftGroupOperation`](@ref) this calls [`diff_right_compose`](@ref)`(G, g, p, X)`, since here ``σ_g(p) = g$(_math(:∘))p`` -* for the [`RightGroupOperation`](@ref) this calls [`diff_left_compose`](@ref)`(G, p, g, X)`, since here ``σ_g(p) = p$(_math(:∘))g`` -* for the [`InverseLeftGroupOperation`](@ref) this calls [`diff_right_compose`](@ref) with ``g^{-1}``, since here ``σ_g(p) = g^{-1}$(_math(:∘))p`` -* for the [`InverseRightGroupOperation`](@ref) this calls [`diff_left_compose`](@ref) with ``g^{-1}``, since here ``σ_g(p) = p$(_math(:∘))g^{-1}`` -""" -@doc "$(_doc_diff_apply_groupop)" -diff_apply(A::GroupOperationAction, g, p, X) - -@doc "$(_doc_diff_apply_groupop)" -diff_apply!(A::GroupOperationAction, Y, g, p, X) - -function diff_apply!(A::GroupOperationAction{LeftGroupOperation}, Y, g, p, X) +function diff_apply!(A::GroupAction{LeftGroupOperationAction}, Y, g, p, X) return diff_right_compose!(A.group, Y, g, p, X) end -function diff_apply!(A::GroupOperationAction{RightGroupOperation}, Y, g, p, X) +function diff_apply!(A::GroupAction{RightGroupOperationAction}, Y, g, p, X) return diff_left_compose!(A.group, Y, p, g, X) end -function diff_apply!(A::GroupOperationAction{InverseLeftGroupOperation}, Y, g, p, X) +function diff_apply!(A::GroupAction{InverseLeftGroupOperationAction}, Y, g, p, X) return diff_right_compose!(A.group, Y, inv(A.group, g), p, X) end -function diff_apply!(A::GroupOperationAction{InverseRightGroupOperation}, Y, g, p, X) +function diff_apply!(A::GroupAction{InverseRightGroupOperationAction}, Y, g, p, X) return diff_left_compose!(A.group, Y, p, inv(A.group, g), X) end -_doc_diff_group_apply_groupop = """ - - diff_group_apply(A::GroupOperationAction, g, p, X) - -Compute the differential ``D_g σ_g(p): $(_math(:𝔤)) → $(_math(:𝔤))`` of a group operation action, -that is - -* for the [`LeftGroupOperation`](@ref) this calls [`diff_left_compose`](@ref)`(G, g, p, X)`, since here ``σ_g(p) = g$(_math(:∘))p`` -* for the [`RightGroupOperation`](@ref) this calls [`diff_right_compose`](@ref)`(G, p, g, X)`, since here ``σ_g(p) = p$(_math(:∘))g`` -* for the [`InverseLeftGroupOperation`](@ref) this calls [`diff_left_compose`](@ref) with ``g^{-1}``, since here ``σ_g(p) = g^{-1}$(_math(:∘))p`` together with [`diff_inv`](@ref) -* for the [`InverseRightGroupOperation`](@ref) this calls [`diff_right_compose`](@ref) with ``g^{-1}``, since here ``σ_g(p) = p$(_math(:∘))g^{-1}`` together with [`diff_inv`](@ref) -""" - -@doc "$(_doc_diff_apply_groupop)" -diff_group_apply(A::GroupOperationAction, g, p, X) - -@doc "$(_doc_diff_apply_groupop)" -diff_group_apply!(A::GroupOperationAction, Y, g, p, X) - -function diff_group_apply!(A::GroupOperationAction{LeftGroupOperation}, Y, g, p, X) +function diff_group_apply!(A::GroupAction{LeftGroupOperationAction}, Y, g, p, X) return diff_left_compose!(A.group, Y, g, p, X) end -function diff_group_apply!(A::GroupOperationAction{RightGroupOperation}, Y, g, p, X) +function diff_group_apply!(A::GroupAction{RightGroupOperationAction}, Y, g, p, X) return diff_right_compose!(A.group, Y, p, g, X) end -function diff_group_apply!(A::GroupOperationAction{InverseLeftGroupOperation}, Y, g, p, X) +function diff_group_apply!(A::GroupAction{InverseLeftGroupOperationAction}, Y, g, p, X) diff_inv!(A.group, Y, g, X) return diff_left_compose!(A.group, Y, inv(A.group, g), p, Y) end -function diff_group_apply!(A::GroupOperationAction{InverseRightGroupOperation}, Y, g, p, X) +function diff_group_apply!(A::GroupAction{InverseRightGroupOperationAction}, Y, g, p, X) diff_inv!(A.group, Y, g, X) return diff_right_compose!(A.group, Y, p, inv(A.group, g), Y) end """ - inv(::GroupOperationAction) + inv(::LeftGroupOperationAction) -return the inverse group operation action, that is, use the type representing the -inverse operation. +Return the inverse of the [`LeftGroupOperationAction`](@ref), that is the [`InverseLeftGroupOperationAction`](@ref). """ -Base.inv(A::GroupOperationAction) = GroupOperationAction(inv(A.type), A.group) - +Base.inv(::LeftGroupOperationAction) = InverseLeftGroupOperationAction() """ - inv(::LeftGroupOperation) + inv(::RightGroupOperationAction) -Return the inverse of the [`LeftGroupOperation`](@ref), that is the [`InverseLeftGroupOperation`](@ref). +Return the inverse of the [`RightGroupOperationAction`](@ref), that is the [`InverseRightGroupOperationAction`](@ref). """ -Base.inv(::LeftGroupOperation) = InverseLeftGroupOperation() +Base.inv(::RightGroupOperationAction) = InverseRightGroupOperationAction() """ - inv(::RightGroupOperation) + inv(::InverseLeftGroupOperationAction) -Return the inverse of the [`RightGroupOperation`](@ref), that is the [`InverseRightGroupOperation`](@ref). +Return the inverse of the [`InverseLeftGroupOperationAction`](@ref), that is the [`LeftGroupOperationAction`](@ref). """ -Base.inv(::RightGroupOperation) = InverseRightGroupOperation() +Base.inv(::InverseLeftGroupOperationAction) = LeftGroupOperationAction() """ - inv(::InverseLeftGroupOperation) + inv(::InverseRightGroupOperationAction) -Return the inverse of the [`InverseLeftGroupOperation`](@ref), that is the [`LeftGroupOperation`](@ref). +Return the inverse of the [`InverseRightGroupOperationAction`](@ref), that is the [`RightGroupOperationAction`](@ref). """ -Base.inv(::InverseLeftGroupOperation) = LeftGroupOperation() -""" - inv(::InverseRightGroupOperation) - -Return the inverse of the [`InverseRightGroupOperation`](@ref), that is the [`RightGroupOperation`](@ref). -""" -Base.inv(::InverseRightGroupOperation) = RightGroupOperation() - -function Base.show(io::IO, A::GroupOperationAction) - return print(io, "GroupOperationAction($(A.type), $(A.group))") -end +Base.inv(::InverseRightGroupOperationAction) = RightGroupOperationAction() """ - switch(::LeftGroupOperation) + switch(::LeftGroupOperationAction) -Return the [`RightGroupOperation`](@ref), that is, +Return the [`RightGroupOperationAction`](@ref), that is, turns ``σ_g = g$(_math(:∘))h`` into ``τ_g(h) = h$(_math(:∘))g`` """ -switch(::LeftGroupOperation) = RightGroupOperation() +switch(::LeftGroupOperationAction) = RightGroupOperationAction() """ - switch(::RightGroupOperation) + switch(::RightGroupOperationAction) -Return the [`LeftGroupOperation`](@ref), that is, +Return the [`LeftGroupOperationAction`](@ref), that is, turns ``σ_g = h$(_math(:∘))g`` into ``τ_g(h) = g$(_math(:∘))h`` """ -switch(::RightGroupOperation) = LeftGroupOperation() +switch(::RightGroupOperationAction) = LeftGroupOperationAction() """ - switch(::InverseLeftGroupOperation) + switch(::InverseLeftGroupOperationAction) -Return the [`InverseRightGroupOperation`](@ref), that is, +Return the [`InverseRightGroupOperationAction`](@ref), that is, turns ``σ_g = g^{-1}$(_math(:∘))h`` into ``τ_g(h) = h$(_math(:∘))g^{-1}`` """ -switch(::InverseLeftGroupOperation) = InverseRightGroupOperation() +switch(::InverseLeftGroupOperationAction) = InverseRightGroupOperationAction() """ - switch(::InverseRightGroupOperation) + switch(::InverseRightGroupOperationAction) -Return the [`InverseLeftGroupOperation`](@ref), that is, +Return the [`InverseLeftGroupOperationAction`](@ref), that is, turns ``σ_g = h$(_math(:∘))g^{-1}`` into ``τ_g(h) = g^{-1}$(_math(:∘))h`` """ -switch(::InverseRightGroupOperation) = InverseLeftGroupOperation() +switch(::InverseRightGroupOperationAction) = InverseLeftGroupOperationAction() diff --git a/src/groups/power_group.jl b/src/groups/power_group.jl new file mode 100644 index 00000000..ccdcbb42 --- /dev/null +++ b/src/groups/power_group.jl @@ -0,0 +1,336 @@ +# +# +# Power Lie groups: work element wise + +@doc """ + PowerGroupOperation{O<:AbstractGroupOperation} <: AbstractGroupOperation + +A struct do model a that a certain group operation is applied element-wise on a [`PowerManifold`](@extref `ManifoldsBase.PowerManifold`). + +# Constructor + + PowerGroupOperation(o::AbstractGroupOperation) +""" +struct PowerGroupOperation{O<:AbstractGroupOperation} <: AbstractGroupOperation + op::O +end + +@doc """ + PowerLieGroup(G::LieGroup, args...; kwargs...) + (G::LieGroup)^(n::Integer) = PowerLieGroup(G, n) + +Generate the [`LieGroup`](@ref) of the `n`-th power of a Lie group `G` or manifold `M`. +If passed a Lie group `G`, the group operation on the [`PowerLieGroup`](@ref) is the same as on `G`, +but applied elementwise. Internally, the corresponding [`PowerGroupOperation`](@ref) is created. +If you pass a manifold `M`, you have to provide the corresponding [`PowerGroupOperation`](@ref) yourself. + +Bot the arguments `args...` as well as the keyword arguments `kwargs...` are passed on to +the constructor of the [`PowerManifold`](@extref `ManifoldsBase.PowerManifold`). +This especially includes the `size` of the manifold and allows to specify a [`NestedPowerRepresentation`](@extref `ManifoldsBase.NestedPowerRepresentation`). +""" +PowerLieGroup(::AbstractManifold, args...; kwargs...) + +function PowerLieGroup(G::LieGroup, args...; kwargs...) + M = G.manifold + pM = ManifoldsBase.PowerManifold(M, args...; kwargs...) + return LieGroup(pM, PowerGroupOperation(G.op)) +end + +Base.:^(G::LieGroup, n::Integer) = PowerLieGroup(G, n) + +function _compose!( + PoG::LieGroup{𝔽,Op,M}, k, g, h +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + compose!( + G, + ManifoldsBase._write(PM, rep_size, k, i), + ManifoldsBase._read(PM, rep_size, g, i), + ManifoldsBase._read(PM, rep_size, h, i), + ) + end + return k +end + +function ManifoldsBase.check_size( + PoG::LieGroup{𝔽,Op,M}, g +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + return ManifoldsBase.check_size(PoG.manifold, g) +end +function ManifoldsBase.check_size( + ::LieGroup{𝔽,Op,M}, ::Identity{Op} +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + return nothing +end +function ManifoldsBase.check_size( + G::LieGroup{𝔽,Op,M}, e::Identity +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + return DomainError( + "The Identity $e is not the identity of the group, expected $(Identity(G.op))." + ) +end +function ManifoldsBase.check_size( + PoG::LieGroup{𝔽,Op,M}, g, X +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + return ManifoldsBase.check_size(PoG.manifold, g, X) +end + +function conjugate!( + PoG::LieGroup{𝔽,Op,M}, h, g, k +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + conjugate!( + G, + ManifoldsBase._write(PM, rep_size, h, i), + ManifoldsBase._read(PM, rep_size, g, i), + ManifoldsBase._read(PM, rep_size, k, i), + ) + end + return h +end + +function diff_conjugate!( + PoG::LieGroup{𝔽,Op,M}, Y, g, h, X +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + diff_conjugate!( + G, + ManifoldsBase._write(PM, rep_size, Y, i), + ManifoldsBase._read(PM, rep_size, g, i), + ManifoldsBase._read(PM, rep_size, h, i), + ManifoldsBase._read(PM, rep_size, X, i), + ) + end + return Y +end +function diff_conjugate!( + PoG::LieGroup{𝔽,Op,M}, Y, g, ::Identity{Op}, X +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + e_g = Identity(G) + for i in ManifoldsBase.get_iterator(PM) + diff_conjugate!( + G, + ManifoldsBase._write(PM, rep_size, Y, i), + ManifoldsBase._read(PM, rep_size, g, i), + e_g, + ManifoldsBase._read(PM, rep_size, X, i), + ) + end + return Y +end +function diff_inv!( + PoG::LieGroup{𝔽,Op,M}, Y, g, X +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + diff_inv!( + G, + ManifoldsBase._write(PM, rep_size, Y, i), + ManifoldsBase._read(PM, rep_size, g, i), + ManifoldsBase._read(PM, rep_size, X, i), + ) + end + return Y +end + +function diff_left_compose!( + PoG::LieGroup{𝔽,Op,M}, Y, g, h, X +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + diff_left_compose!( + G, + ManifoldsBase._write(PM, rep_size, Y, i), + ManifoldsBase._read(PM, rep_size, g, i), + ManifoldsBase._read(PM, rep_size, h, i), + ManifoldsBase._read(PM, rep_size, X, i), + ) + end + return Y +end + +function diff_right_compose!( + PoG::LieGroup{𝔽,Op,M}, Y, g, h, X +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + diff_right_compose!( + G, + ManifoldsBase._write(PM, rep_size, Y, i), + ManifoldsBase._read(PM, rep_size, g, i), + ManifoldsBase._read(PM, rep_size, h, i), + ManifoldsBase._read(PM, rep_size, X, i), + ) + end + return Y +end + +function ManifoldsBase.exp!( + PoG::LieGroup{𝔽,Op,M}, h, g, X, t::Number=1 +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + exp!( + G, + ManifoldsBase._write(PM, rep_size, h, i), + ManifoldsBase._read(PM, rep_size, g, i), + ManifoldsBase._read(PM, rep_size, X, i), + t, + ) + end + return h +end + +function ManifoldsBase.exp!( + PoG::LieGroup{𝔽,Op,M}, h, ::Identity{Op}, X, t::Number=1 +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + e_g = Identity(G) + for i in ManifoldsBase.get_iterator(PM) + exp!( + G, + ManifoldsBase._write(PM, rep_size, h, i), + e_g, + ManifoldsBase._read(PM, rep_size, X, i), + t, + ) + end + return h +end + +function hat!( + PoG::LieGroup{𝔽,Op,M}, X, c +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM.manifold) + dim = manifold_dimension(PM.manifold) + v_iter = 1 + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + hat!(G, ManifoldsBase._write(PM, rep_size, X, i), c[v_iter:(v_iter + dim - 1)]) + v_iter += dim + end + return X +end + +function identity_element!( + PoG::LieGroup{𝔽,Op,M}, e +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + identity_element!(G, ManifoldsBase._write(PM, rep_size, e, i)) + end + return e +end + +function inv!( + PoG::LieGroup{𝔽,Op,M}, h, g +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + for i in ManifoldsBase.get_iterator(PM) + inv!( + G, + ManifoldsBase._write(PM, rep_size, h, i), + ManifoldsBase._read(PM, rep_size, g, i), + ) + end + return h +end +function inv!( + PoG::LieGroup{𝔽,Op,M}, h, ::Identity{Op} +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + e_g = Identity(G.op) + for i in ManifoldsBase.get_iterator(PM) + inv!(G, ManifoldsBase._write(PM, rep_size, h, i), e_g) + end + return h +end + +function lie_bracket!( + PoA::LieAlgebra{𝔽,Op,<:LieGroup{𝔽,Op,M}}, Z, X, Y +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoA.manifold.manifold + rep_size = representation_size(PM) + 𝔤 = LieAlgebra(LieGroup(PM.manifold, PoA.manifold.op.op)) + for i in ManifoldsBase.get_iterator(PM) + lie_bracket!( + 𝔤, + ManifoldsBase._write(PM, rep_size, Z, i), + ManifoldsBase._read(PM, rep_size, X, i), + ManifoldsBase._read(PM, rep_size, Y, i), + ) + end + return Z +end + +function ManifoldsBase.log!( + PoG::LieGroup{𝔽,Op,M}, X, ::Identity{Op}, g +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM) + G = LieGroup(PM.manifold, PoG.op.op) + e_g = Identity(G.op) + for i in ManifoldsBase.get_iterator(PM) + log!( + G, + ManifoldsBase._write(PM, rep_size, X, i), + e_g, + ManifoldsBase._read(PM, rep_size, g, i), + ) + end + return X +end + +function Base.show( + io::IO, G::LieGroup{𝔽,Op,M} +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = G.manifold + POp = G.op + L = LieGroup(PM.manifold, POp.op) + size = Manifolds.get_parameter(G.manifold.size) + return print(io, "PowerLieGroup($L, $(join(size, ", ")))") +end + +function vee!( + PoG::LieGroup{𝔽,Op,M}, c, X +) where {𝔽,Op<:PowerGroupOperation,M<:ManifoldsBase.AbstractPowerManifold} + PM = PoG.manifold + rep_size = representation_size(PM.manifold) + dim = manifold_dimension(PM.manifold) + G = LieGroup(PM.manifold, PoG.op.op) + v_iter = 1 + for i in ManifoldsBase.get_iterator(PM) + vee!(G, view(c, v_iter:(v_iter + dim - 1)), ManifoldsBase._read(PM, rep_size, X, i)) + v_iter += dim + end + return c +end diff --git a/src/groups/product_group.jl b/src/groups/product_group.jl new file mode 100644 index 00000000..a27529a8 --- /dev/null +++ b/src/groups/product_group.jl @@ -0,0 +1,298 @@ +# +# +# Together with a product group operation, that is a tuple of operations, +# One for each factor, we define the product operation as acting element wise. + +""" + ProductGroupOperation{O<:<:NTuple{N,AbstractGroupOperation} where N} <: AbstractGroupOperation + +A struct do model a tuple of group operations, one for each factor of a product group, +that together forms a new group operation. + +# Constructor + + ProductGroupOperation(o::AbstractGroupOperation...) + ×(o::AbstractGroupOperation...) = ProductGroupOperation(o...) +""" +struct ProductGroupOperation{OTM<:NTuple{N,AbstractGroupOperation} where {N}} <: + AbstractGroupOperation + operations::OTM +end +function ProductGroupOperation(operations::AbstractGroupOperation...) + return ProductGroupOperation(operations) +end + +@doc raw""" + cross(O1::AbstractGroupOperation, O2::AbstractGroupOperation) + O1 × O2 + O1 × O2 × O3 × ... + +Return the [`ProductGroupOperation`](@ref) For two [AbstractGroupOperation`](@ref) `O1` and `O2`, +where for the case that one of them is a [`ProductGroupOperation`](@ref) itself, +the other is either prepended (if `O1` is a product) or appended (if `O2` is). +If both are product operations, they are combined into one, keeping the order of operations. + +For the case that more than two are concatenated with `×` this is iterated. +""" +cross(::AbstractGroupOperation...) +function LinearAlgebra.cross(O1::AbstractGroupOperation, O2::AbstractGroupOperation) + return ProductGroupOperation(O1, O2) +end +function LinearAlgebra.cross(P::ProductGroupOperation, O::AbstractGroupOperation) + return ProductGroupOperation(P.operations..., O) +end +function LinearAlgebra.cross(O::AbstractGroupOperation, P::ProductGroupOperation) + return ProductGroupOperation(O, P.operations...) +end +function LinearAlgebra.cross(P1::ProductGroupOperation, P2::ProductGroupOperation) + return ProductGroupOperation(P1.operations..., P2.operations...) +end + +""" + ProductLieGroup(G, H, ...) + +Return the [`LieGroup`](@ref) of the product of Lie groups `G` and `H`. + +Alternatively, the short hand `G × H` can be used. +""" +function ProductLieGroup(G::LieGroup, H::LieGroup) + return LieGroup(G.manifold × H.manifold, G.op × H.op) +end + +function _compose!( + PrG::LieGroup{𝔽,Op,M}, k, g, h +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + map( + compose!, + map(LieGroup, PrG.manifold.manifolds, PrG.op.operations), + submanifold_components(PrG.manifold, k), + submanifold_components(PrG.manifold, g), + submanifold_components(PrG.manifold, h), + ) + return k +end + +function ManifoldsBase.check_size( + PrG::LieGroup{𝔽,Op,M}, g +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + return ManifoldsBase.check_size(PrG.manifold, g) +end +function ManifoldsBase.check_size( + ::LieGroup{𝔽,Op,M}, ::Identity +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + return nothing +end +function ManifoldsBase.check_size( + PrG::LieGroup{𝔽,Op,M}, g, X +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + return ManifoldsBase.check_size(PrG.manifold, g, X) +end + +function conjugate!( + PrG::LieGroup{𝔽,Op,M}, k, g, h +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + conjugate!, + map(LieGroup, PrM.manifolds, PrG.op.operations), + submanifold_components(PrM, k), + submanifold_components(PrM, g), + submanifold_components(PrM, h), + ) + return k +end + +@doc raw""" + cross(G::LieGroup, H::LieGroup) + G × H + G1 × G2 × G3 × ... + +Return the [`ProductLieGroup`](@ref) For two [`LieGroups`](@ref) `G` and `H`, +where for the case that one of them is a [`ProductLieGroup`](@ref) itself, +the other is either prepended (if `H` is a product) or appended (if `G` is). +If both are product Lie groups, they are combined into one, keeping the order of operations. + +For the case that more than two are concatenated with `×` this is iterated. +""" +cross(::LieGroup...) +function LinearAlgebra.cross(G::LieGroup, H::LieGroup) + return ProductLieGroup(G, H) +end + +function diff_conjugate!( + PrG::LieGroup{𝔽,Op,M}, Y, g, h, X +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + diff_conjugate!, + LieGroup.(PrM.manifolds, PrG.op.operations), + submanifold_components(PrG, Y), + submanifold_components(PrG, g), + submanifold_components(PrG, h), + submanifold_components(PrG, X), + ) + return Y +end + +function diff_inv!( + PrG::LieGroup{𝔽,Op,M}, Y, g, X +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + diff_inv!, + LieGroup.(PrM.manifolds, PrG.op.operations), + submanifold_components(PrG, Y), + submanifold_components(PrG, g), + submanifold_components(PrG, X), + ) + return Y +end + +function diff_left_compose!( + PrG::LieGroup{𝔽,Op,M}, Y, g, h, X +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + diff_left_compose!, + LieGroup.(PrM.manifolds, PrG.op.operations), + submanifold_components(PrG, Y), + submanifold_components(PrG, g), + submanifold_components(PrG, h), + submanifold_components(PrG, X), + ) + return Y +end + +function diff_right_compose!( + PrG::LieGroup{𝔽,Op,M}, Y, g, h, X +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + diff_right_compose!, + LieGroup.(PrM.manifolds, PrG.op.operations), + submanifold_components(PrG, Y), + submanifold_components(PrG, g), + submanifold_components(PrG, h), + submanifold_components(PrG, X), + ) + return Y +end + +function ManifoldsBase.exp!( + PrG::LieGroup{𝔽,Op,M}, h, ::Identity{Op}, X, t::Number=1 +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + (M, h, e, X) -> exp!(M, h, e, X, t), # introduce a function with “hard coded” t + map(LieGroup, PrM.manifolds, PrG.op.operations), + submanifold_components(PrM, h), + map(Identity, PrG.op.operations), + submanifold_components(PrM, X), + ) + return h +end + +function hat!( + PrG::LieGroup{𝔽,Op,M}, X, c +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + dims = map(manifold_dimension, PrM.manifolds) + @assert length(c) == sum(dims) + dim_ranges = ManifoldsBase._get_dim_ranges(dims) + Prc = map(dr -> (@inbounds view(c, dr)), dim_ranges) + PrL = LieGroup.(PrM.manifolds, PrG.op.operations) + ts = ManifoldsBase.ziptuples(PrL, submanifold_components(PrM, X), Prc) + map(ts) do t + return hat!(t...) + end + return X +end + +function identity_element!( + PrG::LieGroup{𝔽,Op,M}, e +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + identity_element!, + LieGroup.(PrM.manifolds, PrG.op.operations), + submanifold_components(PrM, e), + ) + return e +end + +function inv!( + PrG::LieGroup{𝔽,Op,M}, h, g +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + inv!, + map(LieGroup, PrM.manifolds, PrG.op.operations), + submanifold_components(PrM, h), + submanifold_components(PrM, g), + ) + return h +end +function inv!( + PrG::LieGroup{𝔽,Op,M}, h, ::Identity{Op} +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + inv!, + map(LieGroup, PrM.manifolds, PrG.op.operations), + submanifold_components(PrM, h), + map(Identity, PrG.op.operations), + ) + return h +end + +function lie_bracket!( + PrA::LieAlgebra{𝔽,Op,<:LieGroup{𝔽,Op,M}}, Z, X, Y +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrA.manifold.manifold + map( + lie_bracket!, + map(LieAlgebra, LieGroup.(PrM.manifolds, PrA.manifold.op.operations)), + submanifold_components(PrM, Z), + submanifold_components(PrM, X), + submanifold_components(PrM, Y), + ) + return Z +end + +function ManifoldsBase.log!( + PrG::LieGroup{𝔽,Op,M}, X, ::Identity{Op}, g +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + map( + log!, + map(LieGroup, PrM.manifolds, PrG.op.operations), + submanifold_components(PrM, X), + map(Identity, PrG.op.operations), + submanifold_components(PrM, g), + ) + return X +end + +function Base.show( + io::IO, G::LieGroup{𝔽,Op,M} +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = G.manifold.manifolds + ops = G.op.operations + return print(io, "ProductLieGroup($(join(PrM, " × ")), $(join(ops, " × ")))") +end + +function vee!( + PrG::LieGroup{𝔽,Op,M}, c, X +) where {𝔽,Op<:ProductGroupOperation,M<:ManifoldsBase.ProductManifold} + PrM = PrG.manifold + dims = map(manifold_dimension, PrM.manifolds) + @assert length(c) == sum(dims) + dim_ranges = ManifoldsBase._get_dim_ranges(dims) + Prc = map(dr -> (@inbounds view(c, dr)), dim_ranges) + PrL = LieGroup.(PrM.manifolds, PrG.op.operations) + ts = ManifoldsBase.ziptuples(PrL, Prc, submanifold_components(PrM, X)) + map(ts) do t + return vee!(t...) + end + return c +end diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl new file mode 100644 index 00000000..43b2c1dc --- /dev/null +++ b/src/groups/semidirect_product_group.jl @@ -0,0 +1,186 @@ + +# +# +# Semidirect product groups – model semidirect products of two Lie groups +# +""" + LeftSemidirectProductGroupOperation{O1,O2,A} <: AbstractGroupOperation + +A struct to model a semidirect Lie group product. + +Let ``($(_tex(:Cal, "N")), ⋄)`` and ``($(_tex(:Cal, "H")), ⋆)`` be two Lie groups +with group operations ``⋄`` and ``⋆``, respectively, as well as a group action +``σ: $(_tex(:Cal, "H"))×$(_tex(:Cal, "N")) → $(_tex(:Cal, "N"))``, cf [`AbstractLeftGroupActionType`](@ref). + +We use here as well use the notation ``σ_h: $(_tex(:Cal, "N")) → $(_tex(:Cal, "N"))`` +as a family of maps on ``$(_tex(:Cal, "N"))`` + +Then we define a group operation ``∘`` on the product manifold ``$(_tex(:Cal, "N"))×$(_tex(:Cal, "H"))`` by + +```math + (h_1,n_1) ∘ (h_2,n_2) := (h_1 ⋆ h_2, σ_{h_2}(n_1) ⋄ n_1). +``` + +See [HilgertNeeb:2012; Definition 9.2.22](@cite), second definition for more details. + +# Constructor + + LeftSemidirectProductGroupOperation( + op1::AbstractGroupOperation, + op2::AbstractGroupOperation, + action::AbstractGroupActionType + ) + +# Parameters + +* `op1::AbstractGroupOperation`: The group operation ``⋄`` on ``$(_tex(:Cal, "H"))`` +* `op2::AbstractGroupOperation`: The group operation ``⋆`` on ``$(_tex(:Cal, "N"))`` +* `action::AbstractGroupActionType` The group action ``σ`` of ``$(_tex(:Cal, "H"))`` on ``$(_tex(:Cal, "N"))`` + +""" +struct LeftSemidirectProductGroupOperation{ + O1<:AbstractGroupOperation,O2<:AbstractGroupOperation,A<:AbstractGroupActionType +} <: AbstractGroupOperation + op1::O1 + op2::O2 + action_type::A + function LeftSemidirectProductGroupOperation( + op1::O1, op2::O2, action::A + ) where { + O1<:AbstractGroupOperation,O2<:AbstractGroupOperation,A<:AbstractGroupActionType + } + return new{O1,O2,A}(op1, op2, action) + end +end + +""" + RightSemidirectProductGroupOperation{O1,O2,A} <: AbstractGroupOperation + +A struct to model a semidirect Lie group product. + +Let ``($(_tex(:Cal, "N")), ⋄)`` and ``($(_tex(:Cal, "H")), ⋆)`` be two Lie groups +with group operations ``⋄`` and ``⋆``, respectively, as well as a group action +``σ: $(_tex(:Cal, "H"))×$(_tex(:Cal, "N")) → $(_tex(:Cal, "N"))``, cf [`AbstractGroupActionType`](#ref). + +We use here as well use the notation ``σ_h: $(_tex(:Cal, "N")) → $(_tex(:Cal, "N"))`` +as a family of maps on ``$(_tex(:Cal, "N"))`` + +Then we define a group operation ``∘`` on the product manifold ``$(_tex(:Cal, "N"))×$(_tex(:Cal, "H"))`` by + +```math + (n_1,h_1) ∘ (n_2,h_2) := (n_1 ⋄ σ_{h_1}(n_2), h_1 ⋆ h_2) +``` + +See [HilgertNeeb:2012; Definition 9.2.22](@cite), first definition for more details. + +# Constructor + + RightSemidirectProductGroupOperation( + op1::AbstractGroupOperation, + op2::AbstractGroupOperation, + action::AbstractGroupActionType + ) + +# Parameters + +* `op1::AbstractGroupOperation`: The group operation ``⋆`` on ``$(_tex(:Cal, "N"))`` +* `op2::AbstractGroupOperation`: The group operation ``⋄`` on ``$(_tex(:Cal, "H"))`` +* `action::AbstractGroupActionType`: The group action ``σ`` of ``$(_tex(:Cal, "H"))`` on ``$(_tex(:Cal, "N"))`` + +""" +struct RightSemidirectProductGroupOperation{ + O1<:AbstractGroupOperation,O2<:AbstractGroupOperation,A<:AbstractGroupActionType +} <: AbstractGroupOperation + op1::O1 + op2::O2 + action_type::A + function RightSemidirectProductGroupOperation( + op1::O1, op2::O2, action::A + ) where { + O1<:AbstractGroupOperation,O2<:AbstractGroupOperation,A<:AbstractGroupActionType + } + return new{O1,O2,A}(op1, op2, action) + end +end + +""" + LeftSemidirectProductLieGroup( + N::LieGroup, H::LieGroup, action::AbstractGroupActionType=default_left_action(N, H) + ) + +Generate the semidirect product Lie Group ``$(_tex(:Cal, "G")) = N ⋉ H`` for an [`AbstractLeftGroupActionType`](@ref) +using the [`LeftSemidirectProductGroupOperation`](@ref) for the group operation definition +as well as [HilgertNeeb:2012; Definition 9.2.22](@cite), second definition, for more details. + +The short form `N `[`⋉`](@ref ⋉(L1::LieGroup, L2::LieGroup))` H` can be used if the +corresponding [`default_left_action`](@ref)`(N,H)` is the one you want to use. +""" +function LeftSemidirectProductLieGroup( + N::LieGroup, H::LieGroup, action::AbstractGroupActionType=default_left_action(N, H) +) + return LieGroup( + N.manifold × H.manifold, LeftSemidirectProductGroupOperation(N.op, H.op, action) + ) +end + +""" + RightSemidirectProductLieGroup( + N::LieGroup, H::LieGroup, action::AbstractGroupActionType=default_right_action(N,H) + ) + +Generate the semidirect product Lie Group ``$(_tex(:Cal, "G")) = N ⋊ H`` for an [`AbstractLeftGroupActionType`](@ref) +using the [`RightSemidirectProductGroupOperation`](@ref) for the group operation definition +as well as [HilgertNeeb:2012; Definition 9.2.22](@cite), first definition, for more details. + +The short form `N `[`⋊`](@ref ⋊(L1::LieGroup, L2::LieGroup))` H` can be used if the +corresponding [`default_right_action`](@ref)`(N,H)` is the one you want to use. +""" +function RightSemidirectProductLieGroup( + N::LieGroup, H::LieGroup, action::AbstractGroupActionType=default_right_action(N, H) +) + return LieGroup( + N.manifold × H.manifold, RightSemidirectProductGroupOperation(N.op, H.op, action) + ) +end + +""" + L1 ⋉ L2 + ⋉(L1, L2) + +For two [`LieGroups`](@ref) `L1`, `L2`, generate the [`LeftSemidirectProductLieGroup`](@ref)`(L1, L2)`, +where the corresponding [`default_left_action`](@ref)`(L1, L2)` is used. +""" +function ⋉(L1::LieGroup, L2::LieGroup) + return LeftSemidirectProductLieGroup(L1, L2, default_left_action(L1, L2)) +end + +""" + L1 ⋊ L2 + ⋊(L1, L2) + +For two [`LieGroups`](@ref) `L1`, `L2`, generate the [`RightSemidirectProductLieGroup`](@ref)`(L1, L2)`, +where the corresponding [`default_right_action`](@ref)`(L1, L2)` is used. +""" +function ⋊(L1::LieGroup, L2::LieGroup) + return RightSemidirectProductLieGroup(L1, L2, default_right_action(L1, L2)) +end +function Base.show( + io::IO, + LSDL::LieGroup{𝔽,<:LeftSemidirectProductGroupOperation,<:ManifoldsBase.ProductManifold}, +) where {𝔽} + L1 = LieGroup(LSDL.manifold[1], LSDL.op.op1) + L2 = LieGroup(LSDL.manifold[2], LSDL.op.op2) + at = LSDL.op.action_type + return print(io, "LeftSemidirectProductLieGroup($L1, $L2, $at)") +end +function Base.show( + io::IO, + RSDL::LieGroup{ + 𝔽,<:RightSemidirectProductGroupOperation,<:ManifoldsBase.ProductManifold + }, +) where {𝔽} + L1 = LieGroup(RSDL.manifold[1], RSDL.op.op1) + L2 = LieGroup(RSDL.manifold[2], RSDL.op.op2) + at = RSDL.op.action_type + return print(io, "RightSemidirectProductLieGroup($L1, $L2, $at)") +end diff --git a/src/interface.jl b/src/interface.jl index 45f79fe8..31036f64 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -30,7 +30,7 @@ function LieAlgebraOrthogonalBasis(𝔽::ManifoldsBase.AbstractNumbers=ℝ) end """ - LieGroup{𝔽, O<:AbstractGroupOperation, M<:AbstractManifold{𝔽}} + LieGroup{𝔽, O<:AbstractGroupOperation, M<:AbstractManifold{𝔽}} <: AbstractManifold{𝔽} Represent a Lie Group ``$(_math(:G))``. @@ -139,14 +139,6 @@ function adjoint!(G::LieGroup, Y, g, X) return Y end -# -# Allocation hints -function ManifoldsBase.allocate_result(G::LieGroup, f::typeof(identity_element)) - apf = ManifoldsBase.allocation_promotion_function(G, f, ()) - rs = ManifoldsBase.representation_size(G) - return ManifoldsBase.allocate_result_array(G, f, apf(Float64), rs) -end - @doc """ base_manifold(G::LieGroup) @@ -239,7 +231,7 @@ This can be done in-place of `k`. """ @doc "$(_doc_conjugate)" function conjugate(G::LieGroup, g, h) - k = ManifoldsBase.allocate_result(G, inv_right_compose, h, g) + k = ManifoldsBase.allocate_result(G, conjugate, h, g) return conjugate!(G, k, g, h) end @@ -417,7 +409,9 @@ end # Fallback to a MethodError to avoid stack overflow @doc "$(_doc_exp_id)" -function ManifoldsBase.exp!(G::LieGroup, h, e::Identity, X, t::Number=1) +function ManifoldsBase.exp!( + G::LieGroup{𝔽,Op}, h, e::Identity{Op}, X, t::Number=1 +) where {𝔽,Op<:AbstractGroupOperation} throw(MethodError(exp!, (typeof(G), typeof(h), typeof(e), typeof(X), typeof(t)))) end @@ -434,7 +428,7 @@ both signatures are equivalent. The operation can be performed in-place of `c`. By default this function requires [`identity_element`](@ref)`(G)` and calls -the corresponding [`get_coordinates`](@extref `ManifoldsBase.get_coordinates-Tuple{AbstractManifold, Any, Any, ManifoldsBase.AbstractBasis}`) function +the corresponding [`get_coordinates`](@extref ManifoldsBase :jl:function:`ManifoldsBase.get_coordinates`) function of the Riemannian manifold the Lie group is build on. The inverse operation is [`get_vector`](@ref). @@ -472,7 +466,7 @@ both signatures are equivalend. The operation can be performed in-place of a tangent vector `X`. By default this function requires [`identity_element`](@ref)`(G)` and calls -the corresponding [`get_vector`](@extref ManifoldsBase.get_vector-Tuple{AbstractManifold, Any, Any, ManifoldsBase.AbstractBasis}) function +the corresponding [`get_vector`](@extref ManifoldsBase :jl:function:`ManifoldsBase.get_vectors`) function of the Riemannian manifold the Lie group is build on. The inverse operation is [`get_coordinates`](@ref). @@ -577,6 +571,10 @@ function Base.inv(::LieGroup{𝔽,O}, e::Identity{O}) where {𝔽,O<:AbstractGro return e end +function inv!(G::LieGroup{𝔽,O}, q, ::Identity{O}) where {𝔽,O<:AbstractGroupOperation} + return identity_element!(G, q) +end + _doc_inv_left_compose = """ inv_left_compose(G::LieGroup, g, h) inv_left_compose!(G::LieGroup, k, g, h) @@ -861,5 +859,27 @@ end function ManifoldsBase.zero_vector!( G::LieGroup{𝔽,O}, X, ::Identity{O} ) where {𝔽,O<:AbstractGroupOperation} - return zero_vector!(G, X, identity_element(G)) + return zero_vector!(G.manifold, X, identity_element(G)) +end + +# +# Allocation hints - mainly pass-through, especially for power manifolds +function ManifoldsBase.allocate_result( + G::LieGroup, + f::Union{typeof(compose),typeof(inv),typeof(conjugate),typeof(exp)}, + args..., +) + return ManifoldsBase.allocate_result(G.manifold, ManifoldsBase.exp, args...) +end +function ManifoldsBase.allocate_result(G::LieGroup, f::typeof(log), args...) + return ManifoldsBase.allocate_result(G.manifold, f, args...) +end +function ManifoldsBase.allocate_result(G::LieGroup, f::typeof(zero_vector), g) + return ManifoldsBase.allocate_result(G.manifold, f, g) +end +function ManifoldsBase.allocate_result( + G::LieGroup, f::Union{typeof(rand),typeof(identity_element)} +) + # both get a type allocated like rand + return ManifoldsBase.allocate_result(G.manifold, rand) end diff --git a/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl index 8982496e..8b265553 100644 --- a/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl +++ b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl @@ -22,9 +22,12 @@ struct DummyOperation <: AbstractGroupOperation end struct DummySecondOperation <: AbstractGroupOperation end struct DummyManifold <: LieGroups.AbstractManifold{LieGroups.ℝ} end struct DummyActionType <: AbstractGroupActionType end +struct DummyLeftActionType <: AbstractLeftGroupActionType end +struct DummyRightActionType <: AbstractRightGroupActionType end const DummyLieGroup = LieGroup{LieGroups.ℝ,DummyOperation,DummyManifold} -struct DummyGroupAction <: AbstractGroupAction{DummyActionType,DummyLieGroup,DummyManifold} end - +LieGroups.switch(a::DummyActionType) = a +LieGroups.switch(::DummyLeftActionType) = DummyRightActionType() +LieGroups.switch(::DummyRightActionType) = DummyLeftActionType() # === Test single functions === # # @@ -60,7 +63,7 @@ function test_adjoint(G::LieGroup, g, X; expected=missing, test_mutating::Bool=t end """ - test_apply(A::AbstractGroupAction, g, p; expected=missing) + test_apply(A::GroupAction, g, p; expected=missing) Test `apply`. @@ -68,9 +71,7 @@ Test `apply`. * `expected=missing`: the result of the application of the group action. * `test_mutating::Bool=true`: test the mutating functions """ -function test_apply( - A::AbstractGroupAction, g, p; expected=missing, test_mutating::Bool=true -) +function test_apply(A::GroupAction, g, p; expected=missing, test_mutating::Bool=true) @testset "apply" begin q1 = apply(A, g, p) M = base_manifold(A) @@ -175,7 +176,7 @@ end # # --- D """ - test_diff_apply(A::AbstractGroupAction, g, p, X; expected=missing) + test_diff_apply(A::GroupAction, g, p, X; expected=missing) Test `diff_apply`. @@ -184,7 +185,7 @@ Test `diff_apply`. * `test_mutating::Bool=true`: test the mutating functions """ function test_diff_apply( - A::AbstractGroupAction, g, p, X; expected=missing, test_mutating::Bool=true + A::GroupAction, g, p, X; expected=missing, test_mutating::Bool=true ) @testset "diff_apply" begin Y1 = diff_apply(A, g, p, X) @@ -202,7 +203,7 @@ function test_diff_apply( end """ - test_diff_apply(A::AbstractGroupAction, g, p, X; expected=missing) + test_diff_apply(A::GroupAction, g, p, X; expected=missing) Test `diff_group_apply`. @@ -211,7 +212,7 @@ Test `diff_group_apply`. * `test_mutating::Bool=true`: test the mutating functions """ function test_diff_group_apply( - A::AbstractGroupAction, g, p, X; expected=missing, test_mutating::Bool=true + A::GroupAction, g, p, X; expected=missing, test_mutating::Bool=true ) @testset "diff_group_apply" begin Y1 = diff_group_apply(A, g, p, X) @@ -339,6 +340,32 @@ function test_copyto(G::LieGroup, g) end end +# +# +# --- D +""" + test_diff_conjugate(A::GroupAction, g, p, X; expected=missing) + +Test `diff_conjugate` +""" +function test_diff_conjugate( + G::LieGroup, g, h, X; expected=missing, test_mutating::Bool=true +) + 𝔤 = LieAlgebra(G) + @testset "diff_conjugate" begin + Y1 = diff_conjugate(G, g, h, X) + @test is_point(𝔤, Y1; error=:error) + if test_mutating + Y2 = zero_vector(𝔤) + diff_conjugate!(G, Y2, g, h, X) + @test isapprox(𝔤, Y1, Y2) + end + if !ismissing(expected) + @test isapprox(𝔤, Y1, expected) + end + return nothing + end +end # # # --- E @@ -592,11 +619,34 @@ function test_inv(G::LieGroup, g; test_mutating::Bool=true, test_identity::Bool= e2 = copy(G, g) inv!(G, e2, e) @test is_identity(G, e2) + e3 = copy(G, g) + println(e3) + inv!(G, e3, e) # materialize identity + println(e3) + @test is_identity(G, e3) end end end return nothing end + +""" + test_is_identity(G::LieGroup, g) + +Test that the `Identity` returns that `is_identity` is true and that it is a point +""" +function test_identity(G::LieGroup) + @testset "Identity" begin + e = Identity(G) + @test is_point(G, e; error=:error) + @test is_identity(G, e) + e2 = Identity(DummyOperation) + @test !is_point(G, e2) + @test_throws DomainError !is_point(G, e2; error=:error) + @test !is_identity(G, e2) + end + return nothing +end # # # --- L @@ -646,33 +696,33 @@ function test_rand( ) @testset "rand" begin g1 = rand(G) - @test is_point(G, g1) + @test is_point(G, g1; error=:error) if test_mutating g2 = copy(G, g) rand!(G, g2) - @test is_point(G, g2) + @test is_point(G, g2; error=:error) end X1 = rand(G; vector_at=g1) - @test is_vector(G, g1, X1) + @test is_vector(G, g1, X1; error=:error) if test_mutating X2 = zero_vector(LieAlgebra(G), g1) rand!(G, X2; vector_at=g1) - @test is_vector(G, g1, X2) + @test is_vector(G, g1, X2; error=:error) end if !ismissing(rng) g1 = rand(rng, G) - @test is_point(G, g1) + @test is_point(G, g1; error=:error) if test_mutating g2 = copy(G, g) rand!(rng, G, g2) - @test is_point(G, g2) + @test is_point(G, g2; error=:error) end X1 = rand(rng, G; vector_at=g1) - @test is_vector(G, g1, X1) + @test is_vector(G, g1, X1; error=:error) if test_mutating X2 = zero_vector(LieAlgebra(G), g1) rand!(rng, G, X2; vector_at=g1) - @test is_vector(G, g1, X2) + @test is_vector(G, g1, X2; error=:error) end end end @@ -690,7 +740,7 @@ For now this (only) checks that `"\$G"` yields the `repr_string`. Requires `show` (or `repr`) to be implemented. """ -function test_show(G::Union{AbstractGroupAction,LieGroup}, repr_string::AbstractString) +function test_show(G::Union{GroupAction,LieGroup}, repr_string::AbstractString) @testset "repr(G, g, h)" begin @test repr(G) == repr_string end @@ -710,8 +760,8 @@ Possible properties are * `:Functions` is a vector of all defined functions for `G` Note that if `f` is in `:Functions`, and `f!` makes sense, for example for `compose`, it is assumed that both are defined. -* `:Points` is a vector of at least three points on `G`, the first is not allowed to be the identity numerically -* `:Vectors` is a vector of at least 3 elements from the Lie algebra `𝔤` og `G` +* `:Points` is a vector of at least 2 points on `G`, the first is not allowed to be the identity numerically +* `:Vectors` is a vector of at least 2 elements from the Lie algebra `𝔤` og `G` * `:Mutating` is a boolean (`true` by default) whether to test the mutating variants of functions or not. * `:Name` is a name of the test. If not provided, defaults to `"\$G"` * `:Rng` is a random number generator, if provided, the random functions are tested with this generator as well @@ -734,9 +784,7 @@ function test_lie_group(G::LieGroup, properties::Dict, expectations::Dict=Dict() mutating = get(properties, :Mutating, true) functions = get(properties, :Functions, Function[]) points = get(properties, :Points, []) - @assert length(points) > 2 vectors = get(properties, :Vectors, []) - @assert length(vectors) > 2 test_name = get(properties, :Name, "$G") @testset "$(test_name)" begin # Call function tests based on their presence in alphabetical order @@ -766,6 +814,13 @@ function test_lie_group(G::LieGroup, properties::Dict, expectations::Dict=Dict() # # # --- D + if (diff_conjugate in functions) + v = get(expectations, :diff_conjugate, missing) + test_diff_conjugate( + G, points[1], points[2], vectors[1]; expected=v, test_mutating=mutating + ) + end + if (diff_inv in functions) v = get(expectations, :diff_inv, missing) test_diff_inv(G, points[1], vectors[1]; expected=v, test_mutating=mutating) @@ -835,7 +890,11 @@ function test_lie_group(G::LieGroup, properties::Dict, expectations::Dict=Dict() end if (inv in functions) test_inv(G, points[1]; test_mutating=mutating) - end # + end + if (is_identity in functions) + test_identity(G) + end + # # # --- L if (lie_bracket in functions) @@ -886,9 +945,7 @@ Possible `expectations` are * `:manifold` is the `AbstractManifold` the action acts upon * `:repr` is a sting one gets from `repr(G)` """ -function test_group_action( - A::AbstractGroupAction, properties::Dict, expectations::Dict=Dict() -) +function test_group_action(A::GroupAction, properties::Dict, expectations::Dict=Dict()) a_tol = get(expectations, :atol, 1e-8) mutating = get(properties, :Mutating, true) functions = get(properties, :Functions, Function[]) diff --git a/test/actions/test_action_interface.jl b/test/actions/test_action_interface.jl index dee799a4..85757c88 100644 --- a/test/actions/test_action_interface.jl +++ b/test/actions/test_action_interface.jl @@ -4,4 +4,12 @@ s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") !(s in LOAD_PATH) && (push!(LOAD_PATH, s)) using LieGroupsTestSuite -@testset "Group Action Interface" begin end +@testset "Group Action Interface" begin + M = LieGroupsTestSuite.DummyManifold() + T = LieGroupsTestSuite.DummyLeftActionType() + G = LieGroupsTestSuite.DummyLieGroup(M, LieGroupsTestSuite.DummyOperation()) + a = GroupAction(T, G, M) + @test repr(a) === "GroupAction($T, $G, $M)" + @test switch(T) === LieGroupsTestSuite.DummyRightActionType() + @test switch(a) === GroupAction(switch(T), G, M) +end diff --git a/test/actions/test_operation_action.jl b/test/actions/test_operation_action.jl index 8cacc84f..de9b9414 100644 --- a/test/actions/test_operation_action.jl +++ b/test/actions/test_operation_action.jl @@ -5,8 +5,8 @@ s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") using LieGroupsTestSuite @testset "Group Operation as a Group Action" begin - a1 = RightGroupOperation() - a2 = LeftGroupOperation() + a1 = RightGroupOperationAction() + a2 = LeftGroupOperationAction() @test switch(a1) == a2 @test switch(a2) == a1 a3 = inv(a1) diff --git a/test/groups/test_power_group.jl b/test/groups/test_power_group.jl new file mode 100644 index 00000000..88e35b2a --- /dev/null +++ b/test/groups/test_power_group.jl @@ -0,0 +1,52 @@ +using LieGroups, Test, ManifoldsBase, Random + +s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +@testset "Generic power Lie group" begin + M = LieGroupsTestSuite.DummyManifold() + op = LieGroupsTestSuite.DummyOperation() + G = LieGroup(M, op) + pG = G^2 + + properties1 = Dict(:Name => "The generic Power Manifold", :Functions => [show]) + expectations1 = Dict( + :repr => "PowerLieGroup(LieGroup(LieGroupsTestSuite.DummyManifold(), LieGroupsTestSuite.DummyOperation()), 2)", + ) + test_lie_group(pG, properties1, expectations1) + + # Explicit one to test element-wise methods + pG2 = PowerLieGroup(TranslationGroup(2), NestedPowerRepresentation(), 2) + g, h = [[1.0, 0.0], [0.0, 3.0]], [[0.0, 1.0], [2.0, 0.0]] + X, Y = [[0.0, 0.1], [0.2, 0.0]], [[0.1, 0.2], [0.0, 0.3]] + properties2 = Dict( + :Name => "The generic nested Power Manifold", + :Points => [g, h], + :Vectors => [X, Y], + :Rng => Random.MersenneTwister(), + :Functions => [ + adjoint, + compose, + conjugate, + diff_conjugate, + diff_inv, + diff_left_compose, + diff_right_compose, + exp, + hat, + inv, + inv_left_compose, + inv_right_compose, + is_identity, + lie_bracket, + log, + rand, + vee, + ], + ) + expectations2 = Dict( + :repr => "PowerLieGroup(LieGroup(LieGroupsTestSuite.DummyManifold(), LieGroupsTestSuite.DummyOperation()), 2)", + ) + test_lie_group(pG2, properties2, expectations2) +end diff --git a/test/groups/test_product_group.jl b/test/groups/test_product_group.jl new file mode 100644 index 00000000..1df26aad --- /dev/null +++ b/test/groups/test_product_group.jl @@ -0,0 +1,50 @@ +using LieGroups, Test, ManifoldsBase, Random, RecursiveArrayTools + +s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +@testset "Generic product Lie group" begin + G = TranslationGroup(2) × TranslationGroup(2) + g, h = ArrayPartition([1.0, 0.0], [0.0, 3.0]), ArrayPartition([0.0, 1.0], [2.0, 0.0]) + X, Y = ArrayPartition([0.0, 0.1], [0.2, 0.0]), ArrayPartition([0.1, 0.2], [0.0, 0.3]) + + properties = Dict( + :Name => "The Product Manifold", + :Rng => Random.MersenneTwister(), + :Points => [g, h], + :Vectors => [X, Y], + :Functions => [ + compose, + conjugate, + diff_conjugate, + diff_inv, + diff_left_compose, + diff_right_compose, + exp, + hat, + inv, + inv_left_compose, + inv_right_compose, + is_identity, + lie_bracket, + log, + rand, + show, + vee, + ], + ) + expectations = Dict( + :repr => "ProductLieGroup(Euclidean(2; field=ℝ) × Euclidean(2; field=ℝ), AdditionGroupOperation() × AdditionGroupOperation())", + ) + test_lie_group(G, properties, expectations) + + @testset "Product Operation generators" begin + op = LieGroupsTestSuite.DummyOperation() + op2 = LieGroupsTestSuite.DummySecondOperation() + O1 = op × op2 + O2 = op2 × op + @test (O1 × op) == (op × O2) + @test (O1 × O2) == (op × op2 × op2 × op) + end +end diff --git a/test/groups/test_semidirect_product_group.jl b/test/groups/test_semidirect_product_group.jl new file mode 100644 index 00000000..b27de5e6 --- /dev/null +++ b/test/groups/test_semidirect_product_group.jl @@ -0,0 +1,55 @@ +using LieGroups, Test, ManifoldsBase + +s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +@testset "Generic semidirect product Lie group" begin + M = LieGroupsTestSuite.DummyManifold() + op1 = LieGroupsTestSuite.DummyOperation() + G1 = LieGroup(M, op1) + op2 = LieGroupsTestSuite.DummySecondOperation() + G2 = LieGroup(M, op2) + + fcts = [ + # compose, + # conjugate, + # diff_conjugate, + # diff_inv, + # diff_left_compose, + # diff_right_compose, + # exp, + # hat, + # inv, + # inv_left_compose, + # inv_right_compose, + # is_identity, + # lie_bracket, + # log, + # rand, + show, + #vee, + ] + + Gl = LeftSemidirectProductLieGroup(G1, G2, LeftGroupOperationAction()) + properties = Dict( + :Name => "The Power Manifold", + # :Rng => Random.MersenneTwister(), + :Functions => fcts, + ) + expectations = Dict( + :repr => "LeftSemidirectProductLieGroup($(G1), $(G2), LeftGroupOperationAction())" + ) + test_lie_group(Gl, properties, expectations) + + Gr = RightSemidirectProductLieGroup(G1, G2, RightGroupOperationAction()) + properties = Dict( + :Name => "The Power Manifold", + # :Rng => Random.MersenneTwister(), + :Functions => fcts, + ) + expectations = Dict( + :repr => "RightSemidirectProductLieGroup($(G1), $(G2), RightGroupOperationAction())" + ) + test_lie_group(Gr, properties, expectations) +end diff --git a/test/groups/test_translation_group.jl b/test/groups/test_translation_group.jl index 9ff55e41..0451244e 100644 --- a/test/groups/test_translation_group.jl +++ b/test/groups/test_translation_group.jl @@ -56,10 +56,10 @@ begin @testset "Translation group operation action" begin # A first group action Test for t in [ - RightGroupOperation(), - LeftGroupOperation(), - InverseLeftGroupOperation(), - InverseRightGroupOperation(), + RightGroupOperationAction(), + LeftGroupOperationAction(), + InverseLeftGroupOperationAction(), + InverseRightGroupOperationAction(), ] A = GroupOperationAction(t, G) properties2[:Name] = "with $A" diff --git a/test/runtests.jl b/test/runtests.jl old mode 100644 new mode 100755 index bd027224..8615bc45 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,7 @@ +#!/usr/bin/env julia --optimize=0 +# +# + using LieGroups, Test s = joinpath(@__DIR__, "LieGroupsTestSuite.jl") @@ -22,6 +26,11 @@ end include_test("actions/test_operation_action.jl") end @testset "Lie Groups" begin + @testset "Meta Lie Groups" begin + include_test("groups/test_power_group.jl") + include_test("groups/test_product_group.jl") + include_test("groups/test_semidirect_product_group.jl") + end include_test("groups/test_general_linear_group.jl") include_test("groups/test_translation_group.jl") end