diff --git a/NEWS.md b/NEWS.md index ab21d8b049..a2829afd23 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,15 +5,48 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.9.0] +## [0.9.0] - 2023-mm-dd ### Added - Vector bundles are generalized to fiber bundles. +- `RotationTranslationAction`. +- `DirectSumType` for vector bundles: `MultitangentBundle`, `MultitangentBundleFibers`, `MultitangentSpaceAtPoint`. ### Changed - Sizes of all manifolds can now be either encoded in type or stored in a field to avoid over-specialization. The default is set to store the size in a field. To obtain the old behavior, pass the `parameter=:type` keyword argument to manifold constructor. Related changes: - - `SpecialEuclidean{N}` renamed to `StaticSpecialEuclidean{N}`. + - Statically sized `SpecialEuclidean{N}` is now `SpecialEuclidean{TypeParameter{Tuple{N}}}`, whereas the type of special Euclidean group with field-stored size is `SpecialEuclidean{Tuple{Int}}`. Similar change applies to `GeneralUnitaryMultiplicationGroup{n}`, `Orthogonal{n}`, `SpecialOrthogonal{n}`, `SpecialUnitary{n}`, `SpecialEuclideanManifold{n}`, `TranslationGroup`. For example + + ```{julia} + function Base.show(io::IO, ::SpecialEuclidean{n}) where {n} + return print(io, "SpecialEuclidean($(n))") + end + ``` + + needs to be replaced with + + ```{julia} + function Base.show(io::IO, ::SpecialEuclidean{TypeParameter{Tuple{n}}}) where {n} + return print(io, "SpecialEuclidean($(n); parameter=:type)") + end + ``` + + for statically-sized groups and + + ```{julia} + function Base.show(io::IO, G::SpecialEuclidean{Tuple{Int}}) + n = get_n(G) + return print(io, "SpecialEuclidean($(n))") + end + ``` + + for groups with size stored in field. +- Argument order for type alias `RotationActionOnVector`: most often dispatched on argument is now first. + +### Removed + +- `ProductRepr` is removed; please use `ArrayPartition` instead. +- Default methods throwing "not implemented" `ErrorException` for some group-related operations. \ No newline at end of file diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index 6060dae02d..bd63c21939 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -10,16 +10,14 @@ abstract type AbstractGroupAction{AD<:ActionDirection} end The group that acts in action `A`. """ -base_group(A::AbstractGroupAction) = error("base_group not implemented for $(typeof(A)).") +base_group(A::AbstractGroupAction) """ group_manifold(A::AbstractGroupAction) The manifold the action `A` acts upon. """ -function group_manifold(A::AbstractGroupAction) - return error("group_manifold not implemented for $(typeof(A)).") -end +group_manifold(A::AbstractGroupAction) function allocate_result(A::AbstractGroupAction, f, p...) return allocate_result(group_manifold(A), f, p...) @@ -65,11 +63,7 @@ end Apply action `a` to the point `p` with the rule specified by `A`. The result is saved in `q`. """ -function apply!(A::AbstractGroupAction{LeftForwardAction}, q, a, p) - return error( - "apply! not implemented for action $(typeof(A)) and points $(typeof(q)), $(typeof(p)) and $(typeof(a)).", - ) -end +apply!(A::AbstractGroupAction, q, a, p) function apply!(A::AbstractGroupAction{RightForwardAction}, q, a, p) ainv = inv(base_group(A), a) apply!(switch_direction(A, LeftRightSwitch()), q, ainv, p) @@ -111,17 +105,7 @@ differential transports vectors (\mathrm{d}τ_a)_p : T_p \mathcal M → T_{τ_a p} \mathcal M ```` """ -function apply_diff(A::AbstractGroupAction, a, p, X) - return error( - "apply_diff not implemented for action $(typeof(A)), points $(typeof(a)) and $(typeof(p)), and vector $(typeof(X))", - ) -end - -function apply_diff!(A::AbstractGroupAction, Y, a, p, X) - return error( - "apply_diff! not implemented for action $(typeof(A)), points $(typeof(a)) and $(typeof(p)), vectors $(typeof(Y)) and $(typeof(X))", - ) -end +apply_diff(A::AbstractGroupAction, a, p, X) @doc raw""" apply_diff_group(A::AbstractGroupAction, a, X, p) @@ -208,11 +192,7 @@ the element closest to `q` in the metric of the G-manifold: ``` where $\mathcal{G}$ is the group that acts on the G-manifold $\mathcal M$. """ -function optimal_alignment(A::AbstractGroupAction, p, q) - return error( - "optimal_alignment not implemented for $(typeof(A)) and points $(typeof(p)) and $(typeof(q)).", - ) -end +optimal_alignment(A::AbstractGroupAction, p, q) """ optimal_alignment!(A::AbstractGroupAction, x, p, q) diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index fe13682fdb..79f9771ddb 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -27,7 +27,7 @@ function Base.show(io::IO, A::RotationAction) end const RotationActionOnVector{TAD,𝔽,TE,TSO} = RotationAction{ - Euclidean{TE,𝔽}, + <:Union{Euclidean{TE,𝔽},TranslationGroup{TE,𝔽}}, SpecialOrthogonal{TSO}, TAD, } where {TAD<:ActionDirection,𝔽,TE,TSO} diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 0439605779..44e189bfad 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -35,7 +35,7 @@ const SpecialEuclidean{T} = SemidirectProductGroup{ const SpecialEuclideanManifold{N} = ProductManifold{ℝ,Tuple{TranslationGroup{N,ℝ},SpecialOrthogonal{N}}} -function SpecialEuclidean(n; parameter::Symbol=:type) +function SpecialEuclidean(n; parameter::Symbol=:field) Tn = TranslationGroup(n; parameter=parameter) SOn = SpecialOrthogonal(n; parameter=parameter) A = RotationAction(Tn, SOn) @@ -43,12 +43,12 @@ function SpecialEuclidean(n; parameter::Symbol=:type) end const SpecialEuclideanOperation{N} = SemidirectProductOperation{ - RotationAction{TranslationGroup{Tuple{N},ℝ},SpecialOrthogonal{N},LeftForwardAction}, + RotationAction{TranslationGroup{N,ℝ},SpecialOrthogonal{N},LeftForwardAction}, } const SpecialEuclideanIdentity{N} = Identity{SpecialEuclideanOperation{N}} function Base.show(io::IO, ::SpecialEuclidean{TypeParameter{Tuple{n}}}) where {n} - return print(io, "SpecialEuclidean($(n); field=:type)") + return print(io, "SpecialEuclidean($(n); parameter=:type)") end function Base.show(io::IO, G::SpecialEuclidean{Tuple{Int}}) n = get_n(G) @@ -60,7 +60,9 @@ end end get_n(::SpecialEuclidean{TypeParameter{Tuple{N}}}) where {N} = N -get_n(M::SpecialEuclidean{Tuple{Int}}) = manifold_dimension(M.manifold.manifolds[1]) +get_n(M::SpecialEuclidean{Tuple{Int}}) = get_n(M.manifold) +get_n(::SpecialEuclideanManifold{TypeParameter{Tuple{N}}}) where {N} = N +get_n(M::SpecialEuclideanManifold{Tuple{Int}}) = manifold_dimension(M.manifolds[1]) Base.@propagate_inbounds function Base.getindex( p::AbstractMatrix, @@ -121,7 +123,7 @@ Base.@propagate_inbounds function _padpoint!( end Base.@propagate_inbounds function _padvector!( - ::Union{SpecialEuclidean,SpecialEuclideanManifold}, + G::Union{SpecialEuclidean,SpecialEuclideanManifold}, X::AbstractMatrix, ) n = get_n(G) @@ -132,7 +134,7 @@ Base.@propagate_inbounds function _padvector!( end @doc raw""" - adjoint_action(::SpecialEuclidean{3}, p, fX::TFVector{<:Any,VeeOrthogonalBasis{ℝ}}) + adjoint_action(::SpecialEuclidean{TypeParameter{Tuple{3}}}, p, fX::TFVector{<:Any,VeeOrthogonalBasis{ℝ}}) Adjoint action of the [`SpecialEuclidean`](@ref) group on the vector with coefficients `fX` tangent at point `p`. @@ -142,7 +144,11 @@ The formula for the coefficients reads ``t×(R⋅ω) + R⋅r`` for the translati matrix part of `p`, `r` is the translation part of `fX` and `ω` is the rotation part of `fX`, ``×`` is the cross product and ``⋅`` is the matrix product. """ -function adjoint_action(::SpecialEuclidean{3}, p, fX::TFVector{<:Any,VeeOrthogonalBasis{ℝ}}) +function adjoint_action( + ::SpecialEuclidean{TypeParameter{Tuple{3}}}, + p, + fX::TFVector{<:Any,VeeOrthogonalBasis{ℝ}}, +) t, R = submanifold_components(p) r = fX.data[SA[1, 2, 3]] ω = fX.data[SA[4, 5, 6]] @@ -178,12 +184,18 @@ function affine_matrix(G::SpecialEuclidean, p) return pmat end affine_matrix(::SpecialEuclidean, p::AbstractMatrix) = p -function affine_matrix(::SpecialEuclidean{n}, ::SpecialEuclideanIdentity{n}) where {n} +function affine_matrix( + ::SpecialEuclidean{TypeParameter{Tuple{n}}}, + ::SpecialEuclideanIdentity{TypeParameter{Tuple{n}}}, +) where {n} s = maybesize(Size(n, n)) s isa Size && return SDiagonal{n,Float64}(I) return Diagonal{Float64}(I, n) end -function affine_matrix(::SpecialEuclidean{Tuple{Int}}, ::SpecialEuclideanIdentity) +function affine_matrix( + G::SpecialEuclidean{Tuple{Int}}, + ::SpecialEuclideanIdentity{Tuple{Int}}, +) n = get_n(G) return Diagonal{Float64}(I, n) end diff --git a/src/manifolds/GeneralUnitaryMatrices.jl b/src/manifolds/GeneralUnitaryMatrices.jl index 72db9235d9..8b8a349b15 100644 --- a/src/manifolds/GeneralUnitaryMatrices.jl +++ b/src/manifolds/GeneralUnitaryMatrices.jl @@ -488,7 +488,7 @@ function get_vector_orthogonal!( return X .= 0 end function get_vector_orthogonal!( - M::GeneralUnitaryMatrices{TypeParameter{2},ℝ}, + M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, X, p, Xⁱ, diff --git a/test/groups/groups_general.jl b/test/groups/groups_general.jl index 5ca867fa9f..a243964ff5 100644 --- a/test/groups/groups_general.jl +++ b/test/groups/groups_general.jl @@ -305,17 +305,17 @@ struct NotImplementedAction <: AbstractGroupAction{LeftForwardAction} end a = [1.0, 2.0] X = [1.0, 2.0] - @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, p, p) - @test_throws ErrorException optimal_alignment!(A, a, p, p) + @test_throws MethodError apply(A, a, p) + @test_throws MethodError apply!(A, p, a, p) + @test_throws MethodError inverse_apply(A, a, p) + @test_throws MethodError inverse_apply!(A, p, a, p) + @test_throws MethodError apply_diff(A, a, p, X) + @test_throws MethodError apply_diff!(A, X, p, a, X) + @test_throws MethodError inverse_apply_diff(A, a, p, X) + @test_throws MethodError inverse_apply_diff!(A, X, p, a, X) + @test_throws MethodError compose(A, a, a) + @test_throws MethodError compose!(A, a, a, a) + @test_throws MethodError optimal_alignment(A, p, p) + @test_throws MethodError optimal_alignment!(A, a, p, p) end end diff --git a/test/groups/special_euclidean.jl b/test/groups/special_euclidean.jl index db3c36e68e..bc2a1d833a 100644 --- a/test/groups/special_euclidean.jl +++ b/test/groups/special_euclidean.jl @@ -15,11 +15,17 @@ Random.seed!(10) @test isa(G, SpecialEuclidean{TypeParameter{Tuple{n}}}) end - @test repr(G) == "SpecialEuclidean($n)" + if se_parameter === :field + @test repr(G) == "SpecialEuclidean($n)" + else + @test repr(G) == "SpecialEuclidean($n; parameter=:type)" + end M = base_manifold(G) - @test M === TranslationGroup(n) × SpecialOrthogonal(n) - @test submanifold(G, 1) === TranslationGroup(n) - @test submanifold(G, 2) === SpecialOrthogonal(n) + @test M === + TranslationGroup(n; parameter=se_parameter) × + SpecialOrthogonal(n; parameter=se_parameter) + @test submanifold(G, 1) === TranslationGroup(n; parameter=se_parameter) + @test submanifold(G, 2) === SpecialOrthogonal(n; parameter=se_parameter) Rn = Rotations(n) p = Matrix(I, n, n) @@ -58,221 +64,221 @@ Random.seed!(10) @test isapprox(G, identity_element(G), Identity(G)) end - for prod_type in [ProductRepr, ArrayPartition] - @testset "product repr" begin - pts = [prod_type(tp...) for tp in tuple_pts] - X_pts = [prod_type(tX...) for tX in tuple_X] - - @testset "setindex! and getindex" begin - p1 = pts[1] - p2 = allocate(p1) - @test p1[G, 1] === p1[M, 1] - p2[G, 1] = p1[M, 1] - @test p2[G, 1] == p1[M, 1] - end + @testset "product repr" begin + pts = [ArrayPartition(tp...) for tp in tuple_pts] + X_pts = [ArrayPartition(tX...) for tX in tuple_X] - g1, g2 = pts[1:2] - t1, R1 = submanifold_components(g1) - t2, R2 = submanifold_components(g2) - g1g2 = prod_type(R1 * t2 + t1, R1 * R2) - @test isapprox(G, compose(G, g1, g2), g1g2) - g1g2mat = affine_matrix(G, g1g2) - @test g1g2mat ≈ affine_matrix(G, g1) * affine_matrix(G, g2) - @test affine_matrix(G, g1g2mat) === g1g2mat + @testset "setindex! and getindex" begin + p1 = pts[1] + p2 = allocate(p1) + @test p1[G, 1] === p1[M, 1] + p2[G, 1] = p1[M, 1] + @test p2[G, 1] == p1[M, 1] + end + + g1, g2 = pts[1:2] + t1, R1 = submanifold_components(g1) + t2, R2 = submanifold_components(g2) + g1g2 = ArrayPartition(R1 * t2 + t1, R1 * R2) + @test isapprox(G, compose(G, g1, g2), g1g2) + g1g2mat = affine_matrix(G, g1g2) + @test g1g2mat ≈ affine_matrix(G, g1) * affine_matrix(G, g2) + @test affine_matrix(G, g1g2mat) === g1g2mat + if se_parameter === :type @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], Identity(G), X_pts[1]) - w2 = allocate(w) - submanifold_component(w2, 1) .= submanifold_component(w, 1) - submanifold_component(w2, 2) .= - submanifold_component(pts[1], 2) * submanifold_component(w, 2) - w2mat = screw_matrix(G, w2) - @test w2mat ≈ affine_matrix(G, pts[1]) * screw_matrix(G, X_pts[1]) - @test screw_matrix(G, w2mat) === w2mat - - test_group( - G, - pts, - X_pts, - X_pts; - test_diff=true, - test_lie_bracket=true, - test_adjoint_action=true, - test_exp_from_identity=true, - test_log_from_identity=true, - test_vee_hat_from_identity=true, - diff_convs=[(), (LeftForwardAction(),), (RightBackwardAction(),)], - ) - test_manifold( - G, - pts; - basis_types_vecs=basis_types, - basis_types_to_from=basis_types, - is_mutating=true, - #test_inplace=true, - test_vee_hat=true, - exp_log_atol_multiplier=50, - ) + end + @test affine_matrix(G, Identity(G)) == SDiagonal{n,Float64}(I) + + w = translate_diff(G, pts[1], Identity(G), X_pts[1]) + w2 = allocate(w) + submanifold_component(w2, 1) .= submanifold_component(w, 1) + submanifold_component(w2, 2) .= + submanifold_component(pts[1], 2) * submanifold_component(w, 2) + w2mat = screw_matrix(G, w2) + @test w2mat ≈ affine_matrix(G, pts[1]) * screw_matrix(G, X_pts[1]) + @test screw_matrix(G, w2mat) === w2mat + + test_group( + G, + pts, + X_pts, + X_pts; + test_diff=true, + test_lie_bracket=true, + test_adjoint_action=true, + test_exp_from_identity=true, + test_log_from_identity=true, + test_vee_hat_from_identity=true, + diff_convs=[(), (LeftForwardAction(),), (RightBackwardAction(),)], + ) + test_manifold( + G, + pts; + basis_types_vecs=basis_types, + basis_types_to_from=basis_types, + is_mutating=true, + #test_inplace=true, + test_vee_hat=true, + exp_log_atol_multiplier=50, + ) + + for CS in + [CartanSchoutenMinus(), CartanSchoutenPlus(), CartanSchoutenZero()] + @testset "$CS" begin + G_TR = ConnectionManifold(G, CS) + + test_group( + G_TR, + pts, + X_pts, + X_pts; + test_diff=true, + test_lie_bracket=true, + test_adjoint_action=true, + diff_convs=[ + (), + (LeftForwardAction(),), + (RightBackwardAction(),), + ], + ) - for CS in - [CartanSchoutenMinus(), CartanSchoutenPlus(), CartanSchoutenZero()] - @testset "$CS" begin - G_TR = ConnectionManifold(G, CS) - - test_group( - G_TR, - pts, - X_pts, - X_pts; - test_diff=true, - test_lie_bracket=true, - test_adjoint_action=true, - diff_convs=[ - (), - (LeftForwardAction(),), - (RightBackwardAction(),), - ], - ) - - test_manifold( - G_TR, - pts; - is_mutating=true, - exp_log_atol_multiplier=50, - test_inner=false, - test_norm=false, - ) - end - end - for MM in [LeftInvariantMetric()] - @testset "$MM" begin - G_TR = MetricManifold(G, MM) - @test base_group(G_TR) === G - - test_group( - G_TR, - pts, - X_pts, - X_pts; - test_diff=true, - test_lie_bracket=true, - test_adjoint_action=true, - diff_convs=[ - (), - (LeftForwardAction(),), - (RightBackwardAction(),), - ], - ) - - test_manifold( - G_TR, - pts; - basis_types_vecs=basis_types, - basis_types_to_from=basis_types, - is_mutating=true, - exp_log_atol_multiplier=50, - ) - end + test_manifold( + G_TR, + pts; + is_mutating=true, + exp_log_atol_multiplier=50, + test_inner=false, + test_norm=false, + ) end end + for MM in [LeftInvariantMetric()] + @testset "$MM" begin + G_TR = MetricManifold(G, MM) + @test base_group(G_TR) === G + + test_group( + G_TR, + pts, + X_pts, + X_pts; + test_diff=true, + test_lie_bracket=true, + test_adjoint_action=true, + diff_convs=[ + (), + (LeftForwardAction(),), + (RightBackwardAction(),), + ], + ) - @testset "affine matrix" begin - pts = [affine_matrix(G, prod_type(tp...)) for tp in tuple_pts] - X_pts = [screw_matrix(G, prod_type(tX...)) for tX in tuple_X] - - @testset "setindex! and getindex" begin - p1 = pts[1] - p2 = allocate(p1) - @test p1[G, 1] === p1[M, 1] - p2[G, 1] = p1[M, 1] - @test p2[G, 1] == p1[M, 1] + test_manifold( + G_TR, + pts; + basis_types_vecs=basis_types, + basis_types_to_from=basis_types, + is_mutating=true, + exp_log_atol_multiplier=50, + ) end - - test_group( - G, - pts, - X_pts, - X_pts; - test_diff=true, - test_lie_bracket=true, - diff_convs=[(), (LeftForwardAction(),), (RightBackwardAction(),)], - atol=1e-9, - ) - test_manifold( - G, - pts; - is_mutating=true, - #test_inplace=true, - test_vee_hat=true, - exp_log_atol_multiplier=50, - ) - # specific affine tests - p = copy(G, pts[1]) - X = copy(G, p, X_pts[1]) - X[n + 1, n + 1] = 0.1 - @test_throws DomainError is_vector(G, p, X, true) - X2 = zeros(n + 2, n + 2) - # nearly correct just too large (and the error from before) - X2[1:n, 1:n] .= X[1:n, 1:n] - X2[1:n, end] .= X[1:n, end] - X2[end, end] = X[end, end] - @test_throws DomainError is_vector(G, p, X2, true) - p[n + 1, n + 1] = 0.1 - @test_throws DomainError is_point(G, p, true) - p2 = zeros(n + 2, n + 2) - # nearly correct just too large (and the error from before) - p2[1:n, 1:n] .= p[1:n, 1:n] - p2[1:n, end] .= p[1:n, end] - p2[end, end] = p[end, end] - @test_throws DomainError 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 + end + + @testset "affine matrix" begin + pts = [affine_matrix(G, ArrayPartition(tp...)) for tp in tuple_pts] + X_pts = [screw_matrix(G, ArrayPartition(tX...)) for tX in tuple_X] - @testset "hat/vee" begin - p = prod_type(tuple_pts[1]...) - X = prod_type(tuple_X[1]...) - Xexp = [ - submanifold_component(X, 1) - vee(Rn, submanifold_component(p, 2), submanifold_component(X, 2)) - ] - Xc = vee(G, p, X) - @test Xc ≈ Xexp - @test isapprox(G, p, hat(G, p, Xc), X) - - Xc = vee(G, affine_matrix(G, p), screw_matrix(G, X)) - @test Xc ≈ Xexp - @test hat(G, affine_matrix(G, p), Xc) ≈ screw_matrix(G, X) - - e = Identity(G) - Xe = log_lie(G, p) - Xc = vee(G, e, Xe) - @test_throws ErrorException vee(M, e, Xe) - w = similar(Xc) - vee!(G, w, e, Xe) - @test isapprox(Xc, w) - @test_throws ErrorException vee!(M, w, e, Xe) - - w = similar(Xc) - vee!(G, w, identity_element(G), Xe) - @test isapprox(Xc, w) - - Ye = hat(G, e, Xc) - @test_throws ErrorException hat(M, e, Xc) - isapprox(G, e, Xe, Ye) - Ye2 = copy(G, p, X) - hat!(G, Ye2, e, Xc) - @test_throws ErrorException hat!(M, Ye, e, Xc) - @test isapprox(G, e, Ye, Ye2) - - Ye2 = copy(G, p, X) - hat!(G, Ye2, identity_element(G), Xc) - @test isapprox(G, e, Ye, Ye2) + @testset "setindex! and getindex" begin + p1 = pts[1] + p2 = allocate(p1) + @test p1[G, 1] === p1[M, 1] + p2[G, 1] = p1[M, 1] + @test p2[G, 1] == p1[M, 1] end + + test_group( + G, + pts, + X_pts, + X_pts; + test_diff=true, + test_lie_bracket=true, + diff_convs=[(), (LeftForwardAction(),), (RightBackwardAction(),)], + atol=1e-9, + ) + test_manifold( + G, + pts; + is_mutating=true, + #test_inplace=true, + test_vee_hat=true, + exp_log_atol_multiplier=50, + ) + # specific affine tests + p = copy(G, pts[1]) + X = copy(G, p, X_pts[1]) + X[n + 1, n + 1] = 0.1 + @test_throws DomainError is_vector(G, p, X, true) + X2 = zeros(n + 2, n + 2) + # nearly correct just too large (and the error from before) + X2[1:n, 1:n] .= X[1:n, 1:n] + X2[1:n, end] .= X[1:n, end] + X2[end, end] = X[end, end] + @test_throws DomainError is_vector(G, p, X2, true) + p[n + 1, n + 1] = 0.1 + @test_throws DomainError is_point(G, p, true) + p2 = zeros(n + 2, n + 2) + # nearly correct just too large (and the error from before) + p2[1:n, 1:n] .= p[1:n, 1:n] + p2[1:n, end] .= p[1:n, end] + p2[end, end] = p[end, end] + @test_throws DomainError 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 + p = ArrayPartition(tuple_pts[1]...) + X = ArrayPartition(tuple_X[1]...) + Xexp = [ + submanifold_component(X, 1) + vee(Rn, submanifold_component(p, 2), submanifold_component(X, 2)) + ] + Xc = vee(G, p, X) + @test Xc ≈ Xexp + @test isapprox(G, p, hat(G, p, Xc), X) + + Xc = vee(G, affine_matrix(G, p), screw_matrix(G, X)) + @test Xc ≈ Xexp + @test hat(G, affine_matrix(G, p), Xc) ≈ screw_matrix(G, X) + + e = Identity(G) + Xe = log_lie(G, p) + Xc = vee(G, e, Xe) + @test_throws ErrorException vee(M, e, Xe) + w = similar(Xc) + vee!(G, w, e, Xe) + @test isapprox(Xc, w) + @test_throws ErrorException vee!(M, w, e, Xe) + + w = similar(Xc) + vee!(G, w, identity_element(G), Xe) + @test isapprox(Xc, w) + + Ye = hat(G, e, Xc) + @test_throws ErrorException hat(M, e, Xc) + isapprox(G, e, Xe, Ye) + Ye2 = copy(G, p, X) + hat!(G, Ye2, e, Xc) + @test_throws ErrorException hat!(M, Ye, e, Xc) + @test isapprox(G, e, Ye, Ye2) + + Ye2 = copy(G, p, X) + hat!(G, Ye2, identity_element(G), Xc) + @test isapprox(G, e, Ye, Ye2) end G = SpecialEuclidean(11) @@ -285,54 +291,52 @@ Random.seed!(10) ω = [[1.0, 2.0, 3.0], [3.0, 2.0, 1.0], [1.0, 3.0, 2.0]] p = Matrix(I, 3, 3) Rn = Rotations(3) - for prod_type in [ProductRepr, ArrayPartition] - pts = [ - prod_type(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω) - ] - X = prod_type([-1.0, 2.0, 1.0], hat(Rn, p, [1.0, 0.5, -0.5])) - q = prod_type([0.0, 0.0, 0.0], p) - - GL = GeneralLinear(4) - SEGL = EmbeddedManifold(G, GL) - @test Manifolds.SpecialEuclideanInGeneralLinear(3) === SEGL - pts_gl = [embed(SEGL, pp) for pp in pts] - q_gl = embed(SEGL, q) - X_gl = embed(SEGL, pts_gl[1], X) - - q_gl2 = allocate(q_gl) - embed!(SEGL, q_gl2, q) - @test isapprox(SEGL, q_gl2, q_gl) - - q2 = allocate(q) - project!(SEGL, q2, q_gl) - @test isapprox(G, q, q2) - - @test isapprox(G, pts[1], project(SEGL, pts_gl[1])) - @test isapprox(G, pts[1], X, project(SEGL, pts_gl[1], X_gl)) - - X_gl2 = allocate(X_gl) - embed!(SEGL, X_gl2, pts_gl[1], X) - @test isapprox(SEGL, pts_gl[1], X_gl2, X_gl) - - X2 = allocate(X) - project!(SEGL, X2, pts_gl[1], X_gl) - @test isapprox(G, pts[1], X, X2) - - for conv in [LeftForwardAction(), RightBackwardAction()] - tpgl = translate(GL, pts_gl[2], pts_gl[1], conv) - tXgl = translate_diff(GL, pts_gl[2], pts_gl[1], X_gl, conv) - tpse = translate(G, pts[2], pts[1], conv) - tXse = translate_diff(G, pts[2], pts[1], X, conv) - @test isapprox(G, tpse, project(SEGL, tpgl)) - @test isapprox(G, tpse, tXse, project(SEGL, tpgl, tXgl)) - - @test isapprox( - G, - pts_gl[1], - X_gl, - translate_diff(G, Identity(G), pts_gl[1], X_gl, conv), - ) - end + pts = [ + ArrayPartition(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω) + ] + X = ArrayPartition([-1.0, 2.0, 1.0], hat(Rn, p, [1.0, 0.5, -0.5])) + q = ArrayPartition([0.0, 0.0, 0.0], p) + + GL = GeneralLinear(4) + SEGL = EmbeddedManifold(G, GL) + @test Manifolds.SpecialEuclideanInGeneralLinear(3) === SEGL + pts_gl = [embed(SEGL, pp) for pp in pts] + q_gl = embed(SEGL, q) + X_gl = embed(SEGL, pts_gl[1], X) + + q_gl2 = allocate(q_gl) + embed!(SEGL, q_gl2, q) + @test isapprox(SEGL, q_gl2, q_gl) + + q2 = allocate(q) + project!(SEGL, q2, q_gl) + @test isapprox(G, q, q2) + + @test isapprox(G, pts[1], project(SEGL, pts_gl[1])) + @test isapprox(G, pts[1], X, project(SEGL, pts_gl[1], X_gl)) + + X_gl2 = allocate(X_gl) + embed!(SEGL, X_gl2, pts_gl[1], X) + @test isapprox(SEGL, pts_gl[1], X_gl2, X_gl) + + X2 = allocate(X) + project!(SEGL, X2, pts_gl[1], X_gl) + @test isapprox(G, pts[1], X, X2) + + for conv in [LeftForwardAction(), RightBackwardAction()] + tpgl = translate(GL, pts_gl[2], pts_gl[1], conv) + tXgl = translate_diff(GL, pts_gl[2], pts_gl[1], X_gl, conv) + tpse = translate(G, pts[2], pts[1], conv) + tXse = translate_diff(G, pts[2], pts[1], X, conv) + @test isapprox(G, tpse, project(SEGL, tpgl)) + @test isapprox(G, tpse, tXse, project(SEGL, tpgl, tXgl)) + + @test isapprox( + G, + pts_gl[1], + X_gl, + translate_diff(G, Identity(G), pts_gl[1], X_gl, conv), + ) end end end @@ -344,16 +348,14 @@ Random.seed!(10) ω = [[1.0, 2.0, 3.0], [3.0, 2.0, 1.0], [1.0, 3.0, 2.0]] p = Matrix(I, 3, 3) Rn = Rotations(3) - for prod_type in [ProductRepr, ArrayPartition] - pts = [prod_type(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] - X = prod_type([-1.0, 2.0, 1.0], hat(Rn, p, [1.0, 0.5, -0.5])) - q = prod_type([0.0, 0.0, 0.0], p) - - # adjoint action of SE(3) - fX = TFVector(vee(G, q, X), VeeOrthogonalBasis()) - fXp = adjoint_action(G, pts[1], fX) - fXp2 = adjoint_action(G, pts[1], X) - @test isapprox(G, pts[1], hat(G, pts[1], fXp.data), fXp2) - end + pts = [ArrayPartition(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] + X = ArrayPartition([-1.0, 2.0, 1.0], hat(Rn, p, [1.0, 0.5, -0.5])) + q = ArrayPartition([0.0, 0.0, 0.0], p) + + # adjoint action of SE(3) + fX = TFVector(vee(G, q, X), VeeOrthogonalBasis()) + fXp = adjoint_action(G, pts[1], fX) + fXp2 = adjoint_action(G, pts[1], X) + @test isapprox(G, pts[1], hat(G, pts[1], fXp.data), fXp2) end end diff --git a/test/manifolds/euclidean.jl b/test/manifolds/euclidean.jl index 09d82943a6..e2daf17cdb 100644 --- a/test/manifolds/euclidean.jl +++ b/test/manifolds/euclidean.jl @@ -10,13 +10,13 @@ using FiniteDifferences EM = Manifolds.MetricManifold(E, Manifolds.EuclideanMetric()) EH = Euclidean(2, 3; field=ℍ, parameter=param) if param === :type + @test repr(E) == "Euclidean(3; field = ℝ, parameter = :type)" + @test repr(Ec) == "Euclidean(3; field = ℂ, parameter = :type)" + @test repr(EH) == "Euclidean(2, 3; field = ℍ, parameter = :type)" + else @test repr(E) == "Euclidean(3; field = ℝ)" @test repr(Ec) == "Euclidean(3; field = ℂ)" @test repr(EH) == "Euclidean(2, 3; field = ℍ)" - else - @test repr(E) == "Euclidean(3; field = ℝ, parameter = :field)" - @test repr(Ec) == "Euclidean(3; field = ℂ, parameter = :field)" - @test repr(EH) == "Euclidean(2, 3; field = ℍ, parameter = :field)" end @test Manifolds.allocation_promotion_function(Ec, get_vector, ()) === complex @@ -389,7 +389,10 @@ using FiniteDifferences @test rH == H end @testset "Volume" begin - @test manifold_volume(Euclidean(2)) == Inf + E = Euclidean(3) + @test manifold_volume(E) == Inf + p = zeros(3) + X = zeros(3) @test volume_density(E, p, X) == 1.0 end end