diff --git a/.github/workflows/documenter.yml b/.github/workflows/documenter.yml index e90ae42d61..eb66151e97 100644 --- a/.github/workflows/documenter.yml +++ b/.github/workflows/documenter.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: julia-actions/setup-julia@latest with: - version: 1.5 + version: 1.6 - uses: julia-actions/julia-docdeploy@v1 env: PYTHON: "" diff --git a/Project.toml b/Project.toml index 2331866c2a..1e396ad3a0 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Manifolds" uuid = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e" authors = ["Seth Axen ", "Mateusz Baran ", "Ronny Bergmann ", "Antoine Levitt "] -version = "0.5.13" +version = "0.6.0" [deps] Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" @@ -32,8 +32,8 @@ FiniteDifferences = "0.12" HybridArrays = "0.4" Kronecker = "0.4" LightGraphs = "1" -ManifoldsBase = "0.12.1" -Plots = "~1.6, =1.10.5" +ManifoldsBase = "0.12.4" +Plots = "1" RecipesBase = "1.1" Requires = "0.5, 1" SimpleWeightedGraphs = "1" @@ -62,4 +62,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92" [targets] -test = ["Test", "Colors", "DoubleFloats", "FiniteDiff", "ForwardDiff", "Gtk", "ImageIO", "ImageMagick", "OrdinaryDiffEq", "NLsolve", "Plots", "PyPlot", "Quaternions", "QuartzImageIO", "RecipesBase", "ReverseDiff", "VisualRegressionTests"] +test = ["Test", "Colors", "DoubleFloats", "FiniteDiff", "ForwardDiff", "Gtk", "ImageIO", "ImageMagick", "OrdinaryDiffEq", "NLsolve", "Plots", "PyPlot", "Quaternions", "QuartzImageIO", "RecipesBase", "ReverseDiff"] diff --git a/docs/Project.toml b/docs/Project.toml index e14a6f58aa..d4acdcbbf0 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -14,9 +14,9 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] -Documenter = "0.24, 0.25, 0.26" +Documenter = "0.24, 0.25, 0.26, 0.27" HybridArrays = "0.4" -ManifoldsBase = "0.12" -Plots = "= 1.10.5" +ManifoldsBase = "0.12.4" +Plots = "1" PyPlot = "2.9" StaticArrays = "1.0" diff --git a/docs/src/manifolds/group.md b/docs/src/manifolds/group.md index 5420d7903e..40a7c2c5df 100644 --- a/docs/src/manifolds/group.md +++ b/docs/src/manifolds/group.md @@ -17,9 +17,10 @@ Depth = 3 The following operations are available for group manifolds: -* [`identity`](@ref): get the identity of the group. +* [`Identity`](@ref): an allocation-free representation of the identity element of the group. * [`inv`](@ref): get the inverse of a given element. * [`compose`](@ref): compose two given elements of a group. +* [`identity_element`](@ref) get the identity element of the group, in the representation used by other points from the group. ### Group manifold diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 5a761cd6b9..76fd3259f0 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -13,6 +13,8 @@ import ManifoldsBase: check_point, check_point__transparent, check_vector, + copy, + copyto!, decorated_manifold, decorator_transparent_dispatch, default_decorator_dispatch, @@ -20,6 +22,7 @@ import ManifoldsBase: dual_basis, embed, embed!, + exp, exp!, exp!__intransparent, get_basis, @@ -32,6 +35,8 @@ import ManifoldsBase: get_vector!, get_vectors, gram_schmidt, + hat, + hat!, injectivity_radius, inner, inner__intransparent, @@ -58,12 +63,25 @@ import ManifoldsBase: vector_transport_direction!, vector_transport_to, vector_transport_to!, + vee, + vee!, zero_vector, zero_vector!, CotangentSpace, TangentSpace import Base: - copyto!, convert, foreach, in, isapprox, isempty, length, ndims, showerror, size + copyto!, + convert, + foreach, + identity, + in, + isapprox, + isempty, + length, + ndims, + showerror, + size, + transpose using Base.Iterators: repeated using Distributions @@ -146,8 +164,6 @@ include("riemannian_diff.jl") include("manifolds/ConnectionManifold.jl") include("manifolds/MetricManifold.jl") include("manifolds/VectorBundle.jl") - -# It's included early to ensure visibility of `Identity` include("groups/group.jl") # Features I: Which are extended on Meta Manifolds @@ -228,7 +244,7 @@ include("groups/connections.jl") include("groups/metric.jl") include("groups/group_action.jl") include("groups/group_operation_action.jl") -include("groups/array_manifold.jl") +include("groups/validation_group.jl") include("groups/product_group.jl") include("groups/semidirect_product_group.jl") @@ -468,6 +484,8 @@ export ×, get_embedding, hat, hat!, + identity_element, + identity_element!, induced_basis, incident_log, injectivity_radius, @@ -476,10 +494,11 @@ export ×, inverse_retract, inverse_retract!, isapprox, - is_group_decorator, is_decorator_transparent, - is_default_metric, is_default_decorator, + is_default_metric, + is_group_decorator, + is_identity, is_point, is_vector, isapprox, @@ -578,15 +597,17 @@ export adjoint_action, compose, compose!, direction, + exp_lie, + exp_lie!, g_manifold, - group_exp, - group_exp!, - group_log, - group_log!, + get_coordinates_lie, + get_coordinates_lie!, + get_vector_lie, + get_vector_lie!, has_biinvariant_metric, has_invariant_metric, - identity, - identity!, + identity_element, + identity_element!, inv, inv!, invariant_metric_dispatch, @@ -600,7 +621,8 @@ export adjoint_action, inverse_translate_diff!, lie_bracket, lie_bracket!, - make_identity, + log_lie, + log_lie!, optimal_alignment, optimal_alignment!, screw_matrix, diff --git a/src/groups/circle_group.jl b/src/groups/circle_group.jl index 2f82394086..932bfe6035 100644 --- a/src/groups/circle_group.jl +++ b/src/groups/circle_group.jl @@ -18,20 +18,17 @@ adjoint_action(::CircleGroup, p, X) = X adjoint_action!(::CircleGroup, Y, p, X) = copyto!(Y, X) -function compose(G::CircleGroup, p::AbstractVector, q::AbstractVector) +function _compose(G::CircleGroup, p::AbstractVector, q::AbstractVector) return map(compose, repeated(G), p, q) end -compose!(G::CircleGroup, x, p, q) = copyto!(x, compose(G, p, q)) +_compose!(G::CircleGroup, x, p, q) = copyto!(x, compose(G, p, q)) -Base.identity(::CircleGroup, p::AbstractVector) = map(one, p) -Base.identity(G::GT, e::Identity{GT}) where {GT<:CircleGroup} = e - -identity!(::CircleGroup, q::AbstractVector, p) = copyto!(q, 1) -identity!(::GT, q::AbstractVector, ::Identity{GT}) where {GT<:CircleGroup} = copyto!(q, 1) +identity_element(G::CircleGroup) = 1.0 +identity_element(::CircleGroup, p::Number) = one(p) +identity_element(::CircleGroup, p::AbstractArray) = map(i -> one(eltype(p)), p) Base.inv(G::CircleGroup, p::AbstractVector) = map(inv, repeated(G), p) -Base.inv(G::GT, e::Identity{GT}) where {GT<:CircleGroup} = e function inverse_translate( ::CircleGroup, @@ -56,12 +53,12 @@ lie_bracket!(::CircleGroup, Z, X, Y) = fill!(Z, 0) translate_diff(::GT, p, q, X, ::ActionDirection) where {GT<:CircleGroup} = map(*, p, X) function translate_diff( - ::GT, - ::Identity{GT}, + ::CircleGroup, + ::Identity{MultiplicationOperation}, q, X, ::ActionDirection, -) where {GT<:CircleGroup} +) return X end @@ -69,7 +66,7 @@ function translate_diff!(G::CircleGroup, Y, p, q, X, conv::ActionDirection) return copyto!(Y, translate_diff(G, p, q, X, conv)) end -function group_exp(G::CircleGroup, X) +function exp_lie(::CircleGroup, X) return map(X) do imθ θ = imag(imθ) sinθ, cosθ = sincos(θ) @@ -77,14 +74,15 @@ function group_exp(G::CircleGroup, X) end end -group_exp!(G::CircleGroup, q, X) = (q .= group_exp(G, X)) +exp_lie!(G::CircleGroup, q, X) = (q .= exp_lie(G, X)) -function group_log(G::CircleGroup, q) +function log_lie(::CircleGroup, q) return map(q) do z cosθ, sinθ = reim(z) θ = atan(sinθ, cosθ) return θ * im end end +log_lie(::CircleGroup, e::Identity{MultiplicationOperation}) = 0.0 * im -group_log!(G::CircleGroup, X::AbstractVector, q::AbstractVector) = (X .= group_log(G, q)) +_log_lie!(G::CircleGroup, X, q) = (X .= log_lie(G, q)) diff --git a/src/groups/connections.jl b/src/groups/connections.jl index 8fcee83d33..8d36f44756 100644 --- a/src/groups/connections.jl +++ b/src/groups/connections.jl @@ -65,7 +65,7 @@ function exp!( X, ) where {𝔽} Y = inverse_translate_diff(M.manifold, q, p, X) - return compose!(M.manifold, q, p, group_exp(M.manifold, Y)) + return compose!(M.manifold, q, p, exp_lie(M.manifold, Y)) end """ @@ -87,8 +87,8 @@ function log!( q, ) where {𝔽} pinvq = compose(M.manifold, inv(M.manifold, p), q) - group_log!(M.manifold, Y, pinvq) - return translate_diff!(M.manifold, Y, p, Identity(M.manifold, p), Y) + log_lie!(M.manifold, Y, pinvq) + return translate_diff!(M.manifold, Y, p, Identity(M.manifold), Y) end """ @@ -156,7 +156,7 @@ function vector_transport_direction!( d, ::ParallelTransport, ) - dexp_half = group_exp(M.manifold, d / 2) + dexp_half = exp_lie(M.manifold, d / 2) translate_diff!(M.manifold, Y, dexp_half, p, X, RightAction()) return translate_diff!(M.manifold, Y, dexp_half, p, Y, LeftAction()) end @@ -177,6 +177,6 @@ function vector_transport_to!( q, m::ParallelTransport, ) - d = group_log(M.manifold, q) + d = log_lie(M.manifold, q) return vector_transport_direction!(M, Y, p, X, d, m) end diff --git a/src/groups/general_linear.jl b/src/groups/general_linear.jl index 2421366668..d9b8b92906 100644 --- a/src/groups/general_linear.jl +++ b/src/groups/general_linear.jl @@ -37,9 +37,13 @@ function check_point(G::GeneralLinear, p; kwargs...) end return nothing end -check_point(::GT, ::Identity{GT}; kwargs...) where {GT<:GeneralLinear} = nothing -function check_point(G::GeneralLinear, e::Identity; kwargs...) - return DomainError(e, "The identity element $(e) does not belong to $(G).") +check_point(::GeneralLinear, ::Identity{MultiplicationOperation}) = nothing +function check_point( + G::GeneralLinear, + e::Identity{O}; + kwargs..., +) where {O<:AbstractGroupOperation} + return invoke(check_point, Tuple{AbstractGroupManifold,typeof(e)}, G, e; kwargs...) end function check_vector(G::GeneralLinear, p, X; kwargs...) @@ -146,13 +150,13 @@ function get_vector!( return copyto!(X, Xⁱ) end -function group_exp!(::GeneralLinear{1}, q, X) +function exp_lie!(::GeneralLinear{1}, q, X) q[1] = exp(X[1]) return q end -group_exp!(::GeneralLinear{2}, q, X) = copyto!(q, exp(SizedMatrix{2,2}(X))) +exp_lie!(::GeneralLinear{2}, q, X) = copyto!(q, exp(SizedMatrix{2,2}(X))) -function group_log!(::GeneralLinear{1}, X::AbstractMatrix, p::AbstractMatrix) +function _log_lie!(::GeneralLinear{1}, X, p) X[1] = log(p[1]) return X end @@ -198,7 +202,6 @@ log(::GeneralLinear, p, q) function log!(G::GeneralLinear{n,𝔽}, X, p, q) where {n,𝔽} pinvq = inverse_translate(G, p, q, LeftAction()) 𝔽 === ℝ && det(pinvq) ≤ 0 && throw(OutOfInjectivityRadiusError()) - e = Identity(G, pinvq) if isnormal(pinvq; atol=sqrt(eps(real(eltype(pinvq))))) log_safe!(X, pinvq) else @@ -207,13 +210,12 @@ function log!(G::GeneralLinear{n,𝔽}, X, p, q) where {n,𝔽} Gᵣ = GeneralLinear(real_dimension(𝔽) * n, ℝ) pinvqᵣ = realify(pinvq, 𝔽) Xᵣ = realify(X, 𝔽) - eᵣ = Identity(Gᵣ, pinvqᵣ) log_safe!(Xᵣ, _project_Un_S⁺(pinvqᵣ)) inverse_retraction = NLsolveInverseRetraction(ExponentialRetraction(), Xᵣ) - inverse_retract!(Gᵣ, Xᵣ, eᵣ, pinvqᵣ, inverse_retraction) + inverse_retract!(Gᵣ, Xᵣ, Identity(G), pinvqᵣ, inverse_retraction) unrealify!(X, Xᵣ, 𝔽, n) end - translate_diff!(G, X, p, e, X, LeftAction()) + translate_diff!(G, X, p, Identity(G), X, LeftAction()) return X end function log!(::GeneralLinear{1}, X, p, q) diff --git a/src/groups/group.jl b/src/groups/group.jl index da97201a82..15d7579f2c 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -8,12 +8,9 @@ Abstract type for smooth binary operations $∘$ on elements of a Lie group $\ma An operation can be either defined for a specific [`AbstractGroupManifold`](@ref) over number system `𝔽` or in general, by defining for an operation `Op` the following methods: - identity!(::AbstractGroupManifold{𝔽,Op}, q, q) - identity(::AbstractGroupManifold{𝔽,Op}, p) + identity_element!(::AbstractGroupManifold{𝔽,Op}, q, q) inv!(::AbstractGroupManifold{𝔽,Op}, q, p) - inv(::AbstractGroupManifold{𝔽,Op}, p) - compose(::AbstractGroupManifold{𝔽,Op}, p, q) - compose!(::AbstractGroupManifold{𝔽,Op}, x, p, q) + _compose!(::AbstractGroupManifold{𝔽,Op}, x, p, q) Note that a manifold is connected with an operation by wrapping it with a decorator, [`AbstractGroupManifold`](@ref). In typical cases the concrete wrapper @@ -50,7 +47,7 @@ struct TransparentGroupDecoratorType <: AbstractGroupDecoratorType end Abstract type for a Lie group, a group that is also a smooth manifold with an [`AbstractGroupOperation`](@ref), a smooth binary operation. `AbstractGroupManifold`s must -implement at least [`inv`](@ref), [`identity`](@ref), [`compose`](@ref), and +implement at least [`inv`](@ref), [`compose`](@ref), and [`translate_diff`](@ref). """ abstract type AbstractGroupManifold{𝔽,O<:AbstractGroupOperation,T<:AbstractDecoratorType} <: @@ -76,9 +73,6 @@ end Base.show(io::IO, G::GroupManifold) = print(io, "GroupManifold($(G.manifold), $(G.op))") -Base.copyto!(M::GroupManifold, q, p) = copyto!(M.manifold, q, p) -Base.copyto!(M::GroupManifold, Y, p, X) = copyto!(M.manifold, Y, p, X) - const GROUP_MANIFOLD_BASIS_DISAMBIGUATION = [AbstractDecoratorManifold, ValidationManifold, VectorBundle] @@ -129,6 +123,8 @@ function (::Type{T})(M::AbstractManifold) where {T<:AbstractGroupOperation} return GroupManifold(M, T()) end +manifold_dimension(G::GroupManifold) = manifold_dimension(G.manifold) + ################### # Action directions ################### @@ -168,202 +164,153 @@ switch_direction(::RightAction) = LeftAction() ################################## @doc raw""" - Identity(G::AbstractGroupManifold, p) + Identity{O<:AbstractGroupOperation} + +Represent the group identity element ``e ∈ \mathcal{G}`` on an [`AbstractGroupManifold`](@ref) `G` +with [`AbstractGroupOperation`](@ref) of type `O`. + +Similar to the philosophy that points are agnostic of their group at hand, the identity +does not store the group `g` it belongs to. However it depends on the type of the [`AbstractGroupOperation`](@ref) used. + +See also [`identity_element`](@ref) on how to obtain the corresponding [`AbstractManifoldPoint`](@ref) or array representation. + +# Constructors -The group identity element $e ∈ \mathcal{G}$ represented by point `p`. + Identity(G::AbstractGroupManifold{𝔽,O}) + Identity(o::O) + Identity(::Type{O}) + +create the identity of the corresponding subtype `O<:`[`AbstractGroupOperation`](@ref) """ -struct Identity{G<:AbstractGroupManifold,PT} - group::G - p::PT +struct Identity{O<:AbstractGroupOperation} end + +function Identity(::AbstractGroupManifold{𝔽,O}) where {𝔽,O<:AbstractGroupOperation} + return Identity{O}() end +Identity(M::AbstractDecoratorManifold) = Identity(base_group(M)) +Identity(::O) where {O<:AbstractGroupOperation} = Identity(O) +Identity(::Type{O}) where {O<:AbstractGroupOperation} = Identity{O}() + +# To ensure allocate_result_type works in general if idenitty apears in the tuple +number_eltype(::Identity) = Bool + +@doc raw""" + identity_element(G::AbstractGroupManifold) -Identity(M::AbstractDecoratorManifold, p) = Identity(decorated_manifold(M), p) -function Identity(M::AbstractManifold, p) - return error("Identity not implemented for manifold $(M) and point $(p).") +Return a point representation of the [`Identity`](@ref) on the [`AbstractGroupManifold`](@ref) `G`. +By default this representation is the default array or number representation. +It should return the corresponding [`AbstractManifoldPoint`](@ref) of points on `G` if +points are not represented by arrays. +""" +identity_element(G::AbstractGroupManifold) +@decorator_transparent_function function identity_element(G::AbstractGroupManifold) + q = allocate_result(G, identity_element) + return identity_element!(G, q) end -function Base.:(==)(e1::Identity, e2::Identity) - return e1.p == e2.p && e1.group == e2.group +@decorator_transparent_signature identity_element!(G::AbstractGroupManifold, p) + +function allocate_result(G::AbstractGroupManifold, ::typeof(identity_element)) + return zeros(representation_size(G)...) end -make_identity(M::AbstractManifold, p) = Identity(M, identity(M, p)) +@doc raw""" + identity_element(G::AbstractGroupManifold, p) -Base.show(io::IO, e::Identity) = print(io, "Identity($(e.group), $(e.p))") +Return a point representation of the [`Identity`](@ref) on the [`AbstractGroupManifold`](@ref) `G`, +where `p` indicates the type to represent the identity. +""" +identity_element(G::AbstractGroupManifold, p) +@decorator_transparent_function function identity_element(G::AbstractGroupManifold, p) + q = allocate_result(G, identity_element, p) + return identity_element!(G, q) +end -# To ensure allocate_result_type works -number_eltype(::Identity) = Bool +@doc raw""" + identity_element!(G::AbstractGroupManifold, p) -Base.copyto!(e::TE, ::TE) where {TE<:Identity} = e -Base.copyto!(p, ::TE) where {TE<:Identity} = copyto!(p, e.p) -Base.copyto!(p::AbstractArray, e::TE) where {TE<:Identity} = copyto!(p, e.p) +Return a point representation of the [`Identity`](@ref) on the [`AbstractGroupManifold`](@ref) `G` +in place of `p`. +""" +identity_element!(G::AbstractGroupManifold, p) -Base.isapprox(p, e::E; kwargs...) where {E<:Identity} = isapprox(e, p; kwargs...) -Base.isapprox(e::E, p; kwargs...) where {E<:Identity} = isapprox(e.group, e, p; kwargs...) -Base.isapprox(e::E, ::E; kwargs...) where {E<:Identity} = true +@doc raw""" + is_identity(G, q; kwargs) -function allocate_result( - M::AbstractManifold, - f::typeof(get_coordinates), - e::Identity, - X, - B::AbstractBasis, -) - T = allocate_result_type(M, f, (e.p, X)) - return allocate(e.p, T, Size(number_of_coordinates(M, B))) -end +Check whether `q` is the identity on the [`AbstractGroupManifold`](@ref) `G`, i.e. it is either +the [`Identity`](@ref)`{O}` with the corresponding [`AbstractGroupOperation`](@ref) `O`, or +(approximately) the correct point representation. +""" +is_identity(G::AbstractGroupManifold, q) -function allocate_result(M::AbstractManifold, f::typeof(get_vector), e::Identity, Xⁱ) - is_group_decorator(M) && return allocate_result(base_group(M), f, e, Xⁱ) - return error( - "allocate_result not implemented for manifold $(M), function $(f), point $(e), and vector $(Xⁱ).", - ) +@decorator_transparent_function function is_identity(G::AbstractGroupManifold, q; kwargs...) + return isapprox(G, identity_element(G), q; kwargs...) end -function allocate_result(M::AbstractGroupManifold, f::typeof(get_vector), e::Identity, Xⁱ) - return error( - "allocate_result not implemented for group manifold $(M), function $(f), $(e), and vector $(Xⁱ).", - ) -end -function allocate_result( - G::GT, - ::typeof(get_vector), - ::Identity{GT}, - Xⁱ, -) where {GT<:AbstractGroupManifold} - B = VectorBundleFibers(TangentSpace, G) - return allocate(Xⁱ, Size(representation_size(B))) +function is_identity( + ::AbstractGroupManifold{𝔽,O}, + ::Identity{O}; + kwargs..., +) where {𝔽,O<:AbstractGroupOperation} + return true end +is_identity(::AbstractGroupManifold, ::Identity; kwargs...) = false -function allocate_result( - M::AbstractDecoratorManifold, - f::typeof(get_coordinates), - e::Identity, - X, -) - is_group_decorator(M) && return allocate_result(base_group(M), f, e, X) - return error( - "allocate_result not implemented for manifold $(M), function $(f), point $(e), and vector $(X).", - ) -end -function allocate_result( - M::AbstractGroupManifold, - f::typeof(get_coordinates), - e::Identity, - X, -) - return error( - "allocate_result not implemented for group manifold $(M), function $(f), $(e), and vector $(X).", - ) -end -function allocate_result( - G::GT, - ::typeof(get_coordinates), - ::Identity{GT}, - X, -) where {GT<:AbstractGroupManifold} - return allocate(X, Size(manifold_dimension(G))) -end -function get_vector(M::AbstractGroupManifold, e::Identity, X, B::VeeOrthogonalBasis) - M != e.group && error("On $(M) the identity $(e) does not match to perform get_vector.") - return get_vector(decorated_manifold(M), e.p, X, B) -end -function get_vector(M::AbstractManifold, e::Identity, X, B::VeeOrthogonalBasis) - M != e.group.manifold && - error("On $(M) the identity $(e) does not match to perform get_vector.") - return get_vector(M, e.p, X, B) -end -for MT in GROUP_MANIFOLD_BASIS_DISAMBIGUATION - eval( - quote - @invoke_maker 1 AbstractManifold get_vector( - M::$MT, - e::Identity, - X, - B::VeeOrthogonalBasis, - ) - end, - ) +function isapprox( + G::AbstractGroupManifold{𝔽,O}, + p::Identity{O}, + q; + kwargs..., +) where {𝔽,O<:AbstractGroupOperation} + return is_identity(G, q; kwargs...) end -function get_vector!(M::AbstractGroupManifold, Y, e::Identity, X, B::VeeOrthogonalBasis) - M != e.group && error("On $(M) the identity $(e) does not match to perform get_vector!") - return get_vector!(decorated_manifold(M), Y, e.p, X, B) +function isapprox( + G::AbstractGroupManifold{𝔽,O}, + p, + q::Identity{O}; + kwargs..., +) where {𝔽,O<:AbstractGroupOperation} + return is_identity(G, p; kwargs...) end -function get_vector!(M::AbstractManifold, Y, e::Identity, X, B::VeeOrthogonalBasis) - M != e.group.manifold && - error("On $(M) the identity $(e) does not match to perform get_vector!") - return get_vector!(M, Y, e.p, X, B) +function isapprox( + G::AbstractGroupManifold{𝔽,O}, + p::Identity{O}, + q::Identity{O}; + kwargs..., +) where {𝔽,O<:AbstractGroupOperation} + return true end -for MT in GROUP_MANIFOLD_BASIS_DISAMBIGUATION - eval( - quote - @invoke_maker 1 AbstractManifold get_vector!( - M::$MT, - Y, - e::Identity, - X, - B::VeeOrthogonalBasis, - ) - end, - ) +function isapprox( + G::AbstractGroupManifold{𝔽,O}, + p::Identity{O}, + X, + Y; + kwargs..., +) where {𝔽,O<:AbstractGroupOperation} + return isapprox(G, identity_element(G), X, Y; kwargs...) end +Base.isapprox(::AbstractGroupManifold, ::Identity, ::Identity; kwargs...) = false -function get_coordinates(M::AbstractGroupManifold, e::Identity, X, B::VeeOrthogonalBasis) - M != e.group && - error("On $(M) the identity $(e) does not match to perform get_coordinates") - return get_coordinates(decorated_manifold(M), e.p, X, B) -end -function get_coordinates(M::AbstractManifold, e::Identity, X, B::VeeOrthogonalBasis) - M != e.group.manifold && - error("On $(M) the identity $(e) does not match to perform get_coordinates") - return get_coordinates(M, e.p, X, B) -end -for MT in GROUP_MANIFOLD_BASIS_DISAMBIGUATION - eval( - quote - @invoke_maker 1 AbstractManifold get_coordinates( - M::$MT, - e::Identity, - X, - B::VeeOrthogonalBasis, - ) - end, - ) +function Base.show(io::IO, ::Identity{O}) where {O<:AbstractGroupOperation} + return print(io, "Identity($O)") end -function get_coordinates!( - M::AbstractGroupManifold, - Y, - e::Identity, - X, - B::VeeOrthogonalBasis, -) - M != e.group && - error("On $(M) the identity $(e) does not match to perform get_coordinates!") - return get_coordinates!(decorated_manifold(M), Y, e.p, X, B) -end -function get_coordinates!(M::AbstractManifold, Y, e::Identity, X, B::VeeOrthogonalBasis) - M != e.group.manifold && - error("On $(M) the identity $(e) does not match to perform get_coordinates!") - return get_coordinates!(M, Y, e.p, X, B) -end -for MT in GROUP_MANIFOLD_BASIS_DISAMBIGUATION - eval( - quote - @invoke_maker 1 AbstractManifold get_coordinates!( - M::$MT, - Y, - e::Identity, - X, - B::VeeOrthogonalBasis, - ) - end, - ) +function check_point( + G::AbstractGroupManifold{𝔽,O}, + e::Identity{O}; + kwargs..., +) where {𝔽,M,O<:AbstractGroupOperation} + return nothing end -manifold_dimension(G::GroupManifold) = manifold_dimension(G.manifold) - -function check_point(G::AbstractGroupManifold, e::Identity; kwargs...) - e.group === G && return nothing - return DomainError(e, "The identity element $(e) does not belong to $(G).") +function check_point( + G::AbstractGroupManifold{𝔽,O1}, + e::Identity{O2}; + kwargs..., +) where {𝔽,M,O1<:AbstractGroupOperation,O2<:AbstractGroupOperation} + return DomainError( + e, + "The Identity $e does not lie on $G, since its the identity with respect to $O2 and not $O1.", + ) end ########################## @@ -394,15 +341,13 @@ adjoint_action(G::AbstractGroupManifold, p, X) p, Xₑ, ) - e = make_identity(G, p) - Xₚ = translate_diff(G, p, e, Xₑ, LeftAction()) + Xₚ = translate_diff(G, p, Identity(G), Xₑ, LeftAction()) Y = inverse_translate_diff(G, p, p, Xₚ, RightAction()) return Y end function adjoint_action!(G::AbstractGroupManifold, Y, p, Xₑ) - e = make_identity(G, p) - Xₚ = translate_diff(G, p, e, Xₑ, LeftAction()) + Xₚ = translate_diff(G, p, Identity(G), Xₑ, LeftAction()) inverse_translate_diff!(G, Y, p, p, Xₚ, RightAction()) return Y end @@ -411,7 +356,7 @@ end inv(G::AbstractGroupManifold, p) Inverse $p^{-1} ∈ \mathcal{G}$ of an element $p ∈ \mathcal{G}$, such that -$p \circ p^{-1} = p^{-1} \circ p = e ∈ \mathcal{G}$, where $e$ is the [`identity`](@ref) +$p \circ p^{-1} = p^{-1} \circ p = e ∈ \mathcal{G}$, where $e$ is the [`Identity`](@ref) element of $\mathcal{G}$. """ inv(::AbstractGroupManifold, ::Any...) @@ -420,62 +365,195 @@ inv(::AbstractGroupManifold, ::Any...) return inv!(G, q, p) end +function Base.inv( + ::AbstractGroupManifold{𝔽,O}, + e::Identity{O}, +) where {𝔽,O<:AbstractGroupOperation} + return e +end + @decorator_transparent_function function inv!(G::AbstractGroupManifold, q, p) return inv!(G.manifold, q, p) end +function inv!( + G::AbstractGroupManifold{𝔽,O}, + q, + ::Identity{O}, +) where {𝔽,O<:AbstractGroupOperation} + return identity_element!(G, q) +end + +function Base.copyto!( + ::AbstractGroupManifold{𝔽,O}, + e::Identity{O}, + ::Identity{O}, +) where {𝔽,O<:AbstractGroupOperation} + return e +end +function Base.copyto!( + G::AbstractGroupManifold{𝔽,O}, + p, + ::Identity{O}, +) where {𝔽,O<:AbstractGroupOperation} + return identity_element!(G, p) +end + @doc raw""" - identity(G::AbstractGroupManifold, p) + compose(G::AbstractGroupManifold, p, q) -Identity element $e ∈ \mathcal{G}$, such that for any element $p ∈ \mathcal{G}$, -$p \circ e = e \circ p = p$. -The returned element is of a similar type to `p`. +Compose elements ``p,q ∈ \mathcal{G}`` using the group operation ``p \circ q``. + +For implementing composition on a new group manifold, please overload [`_compose`](@ref) +instead so that methods with [`Identity`] arguments are not ambiguous. """ -identity(::AbstractGroupManifold, ::Any) -@decorator_transparent_function function Base.identity(G::AbstractGroupManifold, p) - y = allocate_result(G, identity, p) - return identity!(G, y, p) +compose(::AbstractGroupManifold, ::Any...) + +@decorator_transparent_function function compose( + G::AbstractGroupManifold{𝔽,Op}, + p, + q, +) where {𝔽,Op<:AbstractGroupOperation} + return _compose(G, p, q) +end +function compose( + ::AbstractGroupManifold{𝔽,Op}, + ::Identity{Op}, + p, +) where {𝔽,Op<:AbstractGroupOperation} + return p +end +function compose( + ::AbstractGroupManifold{𝔽,Op}, + p, + ::Identity{Op}, +) where {𝔽,Op<:AbstractGroupOperation} + return p +end +function compose( + ::AbstractGroupManifold{𝔽,Op}, + e::Identity{Op}, + ::Identity{Op}, +) where {𝔽,Op<:AbstractGroupOperation} + return e end -@decorator_transparent_signature identity!(G::AbstractDecoratorManifold, q, p) +function _compose(G::AbstractGroupManifold, p, q) + x = allocate_result(G, compose, p, q) + return _compose!(G, x, p, q) +end -function Base.isapprox( - G::GT, - e::Identity{GT}, - p; - kwargs..., -) where {GT<:AbstractGroupManifold} - return isapprox(G, e.p, p; kwargs...) +@decorator_transparent_signature compose!(M::AbstractDecoratorManifold, x, p, q) + +compose!(G::AbstractGroupManifold, x, q, p) = _compose!(G, x, q, p) +function compose!( + G::AbstractGroupManifold{𝔽,Op}, + q, + p, + ::Identity{Op}, +) where {𝔽,Op<:AbstractGroupOperation} + return copyto!(G, q, p) end -function Base.isapprox( - G::GT, +function compose!( + G::AbstractGroupManifold{𝔽,Op}, + q, + ::Identity{Op}, p, - e::Identity{GT}; - kwargs..., -) where {GT<:AbstractGroupManifold} - return isapprox(G, e, p; kwargs...) +) where {𝔽,Op<:AbstractGroupOperation} + return copyto!(G, q, p) end -function Base.isapprox( - ::GT, - ::E, - ::E; - kwargs..., -) where {GT<:AbstractGroupManifold,E<:Identity{GT}} - return true +function compose!( + G::AbstractGroupManifold{𝔽,Op}, + q, + ::Identity{Op}, + e::Identity{Op}, +) where {𝔽,Op<:AbstractGroupOperation} + return identity_element!(G, q) +end +function compose!( + ::AbstractGroupManifold{𝔽,Op}, + e::Identity{Op}, + ::Identity{Op}, + ::Identity{Op}, +) where {𝔽,Op<:AbstractGroupOperation} + return e end +transpose(e::Identity) = e + @doc raw""" - compose(G::AbstractGroupManifold, p, q) + hat(M::AbstractGroupManifold{𝔽,O}, ::Identity{O}, Xⁱ) where {𝔽,O<:AbstractGroupOperation} + +Given a basis $e_i$ on the tangent space at a the [`Identity`}(@ref) and tangent +component vector ``X^i``, compute the equivalent vector representation +``X=X^i e_i**, where Einstein summation notation is used: -Compose elements $p,q ∈ \mathcal{G}$ using the group operation $p \circ q$. +````math +∧ : X^i ↦ X^i e_i +```` + +For array manifolds, this converts a vector representation of the tangent +vector to an array representation. The [`vee`](@ref) map is the `hat` map's +inverse. """ -compose(::AbstractGroupManifold, ::Any...) -@decorator_transparent_function function compose(G::AbstractGroupManifold, p, q) - x = allocate_result(G, compose, p, q) - return compose!(G, x, p, q) +function hat( + G::AbstractGroupManifold{𝔽,O}, + ::Identity{O}, + X, +) where {𝔽,O<:AbstractGroupOperation} + return get_vector_lie(G, X, VeeOrthogonalBasis()) +end +function hat!( + G::AbstractGroupManifold{𝔽,O}, + Y, + ::Identity{O}, + X, +) where {𝔽,O<:AbstractGroupOperation} + return get_vector_lie!(G, Y, X, VeeOrthogonalBasis()) +end +function hat(M::AbstractManifold, e::Identity, ::Any) + return throw(ErrorException("On $M there exsists no identity $e")) +end +function hat!(M::AbstractManifold, ::Any, e::Identity, ::Any) + return throw(ErrorException("On $M there exsists no identity $e")) end -@decorator_transparent_signature compose!(M::AbstractDecoratorManifold, x, p, q) +@doc raw""" + vee(M::AbstractManifold, p, X) + +Given a basis $e_i$ on the tangent space at a point `p` and tangent +vector `X`, compute the vector components $X^i$, such that $X = X^i e_i$, where +Einstein summation notation is used: + +````math +\vee : X^i e_i ↦ X^i +```` + +For array manifolds, this converts an array representation of the tangent +vector to a vector representation. The [`hat`](@ref) map is the `vee` map's +inverse. +""" +function vee( + M::AbstractGroupManifold{𝔽,O}, + ::Identity{O}, + X, +) where {𝔽,O<:AbstractGroupOperation} + return get_coordinates_lie(M, X, VeeOrthogonalBasis()) +end +function vee!( + M::AbstractGroupManifold{𝔽,O}, + Y, + ::Identity{O}, + X, +) where {𝔽,O<:AbstractGroupOperation} + return get_coordinates_lie!(M, Y, X, VeeOrthogonalBasis()) +end +function vee(M::AbstractManifold, e::Identity, ::Any) + return throw(ErrorException("On $M there exsists no identity $e")) +end +function vee!(M::AbstractManifold, ::Any, e::Identity, ::Any) + return throw(ErrorException("On $M there exsists no identity $e")) +end """ lie_bracket(G::AbstractGroupManifold, X, Y) @@ -671,12 +749,12 @@ end end @doc raw""" - group_exp(G::AbstractGroupManifold, X) + exp_lie(G::AbstractGroupManifold, X) Compute the group exponential of the Lie algebra element `X`. It is equivalent to the exponential map defined by the [`CartanSchoutenMinus`](@ref) connection. -Given an element $X ∈ 𝔤 = T_e \mathcal{G}$, where $e$ is the [`identity`](@ref) element of +Given an element $X ∈ 𝔤 = T_e \mathcal{G}$, where $e$ is the [`Identity`](@ref) element of the group $\mathcal{G}$, and $𝔤$ is its Lie algebra, the group exponential is the map ````math @@ -699,12 +777,12 @@ following properties: [`exp`](@ref). ``` -group_exp(G::AbstractGroupManifold{𝔽,AdditionOperation}, X) where {𝔽} +exp_lie(G::AbstractGroupManifold{𝔽,AdditionOperation}, X) where {𝔽} ``` Compute $q = X$. - group_exp(G::AbstractGroupManifold{𝔽,MultiplicationOperation}, X) where {𝔽} + exp_lie(G::AbstractGroupManifold{𝔽,MultiplicationOperation}, X) where {𝔽} For `Number` and `AbstractMatrix` types of `X`, compute the usual numeric/matrix exponential, @@ -713,52 +791,64 @@ exponential, \exp X = \operatorname{Exp} X = \sum_{n=0}^∞ \frac{1}{n!} X^n. ```` """ -group_exp(::AbstractGroupManifold, ::Any...) -@decorator_transparent_function function group_exp(G::AbstractGroupManifold, X) - q = allocate_result(G, group_exp, X) - return group_exp!(G, q, X) +exp_lie(::AbstractGroupManifold, ::Any...) +@decorator_transparent_function function exp_lie(G::AbstractGroupManifold, X) + q = allocate_result(G, exp_lie, X) + return exp_lie!(G, q, X) end -@decorator_transparent_signature group_exp!(M::AbstractDecoratorManifold, q, X) +@decorator_transparent_signature exp_lie!(M::AbstractDecoratorManifold, q, X) @doc raw""" - group_log(G::AbstractGroupManifold, q) + log_lie(G::AbstractGroupManifold, q) + log_lie!(G::AbstractGroupManifold, X, q) -Compute the group logarithm of the group element `q`. It is equivalent to the +Compute the Lie group logarithm of the Lie group element `q`. It is equivalent to the logarithmic map defined by the [`CartanSchoutenMinus`](@ref) connection. Given an element $q ∈ \mathcal{G}$, compute the right inverse of the group exponential map -[`group_exp`](@ref), that is, the element $\log q = X ∈ 𝔤 = T_e \mathcal{G}$, such that +[`exp_lie`](@ref), that is, the element $\log q = X ∈ 𝔤 = T_e \mathcal{G}$, such that $q = \exp X$ !!! note In general, the group logarithm map is distinct from the Riemannian logarithm map [`log`](@ref). -``` -group_log(G::AbstractGroupManifold{𝔽,AdditionOperation}, q) where {𝔽} -``` - -Compute $X = q$. - - group_log(G::AbstractGroupManifold{𝔽,MultiplicationOperation}, q) where {𝔽} - -For `Number` and `AbstractMatrix` types of `q`, compute the usual numeric/matrix logarithm: + For matrix Llie groups this is equal to the (matrix) logarithm: ````math \log q = \operatorname{Log} q = \sum_{n=1}^∞ \frac{(-1)^{n+1}}{n} (q - e)^n, ```` -where $e$ here is the [`identity`](@ref) element, that is, $1$ for numeric $q$ or the +where $e$ here is the [`Identity`](@ref) element, that is, $1$ for numeric $q$ or the identity matrix $I_m$ for matrix $q ∈ ℝ^{m × m}$. + +Since this function handles [`Identity`](@ref) arguments, the preferred function to override +is `_log_lie!`. """ -group_log(::AbstractGroupManifold, ::Any...) -@decorator_transparent_function function group_log(G::AbstractGroupManifold, q) - X = allocate_result(G, group_log, q) - return group_log!(G, X, q) +log_lie(::AbstractGroupManifold, ::Any...) +@decorator_transparent_function function log_lie(G::AbstractGroupManifold, q) + X = allocate_result(G, log_lie, q) + return log_lie!(G, X, q) +end +function log_lie( + G::AbstractGroupManifold{𝔽,Op}, + ::Identity{Op}, +) where {𝔽,Op<:AbstractGroupOperation} + return zero_vector(G, identity_element(G)) end -@decorator_transparent_signature group_log!(M::AbstractDecoratorManifold, X, q) +@decorator_transparent_function function log_lie!(G::AbstractGroupManifold, X, q) + return _log_lie!(G, X, q) +end + +function log_lie!( + G::AbstractGroupManifold{𝔽,Op}, + X, + ::Identity{Op}, +) where {𝔽,Op<:AbstractGroupOperation} + return zero_vector!(G, X, identity_element(G)) +end ############################ # Group-specific Retractions @@ -767,7 +857,7 @@ end """ GroupExponentialRetraction{D<:ActionDirection} <: AbstractRetractionMethod -Retraction using the group exponential [`group_exp`](@ref) "translated" to any point on the +Retraction using the group exponential [`exp_lie`](@ref) "translated" to any point on the manifold. For more details, see @@ -786,7 +876,7 @@ end """ GroupLogarithmicInverseRetraction{D<:ActionDirection} <: AbstractInverseRetractionMethod -Retraction using the group logarithm [`group_log`](@ref) "translated" to any point on the +Retraction using the group logarithm [`log_lie`](@ref) "translated" to any point on the manifold. For more details, see @@ -814,7 +904,7 @@ direction(::GroupLogarithmicInverseRetraction{D}) where {D} = D() method::GroupExponentialRetraction{<:ActionDirection}, ) -Compute the retraction using the group exponential [`group_exp`](@ref) "translated" to any +Compute the retraction using the group exponential [`exp_lie`](@ref) "translated" to any point on the manifold. With a group translation ([`translate`](@ref)) $τ_p$ in a specified direction, the retraction is @@ -823,14 +913,14 @@ retraction is \operatorname{retr}_p = τ_p \circ \exp \circ (\mathrm{d}τ_p^{-1})_p, ```` -where $\exp$ is the group exponential ([`group_exp`](@ref)), and $(\mathrm{d}τ_p^{-1})_p$ is +where $\exp$ is the group exponential ([`exp_lie`](@ref)), and $(\mathrm{d}τ_p^{-1})_p$ is the action of the differential of inverse translation $τ_p^{-1}$ evaluated at $p$ (see [`inverse_translate_diff`](@ref)). """ function retract(G::AbstractGroupManifold, p, X, method::GroupExponentialRetraction) conv = direction(method) Xₑ = inverse_translate_diff(G, p, p, X, conv) - pinvq = group_exp(G, Xₑ) + pinvq = exp_lie(G, Xₑ) q = translate(G, p, pinvq, conv) return q end @@ -838,7 +928,7 @@ end function retract!(G::AbstractGroupManifold, q, p, X, method::GroupExponentialRetraction) conv = direction(method) Xₑ = inverse_translate_diff(G, p, p, X, conv) - pinvq = group_exp(G, Xₑ) + pinvq = exp_lie(G, Xₑ) return translate!(G, q, p, pinvq, conv) end @@ -850,7 +940,7 @@ end method::GroupLogarithmicInverseRetraction{<:ActionDirection}, ) -Compute the inverse retraction using the group logarithm [`group_log`](@ref) "translated" +Compute the inverse retraction using the group logarithm [`log_lie`](@ref) "translated" to any point on the manifold. With a group translation ([`translate`](@ref)) $τ_p$ in a specified direction, the retraction is @@ -859,15 +949,15 @@ retraction is \operatorname{retr}_p^{-1} = (\mathrm{d}τ_p)_e \circ \log \circ τ_p^{-1}, ```` -where $\log$ is the group logarithm ([`group_log`](@ref)), and $(\mathrm{d}τ_p)_e$ is the +where $\log$ is the group logarithm ([`log_lie`](@ref)), and $(\mathrm{d}τ_p)_e$ is the action of the differential of translation $τ_p$ evaluated at the identity element $e$ (see [`translate_diff`](@ref)). """ function inverse_retract(G::GroupManifold, p, q, method::GroupLogarithmicInverseRetraction) conv = direction(method) pinvq = inverse_translate(G, p, q, conv) - Xₑ = group_log(G, pinvq) - return translate_diff(G, p, Identity(G, p), Xₑ, conv) + Xₑ = log_lie(G, pinvq) + return translate_diff(G, p, Identity(G), Xₑ, conv) end function inverse_retract!( @@ -879,8 +969,8 @@ function inverse_retract!( ) conv = direction(method) pinvq = inverse_translate(G, p, q, conv) - Xₑ = group_log(G, pinvq) - return translate_diff!(G, X, p, Identity(G, p), Xₑ, conv) + Xₑ = log_lie(G, pinvq) + return translate_diff!(G, X, p, Identity(G), Xₑ, conv) end ################################# @@ -896,62 +986,69 @@ struct AdditionOperation <: AbstractGroupOperation end const AdditionGroup = AbstractGroupManifold{𝔽,AdditionOperation} where {𝔽} -Base.:+(e::Identity{G}) where {G<:AdditionGroup} = e -Base.:+(p::Identity{G}, ::Identity{G}) where {G<:AdditionGroup} = p -Base.:+(::Identity{G}, p) where {G<:AdditionGroup} = p -Base.:+(p, ::Identity{G}) where {G<:AdditionGroup} = p -Base.:+(e::E, ::E) where {G<:AdditionGroup,E<:Identity{G}} = e +Base.:+(e::Identity{AdditionOperation}) = e +Base.:+(e::Identity{AdditionOperation}, ::Identity{AdditionOperation}) = e +Base.:+(::Identity{AdditionOperation}, p) = p +Base.:+(p, ::Identity{AdditionOperation}) = p -Base.:-(e::Identity{G}) where {G<:AdditionGroup} = e -Base.:-(e::Identity{G}, ::Identity{G}) where {G<:AdditionGroup} = e -Base.:-(::Identity{G}, p) where {G<:AdditionGroup} = -p -Base.:-(p, ::Identity{G}) where {G<:AdditionGroup} = p -Base.:-(e::E, ::E) where {G<:AdditionGroup,E<:Identity{G}} = e +Base.:-(e::Identity{AdditionOperation}) = e +Base.:-(e::Identity{AdditionOperation}, ::Identity{AdditionOperation}) = e +Base.:-(::Identity{AdditionOperation}, p) = -p +Base.:-(p, ::Identity{AdditionOperation}) = p -Base.:*(e::Identity{G}, p) where {G<:AdditionGroup} = e -Base.:*(p, e::Identity{G}) where {G<:AdditionGroup} = e -Base.:*(e::E, ::E) where {G<:AdditionGroup,E<:Identity{G}} = e +Base.:*(e::Identity{AdditionOperation}, p) = e +Base.:*(p, e::Identity{AdditionOperation}) = e +Base.:*(e::Identity{AdditionOperation}, ::Identity{AdditionOperation}) = e adjoint_action(::AdditionGroup, p, X) = X -adjoint_action!(::AdditionGroup, Y, p, X) = copyto!(Y, X) - -Base.zero(e::Identity{G}) where {G<:AdditionGroup} = e - -Base.identity(::AdditionGroup, p) = zero(p) +adjoint_action!(G::AdditionGroup, Y, p, X) = copyto!(G, Y, p, X) -identity!(::AdditionGroup, q, p) = fill!(q, 0) +function identity_element!(::AbstractGroupManifold{𝔽,<:AdditionOperation}, p) where {𝔽} + return fill!(p, zero(eltype(p))) +end Base.inv(::AdditionGroup, p) = -p +Base.inv(::AdditionGroup, e::Identity) = e + +inv!(G::AdditionGroup, q, p) = copyto!(G, q, -p) +inv!(G::AdditionGroup, q, ::Identity{AdditionOperation}) = identity_element!(G, q) +inv!(::AdditionGroup, q::Identity{AdditionOperation}, e::Identity{AdditionOperation}) = q -inv!(::AdditionGroup, q, p) = copyto!(q, -p) +function is_identity(G::AdditionGroup, q; kwargs...) + return isapprox(G, q, zero(q); kwargs...) +end +function is_identity(G::AdditionGroup, e::Identity; kwargs...) + return invoke(is_identity, Tuple{AbstractGroupManifold,typeof(e)}, G, e; kwargs...) +end -compose(::AdditionGroup, p, q) = p + q +_compose(::AdditionGroup, p, q) = p + q -function compose!(::GT, x, p, q) where {GT<:AdditionGroup} - p isa Identity{GT} && return copyto!(x, q) - q isa Identity{GT} && return copyto!(x, p) +function _compose!(::AdditionGroup, x, p, q) x .= p .+ q return x end translate_diff(::AdditionGroup, p, q, X, ::ActionDirection) = X -translate_diff!(::AdditionGroup, Y, p, q, X, ::ActionDirection) = copyto!(Y, X) +translate_diff!(G::AdditionGroup, Y, p, q, X, ::ActionDirection) = copyto!(G, Y, p, X) inverse_translate_diff(::AdditionGroup, p, q, X, ::ActionDirection) = X -function inverse_translate_diff!(::AdditionGroup, Y, p, q, X, ::ActionDirection) - return copyto!(Y, X) +function inverse_translate_diff!(G::AdditionGroup, Y, p, q, X, ::ActionDirection) + return copyto!(G, Y, p, X) end -group_exp(::AdditionGroup, X) = X +exp_lie(::AdditionGroup, X) = X -group_exp!(::AdditionGroup, q, X) = copyto!(q, X) +exp_lie!(G::AdditionGroup, q, X) = copyto!(G, q, X) -group_log(::AdditionGroup, q) = q +log_lie(::AdditionGroup, q) = q +function log_lie(G::AdditionGroup, ::Identity{AdditionOperation}) + return zero_vector(G, identity_element(G)) +end -group_log!(::AdditionGroup, X, q) = copyto!(X, q) +_log_lie!(G::AdditionGroup, X, q) = copyto!(G, X, q) lie_bracket(::AdditionGroup, X, Y) = zero(X) @@ -970,51 +1067,93 @@ struct MultiplicationOperation <: AbstractGroupOperation end const MultiplicationGroup = AbstractGroupManifold{𝔽,MultiplicationOperation} where {𝔽} -Base.:*(e::Identity{G}) where {G<:MultiplicationGroup} = e -Base.:*(::Identity{G}, p) where {G<:MultiplicationGroup} = p -Base.:*(p, ::Identity{G}) where {G<:MultiplicationGroup} = p -Base.:*(e::E, ::E) where {G<:MultiplicationGroup,E<:Identity{G}} = e -Base.:*(::Identity{<:MultiplicationGroup}, e::Identity{<:AdditionGroup}) = e - -Base.:/(p, ::Identity{G}) where {G<:MultiplicationGroup} = p -Base.:/(::Identity{G}, p) where {G<:MultiplicationGroup} = inv(p) -Base.:/(e::E, ::E) where {G<:MultiplicationGroup,E<:Identity{G}} = e - -Base.:\(p, ::Identity{G}) where {G<:MultiplicationGroup} = inv(p) -Base.:\(::Identity{G}, p) where {G<:MultiplicationGroup} = p -Base.:\(e::E, ::E) where {G<:MultiplicationGroup,E<:Identity{G}} = e +Base.:*(e::Identity{MultiplicationOperation}) = e +Base.:*(::Identity{MultiplicationOperation}, p) = p +Base.:*(p, ::Identity{MultiplicationOperation}) = p +Base.:*(e::Identity{MultiplicationOperation}, ::Identity{MultiplicationOperation}) = e +Base.:*(::Identity{MultiplicationOperation}, e::Identity{AdditionOperation}) = e +Base.:*(e::Identity{AdditionOperation}, ::Identity{MultiplicationOperation}) = e -Base.inv(e::Identity{G}) where {G<:MultiplicationGroup} = e +Base.:/(p, ::Identity{MultiplicationOperation}) = p +Base.:/(::Identity{MultiplicationOperation}, p) = inv(p) +Base.:/(e::Identity{MultiplicationOperation}, ::Identity{MultiplicationOperation}) = e -Base.one(e::Identity{G}) where {G<:MultiplicationGroup} = e +Base.:\(p, ::Identity{MultiplicationOperation}) = inv(p) +Base.:\(::Identity{MultiplicationOperation}, p) = p +Base.:\(e::Identity{MultiplicationOperation}, ::Identity{MultiplicationOperation}) = e -Base.transpose(e::Identity{G}) where {G<:MultiplicationGroup} = e +LinearAlgebra.det(::Identity{MultiplicationOperation}) = true +LinearAlgebra.adjoint(e::Identity{MultiplicationOperation}) = e -LinearAlgebra.det(::Identity{<:MultiplicationGroup}) = 1 +function identity_element!(::MultiplicationGroup, p::AbstractMatrix) + return copyto!(p, I) +end -LinearAlgebra.mul!(q, ::Identity{G}, p) where {G<:MultiplicationGroup} = copyto!(q, p) -LinearAlgebra.mul!(q, p, ::Identity{G}) where {G<:MultiplicationGroup} = copyto!(q, p) -function LinearAlgebra.mul!(q, e::E, ::E) where {G<:MultiplicationGroup,E<:Identity{G}} - return identity!(e.group, q, e) +function identity_element!(G::MultiplicationGroup, p::AbstractArray) + if length(p) == 1 + fill!(p, one(eltype(p))) + else + throw(DimensionMismatch("Array $p cannot be set to identity element of group $G")) + end + return p end -Base.identity(::MultiplicationGroup, p) = one(p) +function is_identity(G::MultiplicationGroup, q::Number; kwargs...) + return isapprox(G, q, one(q); kwargs...) +end +function is_identity(G::MultiplicationGroup, q::AbstractVector; kwargs...) + return length(q) == 1 && isapprox(G, q[], one(q[]); kwargs...) +end +function is_identity(G::MultiplicationGroup, q::AbstractMatrix; kwargs...) + return isapprox(G, q, I; kwargs...) +end +function is_identity(G::MultiplicationGroup, e::Identity; kwargs...) + return invoke(is_identity, Tuple{AbstractGroupManifold,typeof(e)}, G, e; kwargs...) +end -function identity!(G::GT, q, p) where {GT<:MultiplicationGroup} - isa(p, Identity{GT}) || return copyto!(q, one(p)) - return error( - "identity! not implemented on $(typeof(G)) for points $(typeof(q)) and $(typeof(p))", - ) +LinearAlgebra.mul!(q, ::Identity{MultiplicationOperation}, p) = copyto!(q, p) +LinearAlgebra.mul!(q, p, ::Identity{MultiplicationOperation}) = copyto!(q, p) +function LinearAlgebra.mul!( + q::AbstractMatrix, + ::Identity{MultiplicationOperation}, + ::Identity{MultiplicationOperation}, +) + return copyto!(q, I) end -identity!(::MultiplicationGroup, q::AbstractMatrix, p) = copyto!(q, I) +function LinearAlgebra.mul!( + q, + ::Identity{MultiplicationOperation}, + ::Identity{MultiplicationOperation}, +) + return copyto!(q, one(q)) +end +function LinearAlgebra.mul!( + q::Identity{MultiplicationOperation}, + ::Identity{MultiplicationOperation}, + ::Identity{MultiplicationOperation}, +) + return q +end +Base.one(e::Identity{MultiplicationOperation}) = e Base.inv(::MultiplicationGroup, p) = inv(p) +Base.inv(::MultiplicationGroup, e::Identity{MultiplicationOperation}) = e inv!(G::MultiplicationGroup, q, p) = copyto!(q, inv(G, p)) +function inv!(G::MultiplicationGroup, q, ::Identity{MultiplicationOperation}) + return identity_element!(G, q) +end +function inv!( + ::MultiplicationGroup, + q::Identity{MultiplicationOperation}, + e::Identity{MultiplicationOperation}, +) + return q +end -compose(::MultiplicationGroup, p, q) = p * q +_compose(::MultiplicationGroup, p, q) = p * q -compose!(::MultiplicationGroup, x, p, q) = mul!_safe(x, p, q) +_compose!(::MultiplicationGroup, x, p, q) = mul!_safe(x, p, q) inverse_translate(::MultiplicationGroup, p, q, ::LeftAction) = p \ q inverse_translate(::MultiplicationGroup, p, q, ::RightAction) = q / p @@ -1023,14 +1162,14 @@ function inverse_translate!(G::MultiplicationGroup, x, p, q, conv::ActionDirecti return copyto!(x, inverse_translate(G, p, q, conv)) end -function group_exp!(G::MultiplicationGroup, q, X) +function exp_lie!(G::MultiplicationGroup, q, X) X isa Union{Number,AbstractMatrix} && return copyto!(q, exp(X)) return error( - "group_exp! not implemented on $(typeof(G)) for vector $(typeof(X)) and element $(typeof(q)).", + "exp_lie! not implemented on $(typeof(G)) for vector $(typeof(X)) and element $(typeof(q)).", ) end -group_log!(::MultiplicationGroup, X::AbstractMatrix, q::AbstractMatrix) = log_safe!(X, q) +log_lie!(::MultiplicationGroup, X::AbstractMatrix, q::AbstractMatrix) = log_safe!(X, q) lie_bracket(::MultiplicationGroup, X, Y) = mul!(X * Y, Y, X, -1, true) @@ -1040,6 +1179,32 @@ function lie_bracket!(::MultiplicationGroup, Z, X, Y) return Z end +@doc raw""" + get_vector_lie(G::AbstractGroupManifold, a, B::AbstractBasis) + +Reconstruct a tangent vector from the Lie algebra of `G` from cooordinates `a` of a basis `B`. +This is similar to calling [`get_vector`](@ref) at the `p=`[`Identity`]('ref)`(G)` +""" +function get_vector_lie(G::AbstractGroupManifold, X, B::AbstractBasis) + return get_vector(G, identity_element(G), X, B) +end +function get_vector_lie!(G::AbstractGroupManifold, Y, X, B::AbstractBasis) + return get_vector!(G, Y, identity_element(G), X, B) +end + +@doc raw""" + get_coordinates_lie(G::AbstractGroupManifold, X, B::AbstractBasis) + +Get the coordinates of an element `X` from the Lie algebra og `G` with respect to a basis `B`. +This is similar to calling [`get_coordinates`](@ref) at the `p=`[`Identity`]('ref)`G` +""" +function get_coordinates_lie(G::AbstractGroupManifold, X, B::AbstractBasis) + return get_coordinates(G, identity_element(G), X, B) +end +function get_coordinates_lie!(G::AbstractGroupManifold, a, X, B::AbstractBasis) + return get_coordinates!(G, a, identity_element(G), X, B) +end + # (a) changes / parent. for f in [ embed, @@ -1073,6 +1238,8 @@ end for f in [ check_point, check_vector, + copy, + copyto!, distance, exp, exp!, @@ -1111,12 +1278,10 @@ end for f in [ compose, compose!, - group_exp, - group_exp!, - group_log, - group_log!, - identity, - identity!, + exp_lie, + exp_lie!, + log_lie, + log_lie!, translate, translate!, translate_diff, diff --git a/src/groups/metric.jl b/src/groups/metric.jl index e26079f7b4..9304f33f46 100644 --- a/src/groups/metric.jl +++ b/src/groups/metric.jl @@ -179,7 +179,7 @@ function invariant_metric_dispatch( direction(metric(M)) === conv && return Val(true) return invoke(invariant_metric_dispatch, Tuple{MetricManifold,typeof(conv)}, M, conv) end -invariant_metric_dispatch(M::AbstractManifold, ::ActionDirection) = Val(false) +invariant_metric_dispatch(::AbstractManifold, ::ActionDirection) = Val(false) function has_invariant_metric(M::AbstractManifold, conv::ActionDirection) return _extract_val(invariant_metric_dispatch(M, conv)) @@ -191,7 +191,7 @@ function inner(M::MetricManifold{𝔽,<:AbstractManifold,<:InvariantMetric}, p, N = MetricManifold(M.manifold, imetric.metric) Xₑ = inverse_translate_diff(M, p, p, X, conv) Yₑ = inverse_translate_diff(M, p, p, Y, conv) - return inner(N, Identity(N, p), Xₑ, Yₑ) + return inner(N, Identity(N), Xₑ, Yₑ) end function default_metric_dispatch( @@ -232,7 +232,7 @@ function LinearAlgebra.norm( conv = direction(imetric) N = MetricManifold(M.manifold, imetric.metric) Xₑ = inverse_translate_diff(M, p, p, X, conv) - return norm(N, Identity(N, p), Xₑ) + return norm(N, Identity(N), Xₑ) end function Base.show(io::IO, metric::LeftInvariantMetric) diff --git a/src/groups/product_group.jl b/src/groups/product_group.jl index 77da490cb1..c7256351f2 100644 --- a/src/groups/product_group.jl +++ b/src/groups/product_group.jl @@ -27,13 +27,33 @@ function ProductGroup(manifold::ProductManifold{𝔽}) where {𝔽} return GroupManifold(manifold, op) end -function decorator_transparent_dispatch(::typeof(group_exp!), M::ProductGroup, q, X) +function decorator_transparent_dispatch(::typeof(exp_lie!), M::ProductGroup, q, X) return Val(:transparent) end -function decorator_transparent_dispatch(::typeof(group_log!), M::ProductGroup, X, q) +function decorator_transparent_dispatch(::typeof(log_lie!), M::ProductGroup, X, q) return Val(:transparent) end +function identity_element(G::ProductGroup) + M = G.manifold + return ProductRepr(map(identity_element, M.manifolds)) +end +function identity_element!(G::ProductGroup, p) + pes = submanifold_components(G, p) + M = G.manifold + map(identity_element!, M.manifolds, pes) + return p +end + +function is_identity(G::ProductGroup, p; kwargs...) + pes = submanifold_components(G, p) + M = G.manifold # Inner prodct manifold (of groups) + return all(map((M, pe) -> is_identity(M, pe; kwargs...), M.manifolds, pes)) +end +function is_identity(G::ProductGroup, e::Identity; kwargs...) + return invoke(is_identity, Tuple{AbstractGroupManifold,typeof(e)}, G, e; kwargs...) +end + function Base.show(io::IO, ::MIME"text/plain", G::ProductGroup) print( io, @@ -50,56 +70,33 @@ end submanifold(G::ProductGroup, i) = submanifold(base_manifold(G), i) function submanifold_component( - e::Identity{GT}, + G::GroupManifold{𝔽,MT,O}, + ::Identity{O}, ::Val{I}, -) where {I,MT<:ProductManifold,𝔽,GT<:GroupManifold{𝔽,MT}} - return Identity(submanifold(e.group, I), submanifold_component(e.p, I)) +) where {I,MT<:ProductManifold,𝔽,O} + M = G.manifold + # the identity on a product manifold with is a group consists of a tuple of identities + return Identity(M.manifolds[I]) end function submanifold_components( - e::Identity{GT}, -) where {MT<:ProductManifold,𝔽,GT<:GroupManifold{𝔽,MT}} - M = base_manifold(e.group) - return map(Identity, M.manifolds, submanifold_components(e.group, e.p)) -end - -Base.inv(G::ProductGroup, p) = inv(G.manifold, p) -Base.inv(::GT, e::Identity{GT}) where {GT<:ProductGroup} = e -function Base.inv(M::ProductManifold, x::ProductRepr) - return ProductRepr(map(inv, M.manifolds, submanifold_components(M, x))...) -end -function Base.inv(M::ProductManifold, p) - q = allocate_result(M, inv, p) - return inv!(M, q, p) -end - -inv!(G::ProductGroup, q, p) = inv!(G.manifold, q, p) -function inv!(M::ProductManifold, q, p) - map(inv!, M.manifolds, submanifold_components(M, q), submanifold_components(M, p)) - return q -end - -Base.identity(G::ProductGroup, p) = identity(G.manifold, p) -Base.identity(::GT, e::Identity{GT}) where {GT<:ProductGroup} = e -function Base.identity(M::ProductManifold, p::ProductRepr) - return ProductRepr(map(identity, M.manifolds, submanifold_components(M, p))...) -end -function Base.identity(M::ProductManifold, p) - q = allocate_result(M, identity, p) - return identity!(M, q, p) + G::GroupManifold{𝔽,MT,O}, + ::Identity{O}, +) where {MT<:ProductManifold,𝔽,O<:AbstractGroupOperation} + M = base_manifold(G) + return map(N -> Identity(N), M.manifolds) end -identity!(G::ProductGroup, q, p) = identity!(G.manifold, q, p) -function identity!(M::ProductManifold, q, p) - map(identity!, M.manifolds, submanifold_components(M, q), submanifold_components(M, p)) +inv!(G::ProductGroup, q, ::Identity{ProductOperation}) = identity_element!(G, q) +function inv!(G::ProductGroup, q, p) + M = G.manifold + map(inv!, M.manifolds, submanifold_components(G, q), submanifold_components(G, p)) return q end +inv!(::ProductGroup, q::Identity{ProductOperation}, ::Identity{ProductOperation}) = q -compose(G::ProductGroup, p, q) = compose(G.manifold, p, q) -compose(G::GT, ::Identity{GT}, p) where {GT<:ProductGroup} = p -compose(G::GT, p, ::Identity{GT}) where {GT<:ProductGroup} = p -compose(G::GT, e::E, ::E) where {GT<:ProductGroup,E<:Identity{GT}} = e -function compose(M::ProductManifold, p::ProductRepr, q::ProductRepr) +_compose(G::ProductGroup, p, q) = _compose(G.manifold, p, q) +function _compose(M::ProductManifold, p::ProductRepr, q::ProductRepr) return ProductRepr( map( compose, @@ -109,13 +106,13 @@ function compose(M::ProductManifold, p::ProductRepr, q::ProductRepr) )..., ) end -function compose(M::ProductManifold, p, q) +function _compose(M::ProductManifold, p, q) x = allocate_result(M, compose, p, q) - return compose!(M, x, p, q) + return _compose!(M, x, p, q) end -compose!(G::ProductGroup, x, p, q) = compose!(G.manifold, x, p, q) -function compose!(M::ProductManifold, x, p, q) +_compose!(G::ProductGroup, x, p, q) = _compose!(G.manifold, x, p, q) +function _compose!(M::ProductManifold, x, p, q) map( compose!, M.manifolds, @@ -164,150 +161,108 @@ function translate!(M::ProductManifold, x, p, q, conv::ActionDirection) end function inverse_translate(G::ProductGroup, p, q, conv::ActionDirection) - return inverse_translate(G.manifold, p, q, conv) -end -function inverse_translate( - M::ProductManifold, - p::ProductRepr, - q::ProductRepr, - conv::ActionDirection, -) + M = G.manifold return ProductRepr( map( inverse_translate, M.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), + submanifold_components(G, p), + submanifold_components(G, q), repeated(conv), )..., ) end -function inverse_translate(M::ProductManifold, p, q, conv::ActionDirection) - x = allocate_result(M, inverse_translate, p, q) - return inverse_translate!(M, x, p, q, conv) -end function inverse_translate!(G::ProductGroup, x, p, q, conv::ActionDirection) - return inverse_translate!(G.manifold, x, p, q, conv) -end -function inverse_translate!(M::ProductManifold, x, p, q, conv::ActionDirection) + M = G.manifold map( inverse_translate!, M.manifolds, - submanifold_components(M, x), - submanifold_components(M, p), - submanifold_components(M, q), + submanifold_components(G, x), + submanifold_components(G, p), + submanifold_components(G, q), repeated(conv), ) return x end function translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) - return translate_diff(G.manifold, p, q, X, conv) -end -function translate_diff( - M::ProductManifold, - p::ProductRepr, - q::ProductRepr, - X::ProductRepr, - conv::ActionDirection, -) + M = G.manifold return ProductRepr( map( translate_diff, M.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - submanifold_components(M, X), + submanifold_components(G, p), + submanifold_components(G, q), + submanifold_components(G, X), repeated(conv), )..., ) end -function translate_diff(M::ProductManifold, p, q, X, conv::ActionDirection) - Y = allocate_result(M, translate_diff, X, p, q) - return translate_diff!(M, Y, p, q, X, conv) -end function translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirection) - return translate_diff!(G.manifold, Y, p, q, X, conv) -end -function translate_diff!(M::ProductManifold, Y, p, q, X, conv::ActionDirection) + M = G.manifold map( translate_diff!, M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, q), - submanifold_components(M, X), + submanifold_components(G, Y), + submanifold_components(G, p), + submanifold_components(G, q), + submanifold_components(G, X), repeated(conv), ) return Y end function inverse_translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) - return inverse_translate_diff(G.manifold, p, q, X, conv) -end -function inverse_translate_diff( - M::ProductManifold, - p::ProductRepr, - q::ProductRepr, - X::ProductRepr, - conv::ActionDirection, -) + M = G.manifold return ProductRepr( map( inverse_translate_diff, M.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - submanifold_components(M, X), + submanifold_components(G, p), + submanifold_components(G, q), + submanifold_components(G, X), repeated(conv), )..., ) end -function inverse_translate_diff(M::ProductManifold, p, q, X, conv::ActionDirection) - Y = allocate_result(M, inverse_translate_diff, X, p, q) - return inverse_translate_diff!(M, Y, p, q, X, conv) -end function inverse_translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirection) - return inverse_translate_diff!(G.manifold, Y, p, q, X, conv) -end -function inverse_translate_diff!(M::ProductManifold, Y, p, q, X, conv::ActionDirection) + M = G.manifold map( inverse_translate_diff!, M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, q), - submanifold_components(M, X), + submanifold_components(G, Y), + submanifold_components(G, p), + submanifold_components(G, q), + submanifold_components(G, X), repeated(conv), ) return Y end -function group_exp(M::ProductManifold, X::ProductRepr) - return ProductRepr(map(group_exp, M.manifolds, submanifold_components(M, X))...) -end -function group_exp(M::ProductManifold, X) - q = allocate_result(M, group_exp, X) - return group_exp!(M, q, X) +function exp_lie(G::ProductGroup, X) + M = G.manifold + return ProductRepr(map(exp_lie, M.manifolds, submanifold_components(G, X))...) end -function group_exp!(M::ProductManifold, q, X) - map(group_exp!, M.manifolds, submanifold_components(M, q), submanifold_components(M, X)) +function exp_lie!(G::ProductGroup, q, X) + M = G.manifold + map(exp_lie!, M.manifolds, submanifold_components(G, q), submanifold_components(G, X)) return q end -function group_log(M::ProductManifold, q::ProductRepr) - return ProductRepr(map(group_log, M.manifolds, submanifold_components(M, q))...) -end -function group_log(M::ProductManifold, q) - X = allocate_result(M, group_log, q) - return group_log!(M, X, q) +# on this meta level we first pass down before we resolve identity. +function log_lie!(G::ProductGroup, X, q) + M = G.manifold + map(log_lie!, M.manifolds, submanifold_components(G, X), submanifold_components(G, q)) + return X end -function group_log!(M::ProductManifold, X, q) - map(group_log!, M.manifolds, submanifold_components(M, X), submanifold_components(M, q)) +#overwrite identity case to avoid allocating identity too early. +function log_lie!(G::ProductGroup, X, q::Identity{ProductOperation}) + M = G.manifold + map(log_lie!, M.manifolds, submanifold_components(G, X), submanifold_components(G, q)) return X end diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index fc50876b52..dd297cc1d1 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -52,7 +52,15 @@ function inverse_apply(A::RotationActionOnVector{N,F,LeftAction}, a, p) where {N end inverse_apply(A::RotationActionOnVector{N,F,RightAction}, a, p) where {N,F} = a * p -apply_diff(A::RotationActionOnVector{N,F,LeftAction}, a, p, X) where {N,F} = a * X +apply_diff(::RotationActionOnVector{N,F,LeftAction}, a, p, X) where {N,F} = a * X +function apply_diff( + ::RotationActionOnVector{N,F,LeftAction}, + ::Identity{MultiplicationOperation}, + p, + X, +) where {N,F} + return X +end function apply_diff(A::RotationActionOnVector{N,F,RightAction}, a, p, X) where {N,F} return inv(base_group(A), a) * X end diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index d874eaf873..13c8545376 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -25,10 +25,10 @@ const SemidirectProductGroup{𝔽,N,H,A} = @doc raw""" SemidirectProductGroup(N::GroupManifold, H::GroupManifold, A::AbstractGroupAction) -A group that is the semidirect product of a normal group $\mathcal{N}$ and a subgroup -$\mathcal{H}$, written $\mathcal{G} = \mathcal{N} ⋊_θ \mathcal{H}$, where -$θ: \mathcal{H} × \mathcal{N} → \mathcal{N}$ is an automorphism action of $\mathcal{H}$ on -$\mathcal{N}$. The group $\mathcal{G}$ has the composition rule +A group that is the semidirect product of a normal group ``\mathcal{N}`` and a subgroup +``\mathcal{H}``, written ``\mathcal{G} = \mathcal{N} ⋊_θ \mathcal{H}``, where +``θ: \mathcal{H} × \mathcal{N} → \mathcal{N}`` is an automorphism action of ``\mathcal{H}`` on +``\mathcal{N}``. The group ``\mathcal{G}`` has the composition rule ````math g \circ g' = (n, h) \circ (n', h') = (n \circ θ_h(n'), h \circ h') @@ -52,6 +52,35 @@ function SemidirectProductGroup( return GroupManifold(M, op) end +function allocate_result(G::SemidirectProductGroup, ::typeof(identity_element)) + M = base_manifold(G) + N, H = M.manifolds + np = allocate_result(N, identity_element) + hp = allocate_result(H, identity_element) + reshaper = ShapeSpecification(StaticReshaper(), M.manifolds...) + return prod_point(reshaper, np, hp) +end + +function identity_element!(G::SemidirectProductGroup, q) + M = base_manifold(G) + N, H = M.manifolds + nq, hq = submanifold_components(G, q) + identity_element!(N, nq) + identity_element!(H, hq) + @inbounds _padpoint!(G, q) + return q +end + +function is_identity(G::SemidirectProductGroup, p; kwargs...) + M = base_manifold(G) + N, H = M.manifolds + nq, hq = submanifold_components(G, p) + return is_identity(N, nq; kwargs...) && is_identity(H, hq; kwargs...) +end +function is_identity(G::SemidirectProductGroup, e::Identity; kwargs...) + return invoke(is_identity, Tuple{AbstractGroupManifold,typeof(e)}, G, e; kwargs...) +end + function Base.show(io::IO, G::SemidirectProductGroup) M = base_manifold(G) N, H = M.manifolds @@ -61,11 +90,9 @@ end submanifold(G::SemidirectProductGroup, i) = submanifold(base_manifold(G), i) -_padpoint!(G::SemidirectProductGroup, q) = q - -_padvector!(G::SemidirectProductGroup, X) = X +_padpoint!(::SemidirectProductGroup, q) = q -Base.inv(G::GT, e::Identity{GT}) where {GT<:SemidirectProductGroup} = e +_padvector!(::SemidirectProductGroup, X) = X function inv!(G::SemidirectProductGroup, q, p) M = base_manifold(G) @@ -79,27 +106,18 @@ function inv!(G::SemidirectProductGroup, q, p) @inbounds _padpoint!(G, q) return q end -inv!(G::AG, p, e::Identity{AG}) where {AG<:SemidirectProductGroup} = identity!(G, p, e) - -Base.identity(G::GT, e::Identity{GT}) where {GT<:SemidirectProductGroup} = e - -function identity!(G::SemidirectProductGroup, q, p) - M = base_manifold(G) - N, H = M.manifolds - np, hp = submanifold_components(G, p) - nq, hq = submanifold_components(G, q) - identity!(N, nq, np) - identity!(H, hq, hp) - @inbounds _padpoint!(G, q) +function inv!(G::SemidirectProductGroup, q, ::Identity{<:SemidirectProductOperation}) + return identity_element!(G, q) +end +function inv!( + G::SemidirectProductGroup, + q::Identity{<:SemidirectProductOperation}, + ::Identity{<:SemidirectProductOperation}, +) return q end -identity!(G::GT, e::E, ::E) where {GT<:SemidirectProductGroup,E<:Identity{GT}} = e -compose(G::GT, p, e::Identity{GT}) where {GT<:SemidirectProductGroup} = p -compose(G::GT, e::Identity{GT}, p) where {GT<:SemidirectProductGroup} = p -compose(G::GT, e::E, ::E) where {GT<:SemidirectProductGroup,E<:Identity{GT}} = e - -function compose!(G::SemidirectProductGroup, x, p, q) +function _compose!(G::SemidirectProductGroup, x, p, q) M = base_manifold(G) N, H = M.manifolds A = G.op.action @@ -112,11 +130,6 @@ function compose!(G::SemidirectProductGroup, x, p, q) @inbounds _padpoint!(G, x) return x end -compose!(G::GT, x, ::Identity{GT}, q) where {GT<:SemidirectProductGroup} = copyto!(x, q) -compose!(G::GT, x, p, ::Identity{GT}) where {GT<:SemidirectProductGroup} = copyto!(x, p) -function compose!(G::GT, x, e::E, ::E) where {GT<:SemidirectProductGroup,E<:Identity{GT}} - return identity!(G, x, e) -end @doc raw""" translate_diff(G::SemidirectProductGroup, p, q, X, conX::LeftAction) @@ -166,17 +179,6 @@ function get_vector!(G::SemidirectProductGroup, Y, p, X, B::VeeOrthogonalBasis) @inbounds _padvector!(G, Y) return Y end -eval( - quote - @invoke_maker 1 AbstractManifold get_vector!( - M::SemidirectProductGroup, - Xⁱ, - e::Identity, - X, - B::VeeOrthogonalBasis, - ) - end, -) function get_coordinates!(G::SemidirectProductGroup, Y, p, X, B::VeeOrthogonalBasis) M = base_manifold(G) @@ -190,17 +192,6 @@ function get_coordinates!(G::SemidirectProductGroup, Y, p, X, B::VeeOrthogonalBa get_coordinates!(H, view(Y, (dimN + 1):(dimN + dimH)), hp, hY, B) return Y end -eval( - quote - @invoke_maker 1 AbstractManifold get_coordinates!( - M::SemidirectProductGroup, - Y, - e::Identity, - X, - B::VeeOrthogonalBasis, - ) - end, -) function zero_vector(G::SemidirectProductGroup, p) X = allocate_result(G, zero_vector, p) @@ -218,14 +209,26 @@ function zero_vector!(G::SemidirectProductGroup, X, p) return X end -function Base.isapprox(G::SemidirectProductGroup, p, q; kwargs...) +function Base.isapprox( + G::SemidirectProductGroup, + p::AbstractArray, + q::AbstractArray; + kwargs..., +) M = base_manifold(G) N, H = M.manifolds np, hp = submanifold_components(G, p) nq, hq = submanifold_components(G, q) return isapprox(N, np, nq; kwargs...) && isapprox(H, hp, hq; kwargs...) end -function Base.isapprox(G::SemidirectProductGroup, p, X, Y; kwargs...) + +function Base.isapprox( + G::SemidirectProductGroup, + p, + X::AbstractMatrix, + Y::AbstractMatrix; + kwargs..., +) M = base_manifold(G) N, H = M.manifolds np, hp = submanifold_components(G, p) @@ -233,27 +236,12 @@ function Base.isapprox(G::SemidirectProductGroup, p, X, Y; kwargs...) nY, hY = submanifold_components(G, Y) return isapprox(N, np, nX, nY; kwargs...) && isapprox(H, hp, hX, hY; kwargs...) end -function Base.isapprox( - G::GT, - p, - e::Identity{GT}; - kwargs..., -) where {GT<:SemidirectProductGroup} - return isapprox(G, e, p; kwargs...) -end -function Base.isapprox( - G::GT, - e::Identity{GT}, - p; - kwargs..., -) where {GT<:SemidirectProductGroup} - return isapprox(G, identity(G, p), p; kwargs...) -end -function Base.isapprox( - ::GT, - ::E, - ::E; +function isapprox( + G::SemidirectProductGroup{𝔽,N,H,A}, + ::Identity{SemidirectProductOperation{A}}, + X::AbstractMatrix, + Y::AbstractMatrix; kwargs..., -) where {GT<:SemidirectProductGroup,E<:Identity{GT}} - return true +) where {𝔽,N<:AbstractManifold,H<:AbstractManifold,A<:AbstractGroupAction} + return isapprox(G, identity_element(G), X, Y; kwargs...) end diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 6e0bc11f4a..e31f37f920 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -39,6 +39,12 @@ function SpecialEuclidean(n) return SemidirectProductGroup(Tn, SOn, A) end +const SpecialEuclideanIdentity{N} = Identity{ + SemidirectProductOperation{ + RotationAction{TranslationGroup{Tuple{N},ℝ},SpecialOrthogonal{N},LeftAction}, + }, +} + Base.show(io::IO, ::SpecialEuclidean{n}) where {n} = print(io, "SpecialEuclidean($(n))") Base.@propagate_inbounds function submanifold_component( @@ -135,7 +141,7 @@ function affine_matrix(G::SpecialEuclidean{n}, p) where {n} return pmat end affine_matrix(::SpecialEuclidean{n}, p::AbstractMatrix) where {n} = p -function affine_matrix(::GT, ::Identity{GT}) where {n,GT<:SpecialEuclidean{n}} +function affine_matrix(::SpecialEuclidean{n}, ::SpecialEuclideanIdentity{n}) where {n} s = maybesize(Size(n, n)) s isa Size && return SDiagonal{n,Float64}(I) return Diagonal{Float64}(I, n) @@ -234,9 +240,9 @@ function allocate_result(::SpecialEuclidean{n}, ::typeof(screw_matrix), X...) wh return allocate(X[1], Size(n + 1, n + 1)) end -compose(::SpecialEuclidean, p::AbstractMatrix, q::AbstractMatrix) = p * q +_compose(::SpecialEuclidean, p::AbstractMatrix, q::AbstractMatrix) = p * q -function compose!( +function _compose!( ::SpecialEuclidean, x::AbstractMatrix, p::AbstractMatrix, @@ -246,7 +252,7 @@ function compose!( end @doc raw""" - group_exp(G::SpecialEuclidean{n}, X) + exp_lie(G::SpecialEuclidean{n}, X) Compute the group exponential of $X = (b, Ω) ∈ 𝔰𝔢(n)$, where $b ∈ 𝔱(n)$ and $Ω ∈ 𝔰𝔬(n)$: @@ -257,12 +263,12 @@ Compute the group exponential of $X = (b, Ω) ∈ 𝔰𝔢(n)$, where $b ∈ where $t ∈ \mathrm{T}(n)$ and $R = \exp Ω$ is the group exponential on $\mathrm{SO}(n)$. In the [`screw_matrix`](@ref) representation, the group exponential is the matrix -exponential (see [`group_exp`](@ref)). +exponential (see [`exp_lie`](@ref)). """ -group_exp(::SpecialEuclidean, ::Any) +exp_lie(::SpecialEuclidean, ::Any) @doc raw""" - group_exp(G::SpecialEuclidean{2}, X) + exp_lie(G::SpecialEuclidean{2}, X) Compute the group exponential of $X = (b, Ω) ∈ 𝔰𝔢(2)$, where $b ∈ 𝔱(2)$ and $Ω ∈ 𝔰𝔬(2)$: @@ -279,10 +285,10 @@ U(θ) = \frac{\sin θ}{θ} I_2 + \frac{1 - \cos θ}{θ^2} Ω, and $θ = \frac{1}{\sqrt{2}} \lVert Ω \rVert_e$ (see [`norm`](@ref norm(M::Rotations, p, X))) is the angle of the rotation. """ -group_exp(::SpecialEuclidean{2}, ::Any) +exp_lie(::SpecialEuclidean{2}, ::Any) @doc raw""" - group_exp(G::SpecialEuclidean{3}, X) + exp_lie(G::SpecialEuclidean{3}, X) Compute the group exponential of $X = (b, Ω) ∈ 𝔰𝔢(3)$, where $b ∈ 𝔱(3)$ and $Ω ∈ 𝔰𝔬(3)$: @@ -299,16 +305,16 @@ U(θ) = I_3 + \frac{1 - \cos θ}{θ^2} Ω + \frac{θ - \sin θ}{θ^3} Ω^2, and $θ = \frac{1}{\sqrt{2}} \lVert Ω \rVert_e$ (see [`norm`](@ref norm(M::Rotations, p, X))) is the angle of the rotation. """ -group_exp(::SpecialEuclidean{3}, ::Any) +exp_lie(::SpecialEuclidean{3}, ::Any) -function group_exp!(G::SpecialEuclidean, q, X) +function exp_lie!(G::SpecialEuclidean, q, X) Xmat = screw_matrix(G, X) qmat = exp(Xmat) map(copyto!, submanifold_components(G, q), submanifold_components(G, qmat)) _padpoint!(G, q) return q end -function group_exp!(G::SpecialEuclidean{2}, q, X) +function exp_lie!(G::SpecialEuclidean{2}, q, X) SO2 = submanifold(G, 2) b, Ω = submanifold_components(G, X) t, R = submanifold_components(G, q) @@ -316,7 +322,7 @@ function group_exp!(G::SpecialEuclidean{2}, q, X) @assert size(t) == (2,) @assert size(b) == (2,) - θ = vee(SO2, Identity(SO2, Ω), Ω)[1] + θ = vee(SO2, identity_element(SO2, R), Ω)[1] sinθ, cosθ = sincos(θ) if θ ≈ 0 α = 1 - θ^2 / 6 @@ -337,14 +343,14 @@ function group_exp!(G::SpecialEuclidean{2}, q, X) end return q end -function group_exp!(G::SpecialEuclidean{3}, q, X) +function exp_lie!(G::SpecialEuclidean{3}, q, X) SO3 = submanifold(G, 2) b, Ω = submanifold_components(G, X) t, R = submanifold_components(G, q) @assert size(R) == (3, 3) @assert size(t) == (3,) - θ = norm(SO3, Identity(SO3, Ω), Ω) / sqrt(2) + θ = norm(SO3, Identity(SO3), Ω) / sqrt(2) θ² = θ^2 if θ ≈ 0 α = 1 - θ² / 6 @@ -366,7 +372,7 @@ function group_exp!(G::SpecialEuclidean{3}, q, X) end @doc raw""" - group_log(G::SpecialEuclidean{n}, p) where {n} + log_lie(G::SpecialEuclidean{n}, p) where {n} Compute the group logarithm of $p = (t, R) ∈ \mathrm{SE}(n)$, where $t ∈ \mathrm{T}(n)$ and $R ∈ \mathrm{SO}(n)$: @@ -378,12 +384,12 @@ and $R ∈ \mathrm{SO}(n)$: where $b ∈ 𝔱(n)$ and $Ω = \log R ∈ 𝔰𝔬(n)$ is the group logarithm on $\mathrm{SO}(n)$. In the [`affine_matrix`](@ref) representation, the group logarithm is the matrix logarithm -(see [`group_log`](@ref)): +(see [`log_lie`](@ref)): """ -group_log(::SpecialEuclidean, ::Any) +log_lie(::SpecialEuclidean, ::Any) @doc raw""" - group_log(G::SpecialEuclidean{2}, p) + log_lie(G::SpecialEuclidean{2}, p) Compute the group logarithm of $p = (t, R) ∈ \mathrm{SE}(2)$, where $t ∈ \mathrm{T}(2)$ and $R ∈ \mathrm{SO}(2)$: @@ -401,10 +407,10 @@ U(θ) = \frac{\sin θ}{θ} I_2 + \frac{1 - \cos θ}{θ^2} Ω, and $θ = \frac{1}{\sqrt{2}} \lVert Ω \rVert_e$ (see [`norm`](@ref norm(M::Rotations, p, X))) is the angle of the rotation. """ -group_log(::SpecialEuclidean{2}, ::Any) +log_lie(::SpecialEuclidean{2}, ::Any) @doc raw""" - group_log(G::SpecialEuclidean{3}, p) + log_lie(G::SpecialEuclidean{3}, p) Compute the group logarithm of $p = (t, R) ∈ \mathrm{SE}(3)$, where $t ∈ \mathrm{T}(3)$ and $R ∈ \mathrm{SO}(3)$: @@ -422,22 +428,22 @@ U(θ) = I_3 + \frac{1 - \cos θ}{θ^2} Ω + \frac{θ - \sin θ}{θ^3} Ω^2, and $θ = \frac{1}{\sqrt{2}} \lVert Ω \rVert_e$ (see [`norm`](@ref norm(M::Rotations, p, X))) is the angle of the rotation. """ -group_log(::SpecialEuclidean{3}, ::Any) +log_lie(::SpecialEuclidean{3}, ::Any) -function group_log!(G::SpecialEuclidean, X, q) +function _log_lie!(G::SpecialEuclidean, X, q) qmat = affine_matrix(G, q) Xmat = real(log_safe(qmat)) map(copyto!, submanifold_components(G, X), submanifold_components(G, Xmat)) _padvector!(G, X) return X end -function group_log!(G::SpecialEuclidean{2}, X, q) +function _log_lie!(G::SpecialEuclidean{2}, X, q) SO2 = submanifold(G, 2) b, Ω = submanifold_components(G, X) t, R = submanifold_components(G, q) @assert size(b) == (2,) - group_log!(SO2, Ω, R) + log_lie!(SO2, Ω, R) @inbounds θ = Ω[2] β = θ / 2 α = θ ≈ 0 ? 1 - β^2 / 3 : β * cot(β) @@ -449,7 +455,7 @@ function group_log!(G::SpecialEuclidean{2}, X, q) end return X end -function group_log!(G::SpecialEuclidean{3}, X, q) +function _log_lie!(G::SpecialEuclidean{3}, X, q) b, Ω = submanifold_components(G, X) t, R = submanifold_components(G, q) @assert size(Ω) == (3, 3) @@ -527,24 +533,6 @@ function translate_diff!(G::SpecialEuclidean, Y, p, q, X, ::RightAction) @inbounds _padvector!(G, Y) return Y end -function translate_diff!(G::SpecialEuclidean, Y, ::Identity, q, X, ::RightAction) - copyto!(G, Y, X) - @inbounds _padvector!(G, Y) - return Y -end -function translate_diff!(G::SpecialEuclidean, Y, p, ::Identity, X, ::RightAction) - np, hp = submanifold_components(G, p) - nX, hX = submanifold_components(G, X) - nY, hY = submanifold_components(G, Y) - hY .= hp' * hX * hp - copyto!(nY, hX * np + nX) - @inbounds _padvector!(G, Y) -end -function translate_diff!(G::SpecialEuclidean, Y, ::Identity, ::Identity, X, ::RightAction) - copyto!(G, Y, X) - @inbounds _padvector!(G, Y) - return Y -end @doc raw""" SpecialEuclideanInGeneralLinear diff --git a/src/groups/special_linear.jl b/src/groups/special_linear.jl index d2a2541f74..2e8b17646c 100644 --- a/src/groups/special_linear.jl +++ b/src/groups/special_linear.jl @@ -38,9 +38,13 @@ function check_point(G::SpecialLinear{n,𝔽}, p; kwargs...) where {n,𝔽} end return nothing end -check_point(::GT, ::Identity{GT}; kwargs...) where {GT<:SpecialLinear} = nothing -function check_point(G::SpecialLinear, e::Identity; kwargs...) - return DomainError(e, "The identity element $(e) does not belong to $(G).") +check_point(G::SpecialLinear, ::Identity{MultiplicationOperation}; kwargs...) = nothing +function check_point( + G::SpecialLinear, + e::Identity{O}; + kwargs..., +) where {O<:AbstractGroupOperation} + return invoke(check_point, Tuple{AbstractGroupManifold,typeof(e)}, G, e; kwargs...) end function check_vector(G::SpecialLinear, p, X; kwargs...) diff --git a/src/groups/special_orthogonal.jl b/src/groups/special_orthogonal.jl index bcac52c625..2987f461c8 100644 --- a/src/groups/special_orthogonal.jl +++ b/src/groups/special_orthogonal.jl @@ -22,6 +22,7 @@ SpecialOrthogonal(n) = SpecialOrthogonal{n}(Rotations(n), MultiplicationOperatio Base.show(io::IO, ::SpecialOrthogonal{n}) where {n} = print(io, "SpecialOrthogonal($(n))") Base.inv(::SpecialOrthogonal, p) = transpose(p) +Base.inv(::SpecialOrthogonal, e::Identity{MultiplicationOperation}) = e inverse_translate(G::SpecialOrthogonal, p, q, ::LeftAction) = inv(G, p) * q inverse_translate(G::SpecialOrthogonal, p, q, ::RightAction) = q * inv(G, p) @@ -41,17 +42,10 @@ function inverse_translate_diff!(G::SpecialOrthogonal, Y, p, q, X, conv::ActionD return copyto!(Y, inverse_translate_diff(G, p, q, X, conv)) end -group_exp!(G::SpecialOrthogonal, q, X) = exp!(G, q, make_identity(G, q).p, X) - -group_log!(G::SpecialOrthogonal, X, q) = log!(G, X, make_identity(G, q).p, q) -function group_log!(G::SpecialOrthogonal, X::AbstractMatrix, q::AbstractMatrix) - return log!(G, X, make_identity(G, q).p, q) -end - function allocate_result( ::GT, ::typeof(exp), - ::Identity{GT}, + ::Identity, X, ) where {n,GT<:SpecialOrthogonal{n}} return allocate(X) @@ -59,7 +53,7 @@ end function allocate_result( ::GT, ::typeof(log), - ::Identity{GT}, + ::Identity, q, ) where {n,GT<:SpecialOrthogonal{n}} return allocate(q) diff --git a/src/groups/translation_action.jl b/src/groups/translation_action.jl index e410f1a2d6..970e81461e 100644 --- a/src/groups/translation_action.jl +++ b/src/groups/translation_action.jl @@ -36,20 +36,20 @@ function switch_direction(A::TranslationAction{TM,TRN,TAD}) where {TM,TRN,TAD} return TranslationAction(A.manifold, A.Rn, switch_direction(TAD())) end -apply(A::TranslationAction, a, p) = p + a +apply(::TranslationAction, a, p) = p + a -apply!(A::TranslationAction{M,G}, q, a, p) where {M,G} = (q .= p .+ a) -apply!(A::TranslationAction{M,G}, q, e::Identity{G}, p) where {M,G} = copyto!(q, p) +apply!(::TranslationAction, q, a, p) = (q .= p .+ a) +apply!(::TranslationAction, q, e::Identity{AdditionOperation}, p) = copyto!(q, p) -inverse_apply(A::TranslationAction, a, p) = p - a +inverse_apply(::TranslationAction, a, p) = p - a -inverse_apply!(A::TranslationAction{M,G}, q, a, p) where {M,G} = (q .= p .- a) -inverse_apply!(A::TranslationAction{M,G}, q, e::Identity{G}, p) where {M,G} = copyto!(q, p) +inverse_apply!(::TranslationAction, q, a, p) = (q .= p .- a) +inverse_apply!(::TranslationAction, q, e::Identity{AdditionOperation}, p) = copyto!(q, p) -apply_diff(A::TranslationAction, a, p, X) = X +apply_diff(::TranslationAction, a, p, X) = X -apply_diff!(A::TranslationAction, Y, a, p, X) = copyto!(Y, X) +apply_diff!(::TranslationAction, Y, a, p, X) = copyto!(Y, X) -inverse_apply_diff(A::TranslationAction, a, p, X) = X +inverse_apply_diff(::TranslationAction, a, p, X) = X -inverse_apply_diff!(A::TranslationAction, Y, a, p, X) = copyto!(Y, X) +inverse_apply_diff!(::TranslationAction, Y, a, p, X) = copyto!(Y, X) diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index 6bf7f03e0d..bc60a2612d 100644 --- a/src/groups/translation_group.jl +++ b/src/groups/translation_group.jl @@ -18,6 +18,8 @@ function TranslationGroup(n::Int...; field::AbstractNumbers=ℝ) ) end +identity_element!(::TranslationGroup, p) = fill!(p, 0) + invariant_metric_dispatch(::TranslationGroup, ::ActionDirection) = Val(true) function default_metric_dispatch( diff --git a/src/groups/array_manifold.jl b/src/groups/validation_group.jl similarity index 74% rename from src/groups/array_manifold.jl rename to src/groups/validation_group.jl index 2f9c2411b4..025c77a25c 100644 --- a/src/groups/array_manifold.jl +++ b/src/groups/validation_group.jl @@ -1,57 +1,50 @@ -array_value(e::Identity) = Identity(e.group, array_value(e.p)) +# +# Interaction of GrooupManifold with a ValidationaManifold +# +array_value(e::Identity) = e array_point(p) = ValidationMPoint(p) array_point(p::ValidationMPoint) = p -array_point(e::Identity) = Identity(e.group, array_point(e.p)) -function adjoint_action(M::ValidationManifold, p, X; kwargs...) +const ValidationGroup{𝔽} = ValidationManifold{𝔽,G} where {G<:AbstractGroupManifold} + +function adjoint_action(M::ValidationGroup, p, X; kwargs...) is_point(M, p, true; kwargs...) - eM = make_identity(M.manifold, array_value(p)) + eM = Identity(M.manifold) is_vector(M, eM, X, true; kwargs...) Y = ValidationTVector(adjoint_action(M.manifold, array_value(p), array_value(X))) is_vector(M, eM, Y, true; kwargs...) return Y end -function adjoint_action!(M::ValidationManifold, Y, p, X; kwargs...) +function adjoint_action!(M::ValidationGroup, Y, p, X; kwargs...) is_point(M, p, true; kwargs...) - eM = make_identity(M.manifold, array_value(p)) + eM = Identity(M.manifold) is_vector(M, eM, X, true; kwargs...) adjoint_action!(M.manifold, array_value(Y), array_value(p), array_value(X)) is_vector(M, eM, Y, true; kwargs...) return Y end -function Base.inv(M::ValidationManifold, p; kwargs...) +Identity(M::ValidationGroup) = array_point(Identity(M.manifold)) +identity_element!(M::ValidationGroup, p) = identity_element!(M.manifold, array_value(p)) + +function Base.inv(M::ValidationGroup, p; kwargs...) is_point(M, p, true; kwargs...) q = array_point(inv(M.manifold, array_value(p))) is_point(M, q, true; kwargs...) return q end -function inv!(M::ValidationManifold, q, p; kwargs...) +function inv!(M::ValidationGroup, q, p; kwargs...) is_point(M, p, true; kwargs...) inv!(M.manifold, array_value(q), array_value(p)) is_point(M, q, true; kwargs...) return q end -function Base.identity(M::ValidationManifold, p; kwargs...) - is_point(M, p, true; kwargs...) - q = array_point(identity(M.manifold, array_value(p))) - is_point(M, q, true; kwargs...) - return q -end - -function identity!(M::ValidationManifold, q, p; kwargs...) - is_point(M, p, true; kwargs...) - identity!(M.manifold, array_value(q), array_value(p)) - is_point(M, q, true; kwargs...) - return q -end - -function lie_bracket(M::ValidationManifold, X, Y) - eM = make_identity(M.manifold, array_value(X)) +function lie_bracket(M::ValidationGroup, X, Y) + eM = Identity(M.manifold) is_vector(M, eM, X, true) is_vector(M, eM, Y, true) Z = ValidationTVector(lie_bracket(M.manifold, array_value(X), array_value(Y))) @@ -59,8 +52,8 @@ function lie_bracket(M::ValidationManifold, X, Y) return Z end -function lie_bracket!(M::ValidationManifold, Z, X, Y) - eM = make_identity(M.manifold, array_value(X)) +function lie_bracket!(M::ValidationGroup, Z, X, Y) + eM = Identity(M.manifold) is_vector(M, eM, X, true) is_vector(M, eM, Y, true) lie_bracket!(M.manifold, array_value(Z), array_value(X), array_value(Y)) @@ -68,7 +61,7 @@ function lie_bracket!(M::ValidationManifold, Z, X, Y) return Z end -function compose(M::ValidationManifold, p, q; kwargs...) +function compose(M::ValidationGroup, p, q; kwargs...) is_point(M, p, true; kwargs...) is_point(M, q, true; kwargs...) x = array_point(compose(M.manifold, array_value(p), array_value(q))) @@ -76,7 +69,7 @@ function compose(M::ValidationManifold, p, q; kwargs...) return x end -function compose!(M::ValidationManifold, x, p, q; kwargs...) +function compose!(M::ValidationGroup, x, p, q; kwargs...) is_point(M, p, true; kwargs...) is_point(M, q, true; kwargs...) compose!(M.manifold, array_value(x), array_value(p), array_value(q)) @@ -210,40 +203,40 @@ function inverse_translate_diff!( return Y end -function group_exp(M::ValidationManifold, X; kwargs...) +function exp_lie(M::ValidationManifold, X; kwargs...) is_vector( M, - make_identity(M.manifold, array_value(X)), + Identity(M.manifold), array_value(X), true; check_base_point=false, kwargs..., ) - q = array_point(group_exp(M.manifold, array_value(X))) + q = array_point(exp_lie(M.manifold, array_value(X))) is_point(M, q, true; kwargs...) return q end -function group_exp!(M::ValidationManifold, q, X; kwargs...) +function exp_lie!(M::ValidationManifold, q, X; kwargs...) is_vector( M, - make_identity(M.manifold, array_value(X)), + Identity(M.manifold), array_value(X), true; check_base_point=false, kwargs..., ) - group_exp!(M.manifold, array_value(q), array_value(X)) + exp_lie!(M.manifold, array_value(q), array_value(X)) is_point(M, q, true; kwargs...) return q end -function group_log(M::ValidationManifold, q; kwargs...) +function log_lie(M::ValidationManifold, q; kwargs...) is_point(M, q, true; kwargs...) - X = ValidationTVector(group_log(M.manifold, array_value(q))) + X = ValidationTVector(log_lie(M.manifold, array_value(q))) is_vector( M, - make_identity(M.manifold, array_value(X)), + Identity(M.manifold), array_value(X), true; check_base_point=false, @@ -252,12 +245,12 @@ function group_log(M::ValidationManifold, q; kwargs...) return X end -function group_log!(M::ValidationManifold, X, q; kwargs...) +function log_lie!(M::ValidationManifold, X, q; kwargs...) is_point(M, q, true; kwargs...) - group_log!(M.manifold, array_value(X), array_value(q)) + log_lie!(M.manifold, array_value(X), array_value(q)) is_vector( M, - make_identity(M.manifold, array_value(X)), + Identity(M.manifold), array_value(X), true; check_base_point=false, diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index 4b59a85252..109b5da6fd 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -143,17 +143,6 @@ function get_coordinates( return @SVector [Xⁱ] end -eval( - quote - @invoke_maker 1 AbstractManifold get_coordinates( - M::Circle, - e::Identity, - X, - B::VeeOrthogonalBasis, - ) - end, -) - function get_coordinates!( M::Circle, Y::AbstractArray, diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 6c81ba4ab8..0e96b5778e 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,5 +1,5 @@ @doc raw""" - FixedRankMatrices{m,n,k,𝔽} <: AbstractManifold{𝔽} + FixedRankMatrices{m,n,k,𝔽} <: AbstractEmbeddedManifold{𝔽,DefaultEmbeddingType} The manifold of ``m × n`` real-valued or complex-valued matrices of fixed rank ``k``, i.e. ````math diff --git a/src/tests/tests_group.jl b/src/tests/tests_group.jl index 2d00d13e9a..4036f5bec0 100644 --- a/src/tests/tests_group.jl +++ b/src/tests/tests_group.jl @@ -1,3 +1,4 @@ +using Base: IdentityUnitRange """ test_group( G, @@ -6,7 +7,7 @@ Xe_pts::AbstractVector = []; atol = 1e-10, test_mutating = true, - test_group_exp_log = true, + test_exp_lie_log = true, test_diff = false, test_invariance = false, test_lie_bracket=false, @@ -30,14 +31,14 @@ function test_group( Xe_pts::AbstractVector=[]; atol=1e-10, test_mutating=true, - test_group_exp_log=true, + test_exp_lie_log=true, test_diff=false, test_invariance=false, test_lie_bracket=false, test_adjoint_action=false, diff_convs=[(), (LeftAction(),), (RightAction(),)], ) - e = make_identity(G, g_pts[1]) + e = Identity(G) Test.@testset "Basic group properties" begin Test.@testset "Closed" begin @@ -63,16 +64,23 @@ function test_group( end Test.@testset "Identity" begin + Test.@test is_point(G, e) + wrong_e = if e === Identity(MultiplicationOperation()) + Identity(AdditionOperation()) + else + Identity(MultiplicationOperation()) + end + Test.@test !is_point(G, wrong_e) + Test.@test !is_identity(G, wrong_e) Test.@test isapprox(G, e, e) - Test.@test identity(G, e) === e Test.@test compose(G, e, e) === e - Test.@test copyto!(e, e) === e + Test.@test copyto!(G, e, e) === e + ge = identity_element(G, g_pts[1]) for g in g_pts Test.@test isapprox(G, compose(G, g, e), g) Test.@test isapprox(G, compose(G, e, g), g) - ge = identity(G, g) Test.@test isapprox(G, compose(G, g, ge), g) Test.@test isapprox(G, compose(G, ge, g), g) end @@ -86,12 +94,10 @@ function test_group( Test.@test compose!(G, h, e, g) === h Test.@test isapprox(G, h, g) - ge = allocate(g) - Test.@test identity!(G, ge, e) === ge - Test.@test isapprox(G, compose(G, g, ge), g) - Test.@test isapprox(G, compose(G, ge, g), g) + ge = Identity(G) + Test.@test isapprox(G, compose(G, g, e), g) + Test.@test isapprox(G, compose(G, e, g), g) - ge = allocate(g) Test.@test compose!(G, ge, e, e) === ge Test.@test isapprox(G, ge, e) end @@ -99,24 +105,23 @@ function test_group( end Test.@testset "Inverse" begin + Test.@test inv(G, e) === e for g in g_pts ginv = inv(G, g) - Test.@test isapprox(G, compose(G, g, ginv), e; atol=atol) - Test.@test isapprox(G, compose(G, ginv, g), e; atol=atol) - Test.@test isapprox(G, e, compose(G, g, ginv); atol=atol) - Test.@test isapprox(G, e, compose(G, ginv, g); atol=atol) - Test.@test inv(G, e) === e + Test.@test is_identity(G, compose(G, g, ginv); atol=atol) + Test.@test is_identity(G, compose(G, ginv, g); atol=atol) test_mutating && Test.@testset "mutating" begin + Test.@test inv!(G, e, e) === e ginv = allocate(g) Test.@test inv!(G, ginv, g) === ginv - Test.@test isapprox(G, compose(G, g, ginv), e; atol=atol) - Test.@test isapprox(G, compose(G, ginv, g), e; atol=atol) + Test.@test is_identity(G, compose(G, g, ginv); atol=atol) + Test.@test is_identity(G, compose(G, ginv, g); atol=atol) Test.@test inv(G, e) === e geinv = allocate(g) Test.@test inv!(G, geinv, e) === geinv - Test.@test isapprox(G, geinv, e; atol=atol) + Test.@test is_identity(G, geinv; atol=atol) end end end @@ -286,53 +291,55 @@ function test_group( end end - test_group_exp_log && Test.@testset "group exp/log properties" begin + test_exp_lie_log && Test.@testset "group exp/log properties" begin Test.@testset "e = exp(0)" begin - X = group_log(G, identity(G, g_pts[1])) - g = group_exp(G, X) - Test.@test isapprox(G, make_identity(G, g_pts[1]), g; atol=atol) + X = log_lie(G, Identity(G)) + g = exp_lie(G, X) + Test.@test isapprox(G, Identity(G), g; atol=atol) + ep = identity_element(G, g) + Test.@test isapprox(G, ep, zero_vector(G, ep), log_lie(G, ep); atol=atol) test_mutating && Test.@testset "mutating" begin X = allocate(Xe_pts[1]) - Test.@test group_log!(G, X, identity(G, g_pts[1])) === X + Test.@test log_lie!(G, X, Identity(G)) === X g = allocate(g_pts[1]) - Test.@test group_exp!(G, g, X) === g - Test.@test isapprox(G, make_identity(G, g_pts[1]), g; atol=atol) + Test.@test exp_lie!(G, g, X) === g + Test.@test is_identity(G, g; atol=atol) end end Test.@testset "X = log(exp(X))" begin for X in Xe_pts - g = group_exp(G, X) + g = exp_lie(G, X) Test.@test is_point(G, g; atol=atol) - X2 = group_log(G, g) - Test.@test isapprox(G, make_identity(G, g_pts[1]), X2, X; atol=atol) + X2 = log_lie(G, g) + Test.@test isapprox(G, Identity(G), X2, X; atol=atol) end test_mutating && Test.@testset "mutating" begin for X in Xe_pts g = allocate(g_pts[1]) - Test.@test group_exp!(G, g, X) === g + Test.@test exp_lie!(G, g, X) === g Test.@test is_point(G, g, true; atol=atol) - Test.@test isapprox(G, g, group_exp(G, X); atol=atol) + Test.@test isapprox(G, g, exp_lie(G, X); atol=atol) X2 = allocate(X) - Test.@test group_log!(G, X2, g) === X2 - Test.@test isapprox(G, make_identity(G, g_pts[1]), X2, X; atol=atol) + Test.@test log_lie!(G, X2, g) === X2 + Test.@test isapprox(G, Identity(G), X2, X; atol=atol) end end end Test.@testset "inv(g) = exp(-log(g))" begin g = g_pts[1] - X = group_log(G, g) - ginv = group_exp(G, -X) + X = log_lie(G, g) + ginv = exp_lie(G, -X) Test.@test isapprox(G, ginv, inv(G, g); atol=atol) end Test.@testset "exp(sX)∘exp(tX) = exp((s+t)X)" begin - g1 = group_exp(G, 0.2 * Xe_pts[1]) - g2 = group_exp(G, 0.3 * Xe_pts[1]) - g12 = group_exp(G, 0.5 * Xe_pts[1]) + g1 = exp_lie(G, 0.2 * Xe_pts[1]) + g2 = exp_lie(G, 0.3 * Xe_pts[1]) + g12 = exp_lie(G, 0.5 * Xe_pts[1]) g1_g2 = compose(G, g1, g2) g2_g1 = compose(G, g2, g1) isapprox(G, g1_g2, g12; atol=atol) @@ -340,7 +347,7 @@ function test_group( end end - test_group_exp_log && + test_exp_lie_log && test_diff && Test.@testset "exp/log retract/inverse_retract" begin for conv in diff_convs @@ -415,7 +422,7 @@ function test_group( # linearity X = Xe_pts[1] Y = Xe_pts[2] - e = identity(G, X) + e = Identity(G) Test.@test isapprox( G, e, @@ -454,7 +461,7 @@ function test_group( # anticommutativity X = X_pts[1] Y = X_pts[2] - e = identity(G, X) + e = Identity(G) Test.@test isapprox(G, e, lie_bracket(G, X, Y), -lie_bracket(G, Y, X)) if test_mutating @@ -503,7 +510,7 @@ function test_action( ) G = base_group(A) M = g_manifold(A) - e = make_identity(G, a_pts[1]) + e = Identity(G) Test.@testset "Basic action properties" begin test_switch_direction && Test.@testset "Direction" begin @@ -576,7 +583,7 @@ function test_action( Test.@test isapprox(G, compose(A, a, e), a; atol=atol_ident_compose) Test.@test isapprox(G, compose(A, e, a), a; atol=atol_ident_compose) - ge = identity(G, a) + ge = identity_element(G, a) Test.@test isapprox(G, compose(A, a, ge), a; atol=atol_ident_compose) Test.@test isapprox(G, compose(A, ge, a), a; atol=atol_ident_compose) @@ -597,11 +604,11 @@ function test_action( Test.@test compose!(A, h, e, a) === h Test.@test isapprox(G, h, a) - ge = identity(G, a) + ge = identity_element(G, a) Test.@test isapprox(G, compose(A, a, ge), a) Test.@test isapprox(G, compose(A, ge, a), a) - ge = identity(G, a) + ge = allocate(a) Test.@test compose!(A, ge, e, e) === ge Test.@test isapprox(G, ge, e) diff --git a/test/groups/circle_group.jl b/test/groups/circle_group.jl index 9eb25ca23d..2d159c403c 100644 --- a/test/groups/circle_group.jl +++ b/test/groups/circle_group.jl @@ -20,17 +20,15 @@ using Manifolds: invariant_metric_dispatch, default_metric_dispatch @test is_default_metric(MetricManifold(G, EuclideanMetric())) @testset "identity overloads" begin - ig = Identity(G, [Complex(1.0)]) - @test identity(G, ig) === ig + ig = Identity(G) @test inv(G, ig) === ig - @test allocate_result(G, get_coordinates, ig, Complex(1.0), DefaultBasis()) isa - Array{Complex{Float64},1} - y = [Complex(0.0)] - @test identity!(G, y, [Complex(1.0)]) === y - @test y == [Complex(1.0)] y = [1.0 * im] v = [Complex(0.5)] @test translate_diff(G, ig, y, v) === v + + @test identity_element(G) === 1.0 + @test identity_element(G, 1.0f0) === 1.0f0 + @test identity_element(G, [1.0f0]) == [1.0f0] end @testset "scalar points" begin diff --git a/test/groups/connections.jl b/test/groups/connections.jl index 0a32c3ecb3..5f60cebf5a 100644 --- a/test/groups/connections.jl +++ b/test/groups/connections.jl @@ -10,10 +10,10 @@ using Manifolds: connection SO3zero = ConnectionManifold(SO3, CartanSchoutenZero()) e = Matrix{Float64}(I, 3, 3) - p = exp(hat(SO3, Identity(SO3, e), [1.0, 2.0, 3.0])) - q = exp(hat(SO3, Identity(SO3, e), [3.0, 4.0, 1.0])) - X = hat(SO3, Identity(SO3, e), [2.0, 3.0, 4.0]) - SO3e = Identity(SO3, e) + p = exp(hat(SO3, Identity(SO3), [1.0, 2.0, 3.0])) + q = exp(hat(SO3, Identity(SO3), [3.0, 4.0, 1.0])) + X = hat(SO3, Identity(SO3), [2.0, 3.0, 4.0]) + SO3e = Identity(SO3) @testset "connection" begin @test connection(SO3minus) === CartanSchoutenMinus() diff --git a/test/groups/general_linear.jl b/test/groups/general_linear.jl index 1a9e3807d6..e79e8678a9 100644 --- a/test/groups/general_linear.jl +++ b/test/groups/general_linear.jl @@ -43,6 +43,11 @@ using NLsolve ) ) === Val(true) @test Manifolds.allocation_promotion_function(Gc, exp!, (1,)) === complex + + q = identity_element(G) + @test is_identity(G, q) + @test isapprox(G, q, Identity(G)) + @test isapprox(G, Identity(G), q) end @testset "GL(1,𝔽) special cases" begin @@ -54,8 +59,10 @@ using NLsolve q = exp(G, p, X) Y = log(G, p, q) @test Y ≈ X - @test group_exp(G, X) ≈ exp(X) - @test group_log(G, exp(X)) ≈ X + @test exp_lie(G, X) ≈ exp(X) + @test log_lie(G, exp(X)) ≈ X + @test log_lie(G, [1.0]) == zeros(1) # vector to vector + log_lie(G, Identity(G)) == zeros(1, 1) # Matrix to matrix end @testset "complex" begin G = GeneralLinear(1, ℂ) @@ -65,8 +72,8 @@ using NLsolve q = exp(G, p, X) Y = log(G, p, q) @test Y ≈ X - @test group_exp(G, X) ≈ exp(X) - @test group_log(G, exp(X)) ≈ X + @test exp_lie(G, X) ≈ exp(X) + @test log_lie(G, exp(X)) ≈ X end end @@ -78,13 +85,8 @@ using NLsolve @test_throws DomainError is_point(G, randn(ComplexF64, 3, 3), true) @test_throws DomainError is_point(G, zeros(3, 3), true) @test_throws DomainError is_point(G, Float64[0 0 0; 0 1 1; 1 1 1], true) - @test_throws DomainError is_point( - G, - make_identity(GeneralLinear(2), ones(2, 2)), - true, - ) @test is_point(G, Float64[0 0 1; 0 1 1; 1 1 1], true) - @test is_point(G, make_identity(G, ones(3, 3)), true) + @test is_point(G, Identity(G), true) @test_throws DomainError is_vector( G, Float64[0 1 1; 0 1 1; 1 0 0], @@ -159,13 +161,8 @@ using NLsolve @test_throws DomainError is_point(G, zeros(2, 2), true) @test_throws DomainError is_point(G, ComplexF64[1 im; 1 im], true) @test is_point(G, ComplexF64[1 1; im 1], true) - @test is_point(G, make_identity(G, ones(ComplexF64, 2, 2)), true) + @test is_point(G, Identity(G), true) @test_throws DomainError is_point(G, Float64[0 0 0; 0 1 1; 1 1 1], true) - @test_throws DomainError is_point( - G, - make_identity(GeneralLinear(3), ones(3, 3)), - true, - ) @test_throws DomainError is_vector( G, ComplexF64[im im; im im], diff --git a/test/groups/groups_general.jl b/test/groups/groups_general.jl index 81c65909c4..6f8e976526 100644 --- a/test/groups/groups_general.jl +++ b/test/groups/groups_general.jl @@ -1,3 +1,4 @@ +using StaticArrays: identity_perm using Manifolds: decorator_transparent_dispatch using Base: decode_overlong @@ -12,133 +13,109 @@ include("group_utils.jl") G = GroupManifold(NotImplementedManifold(), NotImplementedOperation()) @test repr(G) == "GroupManifold(NotImplementedManifold(), NotImplementedOperation())" - x = [1.0, 2.0] - v = [2.0, 3.0] - eg = Identity(G, [0.0, 0.0]) - @test repr(eg) === "Identity($(G), $([0.0, 0.0]))" + p = [1.0, 2.0] + X = [2.0, 3.0] + eg = Identity(G) + @test repr(eg) === "Identity(NotImplementedOperation)" @test number_eltype(eg) == Bool - @test is_point(G, eg) # identity transparent - p = similar(x) - copyto!(p, eg) - @test p == eg.p - @test isapprox(G, eg, p) - @test isapprox(G, p, eg) + @test is_identity(G, eg) # identity transparent + @test_throws MethodError identity_element(G) # but for a NotImplOp there is no concrete id. @test isapprox(G, eg, eg) + @test_throws MethodError is_identity(G, 1) # same rror as before i.e. dispatch isapprox works @test length(methods(is_group_decorator)) == 1 + @test Identity(NotImplementedOperation()) === eg + @test Identity(NotImplementedOperation) === eg + @test !is_point(G, Identity(AdditionOperation())) + @test !isapprox(G, eg, Identity(AdditionOperation())) + @test Manifolds.is_group_decorator(G) @test Manifolds.decorator_group_dispatch(G) === Val{true}() @test Manifolds.default_decorator_dispatch(G) === Val{false}() @test !Manifolds.is_group_decorator(NotImplementedManifold()) @test Manifolds.decorator_group_dispatch(NotImplementedManifold()) === Val{false}() - @test Manifolds.decorator_transparent_dispatch(compose, G, x, x, x) === + @test Manifolds.decorator_transparent_dispatch(compose, G, p, p, p) === Val{:intransparent}() - @test Manifolds.decorator_transparent_dispatch(compose!, G, x, x, x) === + @test Manifolds.decorator_transparent_dispatch(compose!, G, p, p, p) === Val{:intransparent}() - @test Manifolds.decorator_transparent_dispatch(group_exp, G, x, x) === + @test Manifolds.decorator_transparent_dispatch(exp_lie, G, p, p) === Val{:intransparent}() - @test Manifolds.decorator_transparent_dispatch(group_log, G, x, x) === + @test Manifolds.decorator_transparent_dispatch(log_lie, G, p, p) === + Val{:intransparent}() + @test Manifolds.decorator_transparent_dispatch(translate_diff!, G, X, p, p, X) === Val{:intransparent}() - @test Manifolds.decorator_transparent_dispatch( - translate_diff!, - G, - x, - x, - x, - x, - x, - ) === Val{:intransparent}() @test base_group(G) === G - z = similar(x) - copyto!(z, eg) - @test z == eg.p @test NotImplementedOperation(NotImplementedManifold()) === G @test (NotImplementedOperation())(NotImplementedManifold()) === G - @test_throws ErrorException allocate_result( - G, - get_vector, - Identity(SpecialOrthogonal(3), x), - v, - ) - @test_throws ErrorException allocate_result( - G, - get_coordinates, - Identity(SpecialOrthogonal(3), x), - v, - ) - @test_throws ErrorException allocate_result( - ValidationManifold(NotImplementedManifold()), - get_coordinates, - Identity(SpecialOrthogonal(3), x), - v, - ) @test_throws ErrorException base_group( MetricManifold(Euclidean(3), EuclideanMetric()), ) @test_throws ErrorException hat(Rotations(3), eg, [1, 2, 3]) - @test_throws ErrorException hat( + # If you force it, you get a not that readable MethodError + @test_throws MethodError hat( GroupManifold(Rotations(3), NotImplementedOperation()), eg, [1, 2, 3], ) @test_throws ErrorException vee(Rotations(3), eg, [1, 2, 3]) - @test_throws ErrorException vee( + @test_throws MethodError vee( GroupManifold(Rotations(3), NotImplementedOperation()), eg, [1, 2, 3], ) - @test_throws ErrorException Identity(Euclidean(3), [0, 0, 0]) - @test_throws ErrorException inv!(G, x, x) - @test_throws ErrorException inv!(G, x, eg) - @test_throws ErrorException inv(G, x) + @test_throws ErrorException inv!(G, p, p) + @test_throws MethodError inv!(G, p, eg) + @test_throws ErrorException inv(G, p) - @test copyto!(x, eg) === x - @test isapprox(G, x, eg) - @test_throws ErrorException identity!(G, x, x) - @test_throws ErrorException identity(G, x) + # no function defined to return the identity array representation + @test_throws MethodError copyto!(G, p, eg) - @test_throws ErrorException compose(G, x, x) - @test_throws ErrorException compose(G, x, eg) - @test_throws ErrorException compose!(G, x, eg, x) - @test_throws ErrorException compose!(G, x, x, eg) - @test_throws ErrorException compose!(G, x, x, x) - @test_throws ErrorException compose!(G, x, eg, eg) + @test_throws MethodError compose(G, p, p) + @test compose(G, p, eg) == p + xO = deepcopy(p) + compose!(G, p, eg, p) + @test xO == p + compose!(G, p, p, eg) + @test xO == p + @test_throws MethodError compose!(G, p, p, p) + @test_throws MethodError compose!(G, p, eg, eg) - @test_throws ErrorException translate(G, x, x) - @test_throws ErrorException translate(G, x, x, LeftAction()) - @test_throws ErrorException translate(G, x, x, RightAction()) - @test_throws ErrorException translate!(G, x, x, x) - @test_throws ErrorException translate!(G, x, x, x, LeftAction()) - @test_throws ErrorException translate!(G, x, x, x, RightAction()) + @test_throws MethodError translate(G, p, p) + @test_throws MethodError translate(G, p, p, LeftAction()) + @test_throws MethodError translate(G, p, p, RightAction()) + @test_throws MethodError translate!(G, p, p, p) + @test_throws MethodError translate!(G, p, p, p, LeftAction()) + @test_throws MethodError translate!(G, p, p, p, RightAction()) - @test_throws ErrorException inverse_translate(G, x, x) - @test_throws ErrorException inverse_translate(G, x, x, LeftAction()) - @test_throws ErrorException inverse_translate(G, x, x, RightAction()) - @test_throws ErrorException inverse_translate!(G, x, x, x) - @test_throws ErrorException inverse_translate!(G, x, x, x, LeftAction()) - @test_throws ErrorException inverse_translate!(G, x, x, x, RightAction()) + @test_throws ErrorException inverse_translate(G, p, p) + @test_throws ErrorException inverse_translate(G, p, p, LeftAction()) + @test_throws ErrorException inverse_translate(G, p, p, RightAction()) + @test_throws ErrorException inverse_translate!(G, p, p, p) + @test_throws ErrorException inverse_translate!(G, p, p, p, LeftAction()) + @test_throws ErrorException inverse_translate!(G, p, p, p, RightAction()) - @test_throws ErrorException translate_diff(G, x, x, v) - @test_throws ErrorException translate_diff(G, x, x, v, LeftAction()) - @test_throws ErrorException translate_diff(G, x, x, v, RightAction()) - @test_throws ErrorException translate_diff!(G, v, x, x, v) - @test_throws ErrorException translate_diff!(G, v, x, x, v, LeftAction()) - @test_throws ErrorException translate_diff!(G, v, x, x, v, RightAction()) + @test_throws ErrorException translate_diff(G, p, p, X) + @test_throws ErrorException translate_diff(G, p, p, X, LeftAction()) + @test_throws ErrorException translate_diff(G, p, p, X, RightAction()) + @test_throws ErrorException translate_diff!(G, X, p, p, X) + @test_throws ErrorException translate_diff!(G, X, p, p, X, LeftAction()) + @test_throws ErrorException translate_diff!(G, X, p, p, X, RightAction()) - @test_throws ErrorException inverse_translate_diff(G, x, x, v) - @test_throws ErrorException inverse_translate_diff(G, x, x, v, LeftAction()) - @test_throws ErrorException inverse_translate_diff(G, x, x, v, RightAction()) - @test_throws ErrorException inverse_translate_diff!(G, v, x, x, v) - @test_throws ErrorException inverse_translate_diff!(G, v, x, x, v, LeftAction()) - @test_throws ErrorException inverse_translate_diff!(G, v, x, x, v, RightAction()) + @test_throws ErrorException inverse_translate_diff(G, p, p, X) + @test_throws ErrorException inverse_translate_diff(G, p, p, X, LeftAction()) + @test_throws ErrorException inverse_translate_diff(G, p, p, X, RightAction()) + @test_throws ErrorException inverse_translate_diff!(G, X, p, p, X) + @test_throws ErrorException inverse_translate_diff!(G, X, p, p, X, LeftAction()) + @test_throws ErrorException inverse_translate_diff!(G, X, p, p, X, RightAction()) - @test_throws ErrorException group_exp(G, v) - @test_throws ErrorException group_exp!(G, x, v) - @test_throws ErrorException group_log(G, x) - @test_throws ErrorException group_log!(G, v, x) + @test_throws ErrorException exp_lie(G, X) + @test_throws ErrorException exp_lie!(G, p, X) + # no transparency error, but _log_lie missing + @test_throws MethodError log_lie(G, p) + @test_throws MethodError log_lie!(G, X, p) for f in [translate, translate!] @test Manifolds.decorator_transparent_dispatch(f, G) === Val{:intransparent}() @@ -146,18 +123,16 @@ include("group_utils.jl") for f in [inverse_translate_diff!, inverse_translate_diff] @test Manifolds.decorator_transparent_dispatch(f, G) === Val{:transparent}() end - for f in [group_exp!, group_exp, group_log, group_log!] - @test Manifolds.decorator_transparent_dispatch(f, G, x, x) === + for f in [exp_lie!, exp_lie, log_lie, log_lie!] + @test Manifolds.decorator_transparent_dispatch(f, G, p, p) === Val{:intransparent}() end for f in [get_vector, get_coordinates] @test Manifolds.decorator_transparent_dispatch(f, G) === Val{:parent}() end - @test Manifolds.decorator_transparent_dispatch(identity!, G, x, x) === - Val{:intransparent}() - @test Manifolds.decorator_transparent_dispatch(isapprox, G, eg, x) === + @test Manifolds.decorator_transparent_dispatch(isapprox, G, eg, p) === Val{:transparent}() - @test Manifolds.decorator_transparent_dispatch(isapprox, G, x, eg) === + @test Manifolds.decorator_transparent_dispatch(isapprox, G, p, eg) === Val{:transparent}() @test Manifolds.decorator_transparent_dispatch(isapprox, G, eg, eg) === Val{:transparent}() @@ -173,65 +148,46 @@ include("group_utils.jl") @testset "Addition operation" begin G = GroupManifold(NotImplementedManifold(), Manifolds.AdditionOperation()) - test_group(G, [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], [], [[1.0, 2.0]]) - - @test_throws DomainError is_point( + test_group( G, - Identity( - GroupManifold(NotImplementedManifold(), NotImplementedOperation()), - [0.0, 0.0], - ), - true, + [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]], + [], + [[1.0, 2.0]]; + test_exp_lie_log=false, # there is no identity element so log/exp on Lie do not work ) - x = [1.0, 2.0] - v = [3.0, 4.0] - ge = Identity(G, [0.0, 0.0]) - @test zero(ge) === ge + p = [1.0, 2.0] + X = [3.0, 4.0] + ge = Identity(G) @test number_eltype(ge) == Bool - @test copyto!(ge, ge) === ge - y = allocate(x) - copyto!(y, ge) - @test y ≈ zero(x) - @test ge - x == -x - @test x - ge === x + y = allocate(p) + copyto!(G, y, ge) + @test y ≈ zero(p) + @test ge - p == -p + @test p - ge === p @test ge - ge === ge - @test ge + x ≈ x - @test x + ge ≈ x + @test ge + p ≈ p + @test p + ge ≈ p @test ge + ge === ge - @test ge + Identity(G, 1) === ge @test -(ge) === ge @test +(ge) === ge - @test ge - Identity(G, 1) === ge @test ge * 1 === ge @test 1 * ge === ge @test ge * ge === ge - @test ge.p ≈ zero(x) - @test zero(ge) == ge - @test inv(G, x) ≈ -x + @test inv(G, p) ≈ -p @test inv(G, ge) === ge - @test identity(G, x) ≈ zero(x) - @test identity(G, ge) === ge - y = allocate(x) - identity!(G, y, x) - @test y ≈ zero(x) - @test compose(G, x, x) ≈ x + x - @test compose(G, x, ge) ≈ x - @test compose(G, ge, x) ≈ x + @test compose(G, p, p) ≈ p + p + @test compose(G, p, ge) ≈ p + @test compose(G, ge, p) ≈ p @test compose(G, ge, ge) == ge - compose!(G, y, x, x) - @test y ≈ x + x - compose!(G, y, x, ge) - @test y ≈ x - compose!(G, y, ge, x) - @test y ≈ x - @test group_exp(G, v) === v - @test group_log(G, x) === x - - y = identity(G, x) - @test isapprox(y, ge; atol=1e-10) - @test isapprox(ge, y; atol=1e-10) - @test isapprox(ge, ge) + compose!(G, y, p, p) + @test y ≈ p + p + compose!(G, y, p, ge) + @test y ≈ p + compose!(G, y, ge, p) + @test y ≈ p + @test exp_lie(G, X) === X + @test log_lie(G, p) === p end @testset "Multiplication operation" begin @@ -241,105 +197,91 @@ include("group_utils.jl") [[2.0 1.0; 3.0 4.0], [3.0 2.0; 4.0 5.0], [4.0 3.0; 5.0 6.0]], [], [[1.0 2.0; 3.0 4.0]]; - test_group_exp_log=true, + test_exp_lie_log=false, # no identity available as array ) - x = [2.0 1.0; 2.0 3.0] - ge = Identity(G, [1.0 0.0; 0.0 1.0]) + p = [2.0 1.0; 2.0 3.0] + ge = Identity(G) @test number_eltype(ge) == Bool - @test copyto!(ge, ge) === ge - y = allocate(x) - copyto!(y, ge) - @test y ≈ one(x) + @test copyto!(G, ge, ge) === ge + y = allocate(p) + identity_element!(G, y) + @test_throws DimensionMismatch identity_element!(G, [1, 2, 3]) + @test y ≈ one(p) @test one(ge) === ge @test transpose(ge) === ge @test det(ge) == 1 - @test ge * x ≈ x - @test x * ge ≈ x + @test ge * p ≈ p + @test p * ge ≈ p @test ge * ge === ge - @test inv(ge) === ge + @test inv(G, ge) === ge @test *(ge) === ge - @test x / ge ≈ x - @test ge \ x ≈ x + @test p / ge ≈ p + @test ge \ p ≈ p @test ge / ge === ge @test ge \ ge === ge - @test ge / x ≈ inv(x) - @test x \ ge ≈ inv(x) - y = allocate(x) - @test LinearAlgebra.mul!(y, x, ge) === y - @test y ≈ x - y = allocate(x) - @test LinearAlgebra.mul!(y, ge, x) === y - @test y ≈ x - y = allocate(x) + @test ge / p ≈ inv(G, p) + @test p \ ge ≈ inv(G, p) + y = allocate(p) + @test LinearAlgebra.mul!(y, p, ge) === y + @test y ≈ p + y = allocate(p) + @test LinearAlgebra.mul!(y, ge, p) === y + @test y ≈ p + y = allocate(p) @test LinearAlgebra.mul!(y, ge, ge) === y @test y ≈ one(y) - @test ge.p ≈ one(x) - @test inv(G, x) ≈ inv(x) + @test inv(G, p) ≈ inv(p) @test inv(G, ge) === ge - @test identity(G, x) ≈ one(x) - @test identity(G, ge) === ge - y = allocate(x) - identity!(G, y, x) - @test y ≈ one(x) - z = allocate(x) - copyto!(G, z, x) - z2 = allocate(x) - copyto!(G.manifold, z2, x) + z = allocate(p) + copyto!(G, z, p) + z2 = allocate(p) + copyto!(G.manifold, z2, p) @test z == z2 X = zeros(2, 2) Y = allocate(X) - copyto!(G, Y, x, X) + copyto!(G, Y, p, X) Y2 = allocate(X) - copyto!(G.manifold, Y2, x, X) + copyto!(G.manifold, Y2, p, X) @test Y == Y2 - @test_throws ErrorException identity!(G, [0.0], ge) - @test compose(G, x, x) ≈ x * x - @test compose(G, x, ge) ≈ x - @test compose(G, ge, x) ≈ x + @test compose(G, p, p) ≈ p * p + @test compose(G, p, ge) ≈ p + @test compose(G, ge, p) ≈ p @test compose(G, ge, ge) == ge - compose!(G, y, x, x) - @test y ≈ x * x - compose!(G, y, x, ge) - @test y ≈ x - compose!(G, y, ge, x) - @test y ≈ x + compose!(G, y, p, p) + @test y ≈ p * p + compose!(G, y, p, ge) + @test y ≈ p + compose!(G, y, ge, p) + @test y ≈ p X = [1.0 2.0; 3.0 4.0] - @test group_exp!(G, y, X) === y - @test_throws ErrorException group_exp!(G, y, :a) + @test exp_lie!(G, y, X) === y + @test_throws ErrorException exp_lie!(G, y, :a) @test y ≈ exp(X) Y = allocate(X) - @test group_log!(G, Y, y) === Y + @test log_lie!(G, Y, y) === Y @test Y ≈ log(y) - @testset "identity optimization" begin - x2 = copy(x) - identity!(G, x2, x) - x3 = copy(x) - invoke( - identity!, - Tuple{ - AbstractGroupManifold{ℝ,Manifolds.MultiplicationOperation}, - Any, - AbstractMatrix, - }, - G, - x3, - x, - ) - @test isapprox(G, x2, x3) - end + q2 = SVDMPoint(2 * Matrix{Float64}(I, 3, 3)) + mul!(q2, ge, ge) + qT = SVDMPoint(Matrix{Float64}(I, 3, 3)) + @test isapprox(FixedRankMatrices(3, 3, 3), q2, qT) end @testset "Identity on Group Manifolds" begin G = TranslationGroup(3) - e = Identity(G, zeros(3)) - @test get_vector(G, e, ones(3), DefaultOrthogonalBasis()) == ones(3) + e = Identity(G) + @test get_vector_lie(G, ones(3), DefaultOrthogonalBasis()) == ones(3) @test e - e == e @test ones(3) + e == ones(3) + e_add = Identity(AdditionOperation) + e_mul = Identity(MultiplicationOperation) + @test e_add * e_mul === e_add + @test e_mul * e_add === e_add + @test mul!(e_mul, e_mul, e_mul) === e_mul end @testset "Transparency tests" begin @@ -368,23 +310,23 @@ struct NotImplementedAction <: AbstractGroupAction{LeftAction} end @testset "General group action tests" begin @testset "Not implemented operations" begin A = NotImplementedAction() - x = [1.0, 2.0] + p = [1.0, 2.0] a = [1.0, 2.0] - v = [1.0, 2.0] + X = [1.0, 2.0] @test_throws ErrorException base_group(A) @test_throws ErrorException g_manifold(A) - @test_throws ErrorException apply(A, a, x) - @test_throws ErrorException apply!(A, x, a, x) - @test_throws ErrorException inverse_apply(A, a, x) - @test_throws ErrorException inverse_apply!(A, x, a, x) - @test_throws ErrorException apply_diff(A, a, x, v) - @test_throws ErrorException apply_diff!(A, v, x, a, v) - @test_throws ErrorException inverse_apply_diff(A, a, x, v) - @test_throws ErrorException inverse_apply_diff!(A, v, x, a, v) + @test_throws ErrorException apply(A, a, p) + @test_throws ErrorException apply!(A, p, a, p) + @test_throws ErrorException inverse_apply(A, a, p) + @test_throws ErrorException inverse_apply!(A, p, a, p) + @test_throws ErrorException apply_diff(A, a, p, X) + @test_throws ErrorException apply_diff!(A, X, p, a, X) + @test_throws ErrorException inverse_apply_diff(A, a, p, X) + @test_throws ErrorException inverse_apply_diff!(A, X, p, a, X) @test_throws ErrorException compose(A, a, a) @test_throws ErrorException compose!(A, a, a, a) - @test_throws ErrorException optimal_alignment(A, x, x) - @test_throws ErrorException optimal_alignment!(A, a, x, x) + @test_throws ErrorException optimal_alignment(A, p, p) + @test_throws ErrorException optimal_alignment!(A, a, p, p) end end diff --git a/test/groups/metric.jl b/test/groups/metric.jl index 0741c34724..f435a670f0 100644 --- a/test/groups/metric.jl +++ b/test/groups/metric.jl @@ -100,14 +100,14 @@ invariant_metric_dispatch(::TestDefaultInvariantMetricManifold, ::RightAction) = e = Matrix{Float64}(I, 3, 3) @testset "inner/norm" begin SO3 = SpecialOrthogonal(3) - p = exp(hat(SO3, Identity(SO3, e), [1.0, 2.0, 3.0])) + p = exp(hat(SO3, Identity(SO3), [1.0, 2.0, 3.0])) B = DefaultOrthonormalBasis() fX = ManifoldsBase.TFVector([2.0, 3.0, 4.0], B) fY = ManifoldsBase.TFVector([3.0, 4.0, 1.0], B) - X = hat(SO3, Identity(SO3, e), fX.data) - Y = hat(SO3, Identity(SO3, e), fY.data) + X = hat(SO3, Identity(SO3), fX.data) + Y = hat(SO3, Identity(SO3), fY.data) G = MetricManifold(SO3, lmetric) @test inner(G, p, fX, fY) ≈ dot(fX.data, Diagonal([1.0, 2.0, 3.0]) * fY.data) @@ -121,9 +121,11 @@ invariant_metric_dispatch(::TestDefaultInvariantMetricManifold, ::RightAction) = @testset "log/exp bi-invariant" begin SO3 = SpecialOrthogonal(3) - p = exp(hat(SO3, Identity(SO3, e), [1.0, 2.0, 3.0])) - q = exp(hat(SO3, Identity(SO3, e), [3.0, 4.0, 1.0])) - X = hat(SO3, Identity(SO3, e), [2.0, 3.0, 4.0]) + e = Identity(SO3) + pe = identity_element(SO3) + p = exp(hat(SO3, pe, [1.0, 2.0, 3.0])) + q = exp(hat(SO3, pe, [3.0, 4.0, 1.0])) + X = hat(SO3, e, [2.0, 3.0, 4.0]) G = MetricManifold(SO3, InvariantMetric(TestBiInvariantMetricBase(), LeftAction())) @test isapprox(SO3, exp(G, p, X), exp(SO3, p, X)) diff --git a/test/groups/product_group.jl b/test/groups/product_group.jl index 44b3af4780..c61b31b99c 100644 --- a/test/groups/product_group.jl +++ b/test/groups/product_group.jl @@ -15,7 +15,7 @@ include("group_utils.jl") @test sprint(show, G) == "ProductGroup($(SOn), $(Tn))" @test sprint(show, "text/plain", G) == "ProductGroup with 2 subgroups:\n $(SOn)\n $(Tn)" x = Matrix{Float64}(I, 3, 3) - for f in [group_exp!, group_log!] + for f in [exp_lie!, log_lie!] @test Manifolds.decorator_transparent_dispatch(f, G, x, x) === Val{:transparent}() end t = Vector{Float64}.([1:2, 2:3, 3:4]) @@ -26,14 +26,11 @@ include("group_utils.jl") shape_se = Manifolds.ShapeSpecification(Manifolds.ArrayReshaper(), M.manifolds...) e = Manifolds.prod_point(shape_se, eA...) - @testset "identity specializations" begin - @test inv(G, Identity(G, e)) === Identity(G, e) - @test identity(G, Identity(G, e)) === Identity(G, e) - @test submanifold_component(G, Identity(G, e), Val(1)) == Identity(SOn, x) - @test submanifold_component(G, Identity(G, e), Val(2)) == Identity(Tn, zeros(2)) - @test submanifold_components(G, Identity(G, e)) == - (Identity(SOn, x), Identity(Tn, zeros(2))) - @test compose(G, Identity(G, e), Identity(G, e)) === Identity(G, e) + @testset "Product Identity" begin + i = Identity(G) + @test submanifold_components(G, i) == (Identity(SOn), Identity(Tn)) + @test submanifold_component(G, i, 1) == Identity(SOn) + @test submanifold_component(G, i, 2) == Identity(Tn) end @testset "product point" begin @@ -42,50 +39,66 @@ include("group_utils.jl") shape_se = Manifolds.ShapeSpecification(reshaper, M.manifolds...) pts = [Manifolds.prod_point(shape_se, tp...) for tp in tuple_pts] v_pts = [Manifolds.prod_point(shape_se, tuple_v...)] - @test compose(G, pts[1], Identity(G, e)) == pts[1] - @test compose(G, Identity(G, e), pts[1]) == pts[1] + @test compose(G, pts[1], Identity(G)) == pts[1] + @test compose(G, Identity(G), pts[1]) == pts[1] test_group(G, pts, v_pts, v_pts; test_diff=true) @test isapprox( - M, - identity(M, pts[1]), - group_exp(M, v_pts[1]), + G, + Identity(G), + exp_lie(G, v_pts[1]), Manifolds.prod_point( shape_se, - group_exp(SOn, v_pts[1].parts[1]), - group_exp(Tn, v_pts[1].parts[2]), + exp_lie(SOn, v_pts[1].parts[1]), + exp_lie(Tn, v_pts[1].parts[2]), ), ) @test isapprox( - M, - identity(M, pts[1]), - group_log(M, pts[1]), + G, + Identity(G), + log_lie(G, pts[1]), Manifolds.prod_point( shape_se, - group_log(SOn, pts[1].parts[1]), - group_log(Tn, pts[1].parts[2]), + log_lie(SOn, pts[1].parts[1]), + log_lie(Tn, pts[1].parts[2]), ), ) + X = log_lie(G, pts[1]) + Z = zero_vector(G, pts[1]) + log_lie!(G, Z, pts[1]) + @test isapprox(G, pts[1], X, Z) + p = exp_lie(G, X) + q = identity_element(G) + @test is_identity(G, q) + @test isapprox(G, q, Identity(G)) + @test isapprox(G, Identity(G), q) + exp_lie!(G, q, X) + @test isapprox(G, p, q) + log_lie!(G, Z, Identity(G)) + @test isapprox(G, Identity(G), Z, zero_vector(G, identity_element(G))) + @test isapprox( + G, + Identity(G), + log_lie(G, Identity(G)), + zero_vector(G, identity_element(G)), + ) end end @testset "product repr" begin pts = [ProductRepr(tp...) for tp in tuple_pts] v_pts = [ProductRepr(tuple_v...)] - @test compose(G, pts[1], Identity(G, e)) == pts[1] - @test compose(G, Identity(G, e), pts[1]) == pts[1] + @test compose(G, pts[1], Identity(G)) == pts[1] + @test compose(G, Identity(G), pts[1]) == pts[1] test_group(G, pts, v_pts, v_pts; test_diff=true, test_mutating=false) @test isapprox( - M, - group_exp(M, v_pts[1]), - ProductRepr( - group_exp(SOn, v_pts[1].parts[1]), - group_exp(Tn, v_pts[1].parts[2]), - ), + G, + exp_lie(G, v_pts[1]), + ProductRepr(exp_lie(SOn, v_pts[1].parts[1]), exp_lie(Tn, v_pts[1].parts[2])), ) @test isapprox( - M, - group_log(M, pts[1]), - ProductRepr(group_log(SOn, pts[1].parts[1]), group_log(Tn, pts[1].parts[2])), + G, + log_lie(G, pts[1]), + ProductRepr(log_lie(SOn, pts[1].parts[1]), log_lie(Tn, pts[1].parts[2])), ) end @test sprint(show, "text/plain", G) === """ diff --git a/test/groups/semidirect_product_group.jl b/test/groups/semidirect_product_group.jl index f006ad0523..e04f2298fc 100644 --- a/test/groups/semidirect_product_group.jl +++ b/test/groups/semidirect_product_group.jl @@ -31,12 +31,17 @@ include("group_utils.jl") @test norm(G, pts[1], Y) ≈ 0 @test norm(G, pts[1], Z) ≈ 0 - e = Identity(G, (zeros(2), zeros(2))) + e = Identity(G) @test inv(G, e) === e - @test identity(G, e) === e - @test identity!(G, e, e) === e @test compose(G, e, pts[1]) == pts[1] @test compose(G, pts[1], e) == pts[1] @test compose(G, e, e) === e + + eA = identity_element(G) + @test isapprox(G, eA, e) + @test isapprox(G, e, eA) + W = log(G, eA, pts[1]) + Z = log(G, eA, pts[1]) + @test isapprox(G, e, W, Z) end diff --git a/test/groups/special_euclidean.jl b/test/groups/special_euclidean.jl index 667e44f404..d0bd0340fb 100644 --- a/test/groups/special_euclidean.jl +++ b/test/groups/special_euclidean.jl @@ -3,6 +3,8 @@ include("group_utils.jl") using ManifoldsBase: VeeOrthogonalBasis +Random.seed!(10) + @testset "Special Euclidean group" begin @testset "SpecialEuclidean($n)" for n in (2, 3, 4) G = SpecialEuclidean(n) @@ -62,7 +64,7 @@ using ManifoldsBase: VeeOrthogonalBasis Manifolds._padvector!(G, tmp) @test tmp == X_pts[1] - w = translate_diff(G, pts[1], make_identity(G, pts[1]), X_pts[1]) + w = translate_diff(G, pts[1], Identity(G), X_pts[1]) w2 = allocate(w) w2.parts[1] .= w.parts[1] w2.parts[2] .= pts[1].parts[2] * w.parts[2] @@ -92,10 +94,10 @@ using ManifoldsBase: VeeOrthogonalBasis g1g2mat = affine_matrix(G, g1g2) @test g1g2mat ≈ affine_matrix(G, g1) * affine_matrix(G, g2) @test affine_matrix(G, g1g2mat) === g1g2mat - @test affine_matrix(G, make_identity(G, pts[1])) isa SDiagonal{n,Float64} - @test affine_matrix(G, make_identity(G, pts[1])) == SDiagonal{n,Float64}(I) + @test affine_matrix(G, Identity(G)) isa SDiagonal{n,Float64} + @test affine_matrix(G, Identity(G)) == SDiagonal{n,Float64}(I) - w = translate_diff(G, pts[1], make_identity(G, pts[1]), X_pts[1]) + w = translate_diff(G, pts[1], Identity(G), X_pts[1]) w2 = allocate(w) w2.parts[1] .= w.parts[1] w2.parts[2] .= pts[1].parts[2] * w.parts[2] @@ -146,6 +148,11 @@ using ManifoldsBase: VeeOrthogonalBasis p2[1:n, end] .= p[1:n, end] p2[end, end] = p[end, end] @test_throws CompositeManifoldError is_point(G, p2, true) + # exp/log_lie for ProductGroup on arrays + X = copy(G, p, X_pts[1]) + p3 = exp_lie(G, X) + X3 = log_lie(G, p3) + isapprox(G, Identity(G), X, X3) end @testset "hat/vee" begin @@ -161,13 +168,29 @@ using ManifoldsBase: VeeOrthogonalBasis v = vee(G, affine_matrix(G, p), screw_matrix(G, V)) @test v ≈ vexp @test hat(G, affine_matrix(G, p), v) ≈ screw_matrix(G, V) + + e = Identity(G) + Ve = log_lie(G, p) + v = vee(G, e, Ve) + @test_throws ErrorException vee(M, e, Ve) + w = similar(v) + vee!(G, w, e, Ve) + @test isapprox(v, w) + @test_throws ErrorException vee!(M, w, e, Ve) + + We = hat(G, e, v) + @test_throws ErrorException hat(M, e, v) + isapprox(G, e, Ve, We) + We2 = copy(G, p, V) + hat!(G, We2, e, v) + @test_throws ErrorException hat!(M, We, e, v) + @test isapprox(G, e, We, We2) end end G = SpecialEuclidean(11) - @test affine_matrix(G, make_identity(G, ones(12, 12))) isa - Diagonal{Float64,Vector{Float64}} - @test affine_matrix(G, make_identity(G, ones(12, 12))) == Diagonal(ones(11)) + @test affine_matrix(G, Identity(G)) isa Diagonal{Float64,Vector{Float64}} + @test affine_matrix(G, Identity(G)) == Diagonal(ones(11)) @testset "Explicit embedding in GL(n+1)" begin G = SpecialEuclidean(3) @@ -217,7 +240,7 @@ using ManifoldsBase: VeeOrthogonalBasis G, pts_gl[1], X_gl, - translate_diff(G, Identity(G, pts_gl[1]), pts_gl[1], X_gl, conv), + translate_diff(G, Identity(G), pts_gl[1], X_gl, conv), ) end end diff --git a/test/groups/special_linear.jl b/test/groups/special_linear.jl index d92dcdb531..75f9223776 100644 --- a/test/groups/special_linear.jl +++ b/test/groups/special_linear.jl @@ -53,13 +53,8 @@ using NLsolve @test_throws DomainError is_point(G, [1 0 im; im 0 0; 0 -1 0], true) @test_throws DomainError is_point(G, zeros(3, 3), true) @test_throws DomainError is_point(G, Float64[1 3 3; 1 1 2; 1 2 3], true) - @test_throws DomainError is_point( - G, - make_identity(SpecialLinear(2), ones(2, 2)), - true, - ) @test is_point(G, Float64[1 1 1; 2 2 1; 2 3 3], true) - @test is_point(G, make_identity(G, ones(3, 3)), true) + @test is_point(G, Identity(G), true) @test_throws DomainError is_vector( G, Float64[2 3 2; 3 1 2; 1 1 1], @@ -155,13 +150,8 @@ using NLsolve @test_throws DomainError is_point(G, randn(2, 2), true) @test_throws DomainError is_point(G, ComplexF64[1 0 im; im 0 0; 0 -1 0], true) @test_throws DomainError is_point(G, ComplexF64[1 im; im 1], true) - @test_throws DomainError is_point( - G, - make_identity(SpecialLinear(2), ones(ComplexF64, 2, 2)), - true, - ) @test is_point(G, ComplexF64[im 1; -2 im], true) - @test is_point(G, make_identity(G, ones(3, 3)), true) + @test is_point(G, Identity(G), true) @test_throws DomainError is_vector( G, ComplexF64[-1+im -1; -im 1], diff --git a/test/groups/special_orthogonal.jl b/test/groups/special_orthogonal.jl index 744eada676..348655c75a 100644 --- a/test/groups/special_orthogonal.jl +++ b/test/groups/special_orthogonal.jl @@ -28,10 +28,10 @@ include("group_utils.jl") vpts = [hat(M, x, [-1.0, 2.0, 0.5]), hat(M, x, [1.0, 0.0, 0.5])] ge = allocate(pts[1]) - copyto!(ge, make_identity(G, pts[1])) + identity_element!(G, ge) @test isapprox(ge, I; atol=1e-10) - gI = Identity(G, ge) + gI = Identity(G) gT = allocate_result(G, exp, gI, log(G, pts[1], pts[2])) @test size(gT) == size(ge) @test eltype(gT) == eltype(ge) @@ -63,11 +63,6 @@ include("group_utils.jl") @test (@inferred Manifolds.decorator_group_dispatch(DM)) === Val(true) @test Manifolds.is_group_decorator(DM) @test base_group(DM) === G - @test_throws DomainError is_point( - DM, - make_identity(TranslationGroup(3), [1, 2, 3]), - true, - ) test_group(DM, pts, vpts, vpts; test_diff=true) end @@ -136,67 +131,37 @@ include("group_utils.jl") @testset "vee/hat" begin X = vpts[1] - pe = identity(G, pts[1]) + pe = Identity(G) - Xⁱ = vee(G, make_identity(G, pts[1]), X) + Xⁱ = vee(G, identity_element(G), X) @test Xⁱ ≈ vee(G, pe, X) - X2 = hat(G, make_identity(G, pts[1]), Xⁱ) + X2 = hat(G, pts[1], Xⁱ) @test isapprox(M, pe, X2, hat(G, pe, Xⁱ); atol=1e-6) end @testset "Identity and get_vector/get_coordinates" begin - e = Identity(G, Matrix{Float64}(I, 3, 3)) - gT = allocate_result(G, get_coordinates, e, pts[1]) - @test size(gT) == (manifold_dimension(M),) - @test eltype(gT) == eltype(e.p) - @test_throws ErrorException allocate_result(M, get_vector, e, pts[1]) - gT = allocate_result(G, get_vector, e, pts[1]) - @test size(gT) == size(e.p) - @test eltype(gT) == eltype(e.p) - eT = similar(e.p) - copyto!(eT, e) - @test eT == e.p - - eF = Identity(SpecialEuclidean(3), 1) + e = Identity(G) + + eF = Identity(SpecialEuclidean(3)) c = [1.0, 0.0, 0.0] Y = zeros(representation_size(G)) - get_vector!(G, Y, e, c, Manifolds.VeeOrthogonalBasis()) - @test Y ≈ get_vector(decorated_manifold(G), e.p, c, Manifolds.VeeOrthogonalBasis()) - @test_throws ErrorException get_vector!(G, Y, eF, c, Manifolds.VeeOrthogonalBasis()) - get_vector!(M, Y, e, c, Manifolds.VeeOrthogonalBasis()) - @test Y ≈ get_vector(decorated_manifold(G), e.p, c, Manifolds.VeeOrthogonalBasis()) - @test_throws ErrorException get_vector!(M, Y, eF, c, Manifolds.VeeOrthogonalBasis()) - - @test get_coordinates( - decorated_manifold(G), - e, - Y, - Manifolds.VeeOrthogonalBasis(), - ) == c - @test_throws ErrorException get_coordinates( - M, - eF, - c, - Manifolds.VeeOrthogonalBasis(), - ) + get_vector_lie!(G, Y, c, Manifolds.VeeOrthogonalBasis()) + @test Y ≈ get_vector_lie(G, c, Manifolds.VeeOrthogonalBasis()) + get_vector!(M, Y, identity_element(G), c, Manifolds.VeeOrthogonalBasis()) + @test Y ≈ get_vector(M, identity_element(G), c, Manifolds.VeeOrthogonalBasis()) + + @test get_coordinates(M, identity_element(G), Y, Manifolds.VeeOrthogonalBasis()) == + c c2 = similar(c) - get_coordinates!(G, c2, e, Y, Manifolds.VeeOrthogonalBasis()) + get_coordinates_lie!(G, c2, Y, Manifolds.VeeOrthogonalBasis()) @test c == c2 - @test_throws ErrorException get_coordinates!( - G, - c2, - eF, - Y, - Manifolds.VeeOrthogonalBasis(), - ) - get_coordinates!(M, c2, e, Y, Manifolds.VeeOrthogonalBasis()) + get_coordinates!(M, c2, identity_element(G), Y, Manifolds.VeeOrthogonalBasis()) @test c == c2 - @test_throws ErrorException get_coordinates!( - M, - c2, - eF, - Y, - Manifolds.VeeOrthogonalBasis(), - ) + + q = zeros(3, 3) + mul!(q, e, e) + @test isone(q) + e2 = Identity(G) + @test mul!(e2, e, e) === e2 end end diff --git a/test/groups/translation_group.jl b/test/groups/translation_group.jl index 65b2b3323f..7bf87c28e2 100644 --- a/test/groups/translation_group.jl +++ b/test/groups/translation_group.jl @@ -15,7 +15,7 @@ include("group_utils.jl") Val{true}() types = [Matrix{Float64}] @test base_manifold(G) === Euclidean(2, 3) - + @test log_lie(G, Identity(G)) == zeros(2, 3) # log_lie with Identity on Addition group. pts = [reshape(i:(i + 5), (2, 3)) for i in 1:3] vpts = [reshape(-2:3, (2, 3)), reshape(-1:4, (2, 3))] for T in types diff --git a/test/groups/array_manifold.jl b/test/groups/validation_group.jl similarity index 76% rename from test/groups/array_manifold.jl rename to test/groups/validation_group.jl index 2d3f25b36b..eaacfb369b 100644 --- a/test/groups/array_manifold.jl +++ b/test/groups/validation_group.jl @@ -13,29 +13,24 @@ include("../utils.jl") p, q = [exp(M, eg, hat(M, eg, ωi)) for ωi in ω] X = hat(M, eg, [-1.0, 2.0, 0.5]) - e = make_identity(AG, p) - @test Manifolds.array_value(e).p == Manifolds.array_value(e.p) - @test Manifolds.array_point(e).p == e.p + e = Identity(G) p2, q2 = ValidationMPoint(p), ValidationMPoint(q) @test q2 === Manifolds.array_point(q2) # test that double wraps are avoided. X2 = ValidationTVector(X) - @test identity(AG, p2) isa ValidationMPoint - @test isapprox(G, identity(AG, p2).value, identity(G, p)) - @test identity(AG, e) isa Identity - @test_throws DomainError identity(AG, Identity(TranslationGroup(3), ω[1])) + @test Identity(G) isa Identity + eg = allocate(p) + identity_element!(G, eg) + eg2 = allocate(p2) + identity_element!(AG, eg2) + @test eg2.value == eg eg = allocate(p2) - identity!(AG, eg, p2) - @test isapprox(G, eg.value, identity(G, p)) - eg = allocate(p2) - identity!(AG, eg, e) - @test isapprox(G, eg.value, identity(G, p)) @test inv(AG, p2) isa ValidationMPoint @test isapprox(G, inv(AG, p2).value, inv(G, p)) - @test inv(AG, e) isa Identity - @test_throws DomainError inv(AG, Identity(TranslationGroup(3), ω[1])) + @test inv(AG, e) isa ValidationMPoint{<:Identity} + @test inv(AG, Identity(AG)) == inv(AG, e) pinvq = allocate(p2) inv!(AG, pinvq, p2) @@ -59,17 +54,17 @@ include("../utils.jl") compose!(AG, pq, p2, e) @test isapprox(G, pq.value, compose(G, p, e)) - @test group_exp(AG, X2) isa ValidationMPoint - @test isapprox(G, group_exp(AG, X2).value, group_exp(G, X)) + @test exp_lie(AG, X2) isa ValidationMPoint + @test isapprox(G, exp_lie(AG, X2).value, exp_lie(G, X)) expX = allocate(p2) - group_exp!(AG, expX, X2) - @test isapprox(G, expX.value, group_exp(G, X)) + exp_lie!(AG, expX, X2) + @test isapprox(G, expX.value, exp_lie(G, X)) - @test group_log(AG, p2) isa ValidationTVector - @test isapprox(G, e, group_log(AG, p2).value, group_log(G, p)) + @test log_lie(AG, p2) isa ValidationTVector + @test isapprox(G, e, log_lie(AG, p2).value, log_lie(G, p)) logp = allocate(X2) - group_log!(AG, logp, p2) - @test isapprox(G, e, logp.value, group_log(G, p)) + log_lie!(AG, logp, p2) + @test isapprox(G, e, logp.value, log_lie(G, p)) @test lie_bracket(AG, X, X2) isa ValidationTVector Xlb = allocate(X2) diff --git a/test/recipes.jl b/test/recipes.jl index 5000f232c0..f95ce37f6a 100644 --- a/test/recipes.jl +++ b/test/recipes.jl @@ -1,4 +1,5 @@ -using RecipesBase, VisualRegressionTests, Plots, Colors +using RecipesBase, Plots, Colors +#using VisualRegressionTests, include("utils.jl") # Note that the `false`s avoid popups and the tests directly fail. # If you have changed something and need to recreate the reference in the test avoids to start Gtk @@ -8,130 +9,133 @@ include("utils.jl") @testset "2D Recipes in GR" begin ENV["GKSwstype"] = "100" gr() - function Hyp2PB_plot() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) - p2 = convert.(Ref(PoincareBallPoint), p) - return plot(M, p2) - end - @plottest Hyp2PB_plot joinpath(references_folder, "Hyp2PBPlot.png") false + # function Hyp2PB_plot() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) + p2 = convert.(Ref(PoincareBallPoint), p) + # return + plot(M, p2) + # end + # @plottest Hyp2PB_plot joinpath(references_folder, "Hyp2PBPlot.png") false - function Hyp2PB_plot_geo() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) - p2 = convert.(Ref(PoincareBallPoint), p) - return plot(M, p2; geodesic_interpolation=80) - end - @plottest Hyp2PB_plot_geo joinpath(references_folder, "Hyp2PBPlotGeo.png") false + # function Hyp2PB_plot_geo() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) + p2 = convert.(Ref(PoincareBallPoint), p) + # return + plot(M, p2; geodesic_interpolation=80) + # end + # @plottest Hyp2PB_plot_geo joinpath(references_folder, "Hyp2PBPlotGeo.png") false - function Hyp2PB_quiver() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 0.0], [0.0, 1.0]]) - p2 = convert.(Ref(PoincareBallPoint), p) - X = [log(M, p2[2], p2[1]), log(M, p2[1], p2[3])] - return plot(M, [p2[2], p2[1]], X) - end - @plottest Hyp2PB_quiver joinpath(references_folder, "Hyp2PBQuiver.png") false + # function Hyp2PB_quiver() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 0.0], [0.0, 1.0]]) + p2 = convert.(Ref(PoincareBallPoint), p) + X = [log(M, p2[2], p2[1]), log(M, p2[1], p2[3])] + # return + plot(M, [p2[2], p2[1]], X) + # end + # @plottest Hyp2PB_quiver joinpath(references_folder, "Hyp2PBQuiver.png") false - function Hyp2PH_plot() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) - p2 = convert.(Ref(PoincareHalfSpacePoint), p) - return plot(M, p2) - end - @plottest Hyp2PH_plot joinpath(references_folder, "Hyp2PHPlot.png") false + # function Hyp2PH_plot() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) + p2 = convert.(Ref(PoincareHalfSpacePoint), p) + # return + plot(M, p2) + # end + # @plottest Hyp2PH_plot joinpath(references_folder, "Hyp2PHPlot.png") false - function Hyp2PH_plot_geo() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) - p2 = convert.(Ref(PoincareHalfSpacePoint), p) - return plot(M, p2; geodesic_interpolation=80) - end - @plottest Hyp2PH_plot_geo joinpath(references_folder, "Hyp2PHPlotGeo.png") false + # function Hyp2PH_plot_geo() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) + p2 = convert.(Ref(PoincareHalfSpacePoint), p) + # return + plot(M, p2; geodesic_interpolation=80) + # end + # @plottest Hyp2PH_plot_geo joinpath(references_folder, "Hyp2PHPlotGeo.png") false - function Hyp2PH_quiver() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 0.0], [0.0, 1.0]]) - p2 = convert.(Ref(PoincareHalfSpacePoint), p) - X = [log(M, p2[2], p2[1]), log(M, p2[1], p2[3])] - return plot(M, [p2[2], p2[1]], X) - end - @plottest Hyp2PH_quiver joinpath(references_folder, "Hyp2PHQuiver.png") false + # function Hyp2PH_quiver() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 0.0], [0.0, 1.0]]) + p2 = convert.(Ref(PoincareHalfSpacePoint), p) + X = [log(M, p2[2], p2[1]), log(M, p2[1], p2[3])] + # return + plot(M, [p2[2], p2[1]], X) + # end + # @plottest Hyp2PH_quiver joinpath(references_folder, "Hyp2PHQuiver.png") false end @testset "3D Recipes in GR" begin ENV["GKSwstype"] = "100" gr() - function Hyp2_plot() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) - return plot(M, p) - end - @plottest Hyp2_plot joinpath(references_folder, "Hyp2Plot.png") false + # function Hyp2_plot() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) + # return + plot(M, p) + # end + # @plottest Hyp2_plot joinpath(references_folder, "Hyp2Plot.png") false - function Hyp2_surfplot() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) - return plot(M, p; surface=true) - end - @plottest Hyp2_surfplot joinpath(references_folder, "Hyp2SurfPlot.png") false + # function Hyp2_surfplot() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) + # return + plot(M, p; surface=true) + #end + #@plottest Hyp2_surfplot joinpath(references_folder, "Hyp2SurfPlot.png") false - function Hyp2_plot_geo() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) - return plot(M, p; geodesic_interpolation=80) - end - @plottest Hyp2_plot_geo joinpath(references_folder, "Hyp2PlotGeo.png") false + # function Hyp2_plot_geo() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) + # return + plot(M, p; geodesic_interpolation=80) + # end + #@plottest Hyp2_plot_geo joinpath(references_folder, "Hyp2PlotGeo.png") false - function Hyp2_quiver() - M = Hyperbolic(2) - p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 0.0], [0.0, 1.0]]) - X = [log(M, p[2], p[1]), log(M, p[1], p[3])] - return plot(M, [p[2], p[1]], X) - end - @plottest Hyp2_quiver joinpath(references_folder, "Hyp2Quiver.png") false + # function Hyp2_quiver() + M = Hyperbolic(2) + p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 0.0], [0.0, 1.0]]) + X = [log(M, p[2], p[1]), log(M, p[1], p[3])] + # return + plot(M, [p[2], p[1]], X) + # end + # @plottest Hyp2_quiver joinpath(references_folder, "Hyp2Quiver.png") false end @testset "3D Recipes in pyplot" begin pyplot() - function Sphere2_plot() - M = Sphere(2) - pts = [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]] - return plot(M, pts; wireframe_color=colorant"#CCCCCC", markersize=10) - end - @plottest Sphere2_plot joinpath(references_folder, "Sphere2Plot.png") false + # function Sphere2_plot() + M = Sphere(2) + pts = [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]] + # return + plot(M, pts; wireframe_color=colorant"#CCCCCC", markersize=10) + # end + # @plottest Sphere2_plot joinpath(references_folder, "Sphere2Plot.png") false - function Sphere2_surfplot() - M = Sphere(2) - pts = [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]] - return plot( - M, - pts; - surface=true, - wireframe_color=colorant"#CCCCCC", - markersize=10, - ) - end - @plottest Sphere2_surfplot joinpath(references_folder, "Sphere2SurfPlot.png") false + # function Sphere2_surfplot() + M = Sphere(2) + pts = [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]] + # return + plot(M, pts; surface=true, wireframe_color=colorant"#CCCCCC", markersize=10) + # end + # @plottest Sphere2_surfplot joinpath(references_folder, "Sphere2SurfPlot.png") false - function Sphere2_plot_geo() - M = Sphere(2) - pts = [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]] - return Plots.plot( - M, - pts; - wireframe_color=colorant"#CCCCCC", - geodesic_interpolation=80, - ) - end - @plottest Sphere2_plot_geo joinpath(references_folder, "Sphere2PlotGeo.png") false + # function Sphere2_plot_geo() + M = Sphere(2) + pts = [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]] + # return + Plots.plot(M, pts; wireframe_color=colorant"#CCCCCC", geodesic_interpolation=80) + # end + # @plottest Sphere2_plot_geo joinpath(references_folder, "Sphere2PlotGeo.png") false - function Sphere2_quiver() - pyplot() - M = Sphere(2) - pts2 = [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]] - p3 = 1 / sqrt(3) .* [1.0, -1.0, 1.0] - vecs = log.(Ref(M), pts2, Ref(p3)) - return plot(M, pts2, vecs; wireframe_color=colorant"#CCCCCC", linewidth=1.5) - end - @plottest Sphere2_quiver joinpath(references_folder, "Sphere2Quiver.png") false + # function Sphere2_quiver() + pyplot() + M = Sphere(2) + pts2 = [[1.0, 0.0, 0.0], [0.0, -1.0, 0.0], [0.0, 0.0, 1.0]] + p3 = 1 / sqrt(3) .* [1.0, -1.0, 1.0] + vecs = log.(Ref(M), pts2, Ref(p3)) + # return + plot(M, pts2, vecs; wireframe_color=colorant"#CCCCCC", linewidth=1.5) + # end + # @plottest Sphere2_quiver joinpath(references_folder, "Sphere2Quiver.png") false end end diff --git a/test/runtests.jl b/test/runtests.jl index 61d172e2fd..25e841c377 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -95,6 +95,14 @@ include("utils.jl") x3r = Manifolds.realify(x3, ℂ) @test x2 * x3 ≈ Manifolds.unrealify!(similar(x2), x2r * x3r, ℂ) end + @testset "allocation" begin + @test allocate([1 2; 3 4], Float64, Size(3, 3)) isa Matrix{Float64} + @test allocate(SA[1 2; 3 4], Float64, Size(3, 3)) isa MMatrix{3,3,Float64} + end + @testset "eigen_safe" begin + @test Manifolds.eigen_safe(SA[1.0 0.0; 0.0 1.0]) isa + Eigen{Float64,Float64,<:SizedMatrix{2,2},<:SizedVector{2}} + end end include_test("groups/group_utils.jl") @@ -144,7 +152,7 @@ include("utils.jl") # Lie groups and actions include_test("groups/groups_general.jl") - include_test("groups/array_manifold.jl") + include_test("groups/validation_group.jl") include_test("groups/circle_group.jl") include_test("groups/translation_group.jl") include_test("groups/general_linear.jl") diff --git a/test/utils.jl b/test/utils.jl index 41fe424cbe..5ebe93579e 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -25,8 +25,8 @@ function include_test(path) @time include(path) # show basic timing, (this will print a newline at end) end -function our_base_ambiguities() - ambigs = Test.detect_ambiguities(Base) +function our_ambiguities(m=Base) + ambigs = Test.detect_ambiguities(m) modules_we_care_about = [Base, LinearAlgebra, Manifolds, ManifoldsBase, StaticArrays, Statistics, StatsBase] our_ambigs = filter(ambigs) do (m1, m2)