diff --git a/Project.toml b/Project.toml index 3bb839aba4..5bd20492ce 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.8.66" +version = "0.8.67" [deps] Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" diff --git a/src/groups/product_group.jl b/src/groups/product_group.jl index b64e509377..842dd4af61 100644 --- a/src/groups/product_group.jl +++ b/src/groups/product_group.jl @@ -42,7 +42,7 @@ end function identity_element(G::ProductGroup) M = G.manifold - return ProductRepr(map(identity_element, M.manifolds)) + return ArrayPartition(map(identity_element, M.manifolds)) end function identity_element!(G::ProductGroup, p) pes = submanifold_components(G, p) @@ -194,7 +194,7 @@ end function translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) M = G.manifold - return ProductRepr( + return ArrayPartition( map( translate_diff, M.manifolds, @@ -282,7 +282,7 @@ end function exp_lie(G::ProductGroup, X) M = G.manifold - return ProductRepr(map(exp_lie, M.manifolds, submanifold_components(G, X))...) + return ArrayPartition(map(exp_lie, M.manifolds, submanifold_components(G, X))...) end function exp_lie!(G::ProductGroup, q, X) diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index 11743df2b8..e6ee76dd2d 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -51,14 +51,14 @@ function allocate_result(G::SemidirectProductGroup, ::typeof(identity_element)) N, H = M.manifolds np = allocate_result(N, identity_element) hp = allocate_result(H, identity_element) - return ProductRepr(np, hp) + return ArrayPartition(np, hp) end """ identity_element(G::SemidirectProductGroup) -Get the identity element of [`SemidirectProductGroup`](@ref) `G`. Uses [`ProductRepr`](@ref) -to represent the point. +Get the identity element of [`SemidirectProductGroup`](@ref) `G`. Uses `ArrayPartition` +from `RecursiveArrayTools.jl` to represent the point. """ identity_element(G::SemidirectProductGroup) diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index fceb209e49..7ebdfe136c 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -129,8 +129,7 @@ matrix part of `p`, `r` is the translation part of `fX` and `ω` is the rotation ``×`` is the cross product and ``⋅`` is the matrix product. """ function adjoint_action(::SpecialEuclidean{3}, p, fX::TFVector{<:Any,VeeOrthogonalBasis{ℝ}}) - t = p.parts[1] - R = p.parts[2] + t, R = submanifold_components(p) r = fX.data[SA[1, 2, 3]] ω = fX.data[SA[4, 5, 6]] Rω = R * ω @@ -553,6 +552,7 @@ end """ lie_bracket(G::SpecialEuclidean, X::ProductRepr, Y::ProductRepr) + lie_bracket(G::SpecialEuclidean, X::ArrayPartition, Y::ArrayPartition) lie_bracket(G::SpecialEuclidean, X::AbstractMatrix, Y::AbstractMatrix) Calculate the Lie bracket between elements `X` and `Y` of the special Euclidean Lie @@ -565,6 +565,11 @@ function lie_bracket(G::SpecialEuclidean, X::ProductRepr, Y::ProductRepr) nY, hY = submanifold_components(G, Y) return ProductRepr(hX * nY - hY * nX, lie_bracket(G.manifold.manifolds[2], hX, hY)) end +function lie_bracket(G::SpecialEuclidean, X::ArrayPartition, Y::ArrayPartition) + nX, hX = submanifold_components(G, X) + nY, hY = submanifold_components(G, Y) + return ArrayPartition(hX * nY - hY * nX, lie_bracket(G.manifold.manifolds[2], hX, hY)) +end function lie_bracket(::SpecialEuclidean, X::AbstractMatrix, Y::AbstractMatrix) return X * Y - Y * X end @@ -668,7 +673,7 @@ This is performed by extracting the rotation and translation part as in [`affine function project(M::SpecialEuclideanInGeneralLinear, p) G = M.manifold np, hp = submanifold_components(G, p) - return ProductRepr(np, hp) + return ArrayPartition(np, hp) end """ project(M::SpecialEuclideanInGeneralLinear, p, X) @@ -681,7 +686,7 @@ function project(M::SpecialEuclideanInGeneralLinear, p, X) G = M.manifold np, hp = submanifold_components(G, p) nX, hX = submanifold_components(G, X) - return ProductRepr(hp * nX, hX) + return ArrayPartition(hp * nX, hX) end function project!(M::SpecialEuclideanInGeneralLinear, q, p) diff --git a/src/manifolds/Circle.jl b/src/manifolds/Circle.jl index de34cb1735..c3990d6a18 100644 --- a/src/manifolds/Circle.jl +++ b/src/manifolds/Circle.jl @@ -37,7 +37,7 @@ end function check_point(M::Circle{ℂ}, p; kwargs...) if !isapprox(sum(abs.(p)), 1.0; kwargs...) return DomainError( - abs(p), + sum(abs.(p)), "The point $(p) does not lie on the $(M) since its norm is not 1.", ) end @@ -45,7 +45,7 @@ function check_point(M::Circle{ℂ}, p; kwargs...) end check_size(::Circle, ::Number) = nothing function check_size(M::Circle, p) - (size(p) == ()) && return nothing + (size(p) === () || size(p) === (1,)) && return nothing return DomainError( size(p), "The point $p can not belong to the $M, since it is not a number nor a vector of size (1,).", @@ -53,7 +53,7 @@ function check_size(M::Circle, p) end check_size(::Circle, ::Number, ::Number) = nothing function check_size(M::Circle, p, X) - (size(X) == ()) && return nothing + (size(X) === () || size(p) === (1,)) && return nothing return DomainError( size(X), "The vector $X is not a tangent vector to $p on $M, since it is not a number nor a vector of size (1,).", diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index b66a42acbd..41632ef3ea 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -118,7 +118,7 @@ function InverseProductRetraction(inverse_retractions::AbstractInverseRetraction end @inline function allocate_result(M::ProductManifold, f) - return ProductRepr(map(N -> allocate_result(N, f), M.manifolds)) + return ArrayPartition(map(N -> allocate_result(N, f), M.manifolds)) end function allocation_promotion_function(M::ProductManifold, f, args::Tuple) diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 6ae460ff5a..5c8a56f556 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -306,7 +306,7 @@ base_manifold(B::VectorSpaceAtPoint) = base_manifold(B.fiber) base_manifold(B::VectorBundle) = base_manifold(B.manifold) """ - bundle_projection(B::VectorBundle, x::ProductRepr) + bundle_projection(B::VectorBundle, p::ArrayPartition) Projection of point `p` from the bundle `M` to the base manifold. Returns the point on the base manifold `B.manifold` at which the vector part @@ -466,7 +466,7 @@ end function get_vector(M::VectorBundle, p, X, B::AbstractBasis) n = manifold_dimension(M.manifold) xp1 = submanifold_component(p, Val(1)) - return ProductRepr( + return ArrayPartition( get_vector(M.manifold, xp1, X[1:n], B), get_vector(M.fiber, xp1, X[(n + 1):end], B), ) @@ -488,7 +488,7 @@ function get_vector( ) where {𝔽} n = manifold_dimension(M.manifold) xp1 = submanifold_component(p, Val(1)) - return ProductRepr( + return ArrayPartition( get_vector(M.manifold, xp1, X[1:n], B.data.base_basis), get_vector(M.fiber, xp1, X[(n + 1):end], B.data.vec_basis), ) @@ -1070,7 +1070,7 @@ end return allocate_result(B.manifold, f, x...) end @inline function allocate_result(M::VectorBundle, f::TF) where {TF} - return ProductRepr(allocate_result(M.manifold, f), allocate_result(M.fiber, f)) + return ArrayPartition(allocate_result(M.manifold, f), allocate_result(M.fiber, f)) end """ @@ -1123,7 +1123,7 @@ function _vector_transport_direction( px, pVx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) dx, dVx = submanifold_components(M.manifold, d) - return ProductRepr( + return ArrayPartition( vector_transport_direction(M.manifold, px, VXM, dx, m.method_point), vector_transport_direction(M.manifold, px, VXF, dx, m.method_vector), ) @@ -1174,7 +1174,7 @@ function _vector_transport_to( px, pVx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) qx, qVx = submanifold_components(M.manifold, q) - return ProductRepr( + return ArrayPartition( vector_transport_to(M.manifold, px, VXM, qx, m.method_point), vector_transport_to(M.manifold, px, VXF, qx, m.method_vector), ) diff --git a/src/product_representations.jl b/src/product_representations.jl index 9fcd89304c..6ecfdb82b8 100644 --- a/src/product_representations.jl +++ b/src/product_representations.jl @@ -71,12 +71,25 @@ created as where `[1.0, 0.0, 0.0]` is the part corresponding to the sphere factor and `[2.0, 3.0]` is the part corresponding to the euclidean manifold. + + +!!! warning + + `ProductRepr` is deprecated and will be removed in a future release. + Please use `ArrayPartition` instead. """ struct ProductRepr{TM<:Tuple} parts::TM end -ProductRepr(points...) = ProductRepr{typeof(points)}(points) +function ProductRepr(points...) + Base.depwarn( + "`ProductRepr` will be deprecated in a future release. " * + "Please use `ArrayPartition` instead of `ProductRepr`.", + :ProductRepr, + ) + return ProductRepr{typeof(points)}(points) +end Base.:(==)(x::ProductRepr, y::ProductRepr) = x.parts == y.parts @@ -89,13 +102,17 @@ allocate(x::ProductRepr) = ProductRepr(map(allocate, submanifold_components(x)). function allocate(x::ProductRepr, ::Type{T}) where {T} return ProductRepr(map(t -> allocate(t, T), submanifold_components(x))...) end -allocate(p::ProductRepr, ::Type{T}, s::Size{S}) where {S,T} = Vector{T}(undef, S) -allocate(p::ProductRepr, ::Type{T}, s::Integer) where {T} = Vector{T}(undef, s) +allocate(::ProductRepr, ::Type{T}, s::Size{S}) where {S,T} = Vector{T}(undef, S) +allocate(::ProductRepr, ::Type{T}, s::Integer) where {T} = Vector{T}(undef, s) allocate(a::AbstractArray{<:ProductRepr}) = map(allocate, a) Base.copy(x::ProductRepr) = ProductRepr(map(copy, x.parts)) -function Base.copyto!(x::ProductRepr, y::ProductRepr) +function Base.copyto!(x::ProductRepr, y::Union{ProductRepr,ArrayPartition}) + map(copyto!, submanifold_components(x), submanifold_components(y)) + return x +end +function Base.copyto!(x::ArrayPartition, y::ProductRepr) map(copyto!, submanifold_components(x), submanifold_components(y)) return x end diff --git a/test/groups/metric.jl b/test/groups/metric.jl index d3244c1905..e4abe5dc5c 100644 --- a/test/groups/metric.jl +++ b/test/groups/metric.jl @@ -144,35 +144,37 @@ end ([-2.0, 1.0, 0.5], hat(Rn, p, [-1.0, -0.5, 1.1])), ] - pts = [ProductRepr(tp...) for tp in tuple_pts] - X_pts = [ProductRepr(tX...) for tX in tuple_X] - - g1, g2 = pts[1:2] - t1, R1 = g1.parts - t2, R2 = g2.parts - g1g2 = ProductRepr(R1 * t2 + t1, R1 * R2) - @test isapprox(G, compose(G, g1, g2), g1g2) - - test_group( - G, - pts, - X_pts, - X_pts; - test_diff=true, - test_lie_bracket=true, - test_adjoint_action=true, - diff_convs=[(), (LeftAction(),), (RightAction(),)], - ) - test_manifold( - G, - pts; - #basis_types_vecs=basis_types, - basis_types_to_from=basis_types, - is_mutating=true, - #test_inplace=true, - test_vee_hat=false, - exp_log_atol_multiplier=50, - ) + for prod_type in [ProductRepr, ArrayPartition] + pts = [prod_type(tp...) for tp in tuple_pts] + X_pts = [prod_type(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) + + test_group( + G, + pts, + X_pts, + X_pts; + test_diff=true, + test_lie_bracket=true, + test_adjoint_action=true, + diff_convs=[(), (LeftAction(),), (RightAction(),)], + ) + test_manifold( + G, + pts; + #basis_types_vecs=basis_types, + basis_types_to_from=basis_types, + is_mutating=true, + #test_inplace=true, + test_vee_hat=false, + exp_log_atol_multiplier=50, + ) + end end end end diff --git a/test/groups/semidirect_product_group.jl b/test/groups/semidirect_product_group.jl index 8d37078b67..7a31bf49e7 100644 --- a/test/groups/semidirect_product_group.jl +++ b/test/groups/semidirect_product_group.jl @@ -18,40 +18,42 @@ include("group_utils.jl") ts2 = Vector{Float64}.([1:2, 2:3, 3:4]) .* 10 tuple_pts = [zip(ts1, ts2)...] - pts = [ProductRepr(tp...) for tp in tuple_pts] - - @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] + for prod_type in [ProductRepr, ArrayPartition] + pts = [prod_type(tp...) for tp in tuple_pts] + + @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 + + X = log(G, pts[1], pts[1]) + Y = zero_vector(G, pts[1]) + Z = Manifolds.allocate_result(G, zero_vector, pts[1]) + Z = zero_vector!(M, Z, pts[1]) + @test norm(G, pts[1], X) ≈ 0 + @test norm(G, pts[1], Y) ≈ 0 + @test norm(G, pts[1], Z) ≈ 0 + + e = Identity(G) + @test inv(G, e) === e + + @test compose(G, e, pts[1]) == pts[1] + @test compose(G, pts[1], e) == pts[1] + @test compose(G, e, e) === e + + # test in-place composition + o1 = copy(pts[1]) + compose!(G, o1, o1, pts[2]) + @test isapprox(G, o1, compose(G, pts[1], pts[2])) + + 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 - - X = log(G, pts[1], pts[1]) - Y = zero_vector(G, pts[1]) - Z = Manifolds.allocate_result(G, zero_vector, pts[1]) - Z = zero_vector!(M, Z, pts[1]) - @test norm(G, pts[1], X) ≈ 0 - @test norm(G, pts[1], Y) ≈ 0 - @test norm(G, pts[1], Z) ≈ 0 - - e = Identity(G) - @test inv(G, e) === e - - @test compose(G, e, pts[1]) == pts[1] - @test compose(G, pts[1], e) == pts[1] - @test compose(G, e, e) === e - - # test in-place composition - o1 = copy(pts[1]) - compose!(G, o1, o1, pts[2]) - @test isapprox(G, o1, compose(G, pts[1], pts[2])) - - 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 c580cb0acf..3dddf9a810 100644 --- a/test/groups/special_euclidean.jl +++ b/test/groups/special_euclidean.jl @@ -51,263 +51,272 @@ Random.seed!(10) @test isapprox(G, identity_element(G), Identity(G)) end - @testset "product repr" begin - pts = [ProductRepr(tp...) for tp in tuple_pts] - X_pts = [ProductRepr(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 - - g1, g2 = pts[1:2] - t1, R1 = g1.parts - t2, R2 = g2.parts - g1g2 = ProductRepr(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 - @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) - w2.parts[1] .= w.parts[1] - w2.parts[2] .= pts[1].parts[2] * w.parts[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=[(), (LeftAction(),), (RightAction(),)], - ) - 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=[(), (LeftAction(),), (RightAction(),)], - ) - - test_manifold( - G_TR, - pts; - is_mutating=true, - exp_log_atol_multiplier=50, - test_inner=false, - test_norm=false, - ) + 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 - 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=[(), (LeftAction(),), (RightAction(),)], - ) - test_manifold( - G_TR, - pts; - basis_types_vecs=basis_types, - basis_types_to_from=basis_types, - is_mutating=true, - exp_log_atol_multiplier=50, - ) + 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 + @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=[(), (LeftAction(),), (RightAction(),)], + ) + 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=[(), (LeftAction(),), (RightAction(),)], + ) + + 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=[(), (LeftAction(),), (RightAction(),)], + ) + + 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 end end - end - @testset "affine matrix" begin - pts = [affine_matrix(G, ProductRepr(tp...)) for tp in tuple_pts] - X_pts = [screw_matrix(G, ProductRepr(tX...)) for tX in tuple_X] + @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] - end + @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=[(), (LeftAction(),), (RightAction(),)], - 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 + test_group( + G, + pts, + X_pts, + X_pts; + test_diff=true, + test_lie_bracket=true, + diff_convs=[(), (LeftAction(),), (RightAction(),)], + 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 = ProductRepr(tuple_pts[1]...) - X = ProductRepr(tuple_X[1]...) - Xexp = [X.parts[1]; vee(Rn, p.parts[2], X.parts[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 "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) + end end - end - G = SpecialEuclidean(11) - @test affine_matrix(G, Identity(G)) isa Diagonal{Float64,Vector{Float64}} - @test affine_matrix(G, Identity(G)) == Diagonal(ones(11)) + G = SpecialEuclidean(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) - t = Vector{Float64}.([1:3, 2:4, 4:6]) - ω = [[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) - pts = [ProductRepr(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] - X = ProductRepr([-1.0, 2.0, 1.0], hat(Rn, p, [1.0, 0.5, -0.5])) - q = ProductRepr([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 [LeftAction(), RightAction()] - 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), - ) + @testset "Explicit embedding in GL(n+1)" begin + G = SpecialEuclidean(3) + t = Vector{Float64}.([1:3, 2:4, 4:6]) + ω = [[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 [LeftAction(), RightAction()] + 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 end @@ -317,14 +326,16 @@ 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) - pts = [ProductRepr(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] - X = ProductRepr([-1.0, 2.0, 1.0], hat(Rn, p, [1.0, 0.5, -0.5])) - q = ProductRepr([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) + 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 end end diff --git a/test/manifolds/circle.jl b/test/manifolds/circle.jl index 7783a6a1e1..866815f43d 100644 --- a/test/manifolds/circle.jl +++ b/test/manifolds/circle.jl @@ -15,6 +15,8 @@ using Manifolds: TFVector, CoTFVector @test is_flat(M) @test !is_point(M, 9.0) @test !is_point(M, zeros(3, 3)) + @test Manifolds.check_size(M, [9.0]) === nothing + @test Manifolds.check_size(M, [1.0], [-2.0]) === nothing @test_throws DomainError is_point(M, 9.0, true) @test_throws DomainError is_point(M, zeros(3, 3), true) @test !is_vector(M, 9.0, 0.0) @@ -240,7 +242,7 @@ using Manifolds: TFVector, CoTFVector test_vee_hat=false, exp_log_atol_multiplier=2.0, is_tangent_atol_multiplier=2.0, - rand_tvector_atol_multiplier=1.0, + rand_tvector_atol_multiplier=2.0, test_rand_point=true, test_rand_tvector=true, ) @@ -254,7 +256,7 @@ using Manifolds: TFVector, CoTFVector test_vee_hat=true, exp_log_atol_multiplier=2.0, is_tangent_atol_multiplier=2.0, - rand_tvector_atol_multiplier=1.0, + rand_tvector_atol_multiplier=2.0, basis_types_vecs=basis_types, basis_types_to_from=basis_types, test_rand_point=true, diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index 4f25b5de35..db37291fc5 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -442,27 +442,38 @@ end SE2 = SpecialEuclidean(2) PSE2 = PowerManifold(SE2, NestedReplacingPowerRepresentation(), 2) - pse = ProductRepr( - SA[1.0, 2.0], - SA[ - 0.5403023058681398 -0.8414709848078965 - 0.8414709848078965 0.5403023058681398 - ], - ) - p2 = [pse, pse] - @test allocate(PSE2, p2) isa - Vector{ProductRepr{Tuple{SVector{2,Float64},SMatrix{2,2,Float64,4}}}} - - pse_ap = ArrayPartition( - SA[1.0, 2.0], - SA[ - 0.5403023058681398 -0.8414709848078965 - 0.8414709848078965 0.5403023058681398 - ], - ) - p2_ap = [pse_ap, pse_ap] - @test allocate(PSE2, p2_ap) isa Vector{ - ArrayPartition{Float64,Tuple{SVector{2,Float64},SMatrix{2,2,Float64,4}}}, - } + for prod_type in [ProductRepr, ArrayPartition] + pse = prod_type( + SA[1.0, 2.0], + SA[ + 0.5403023058681398 -0.8414709848078965 + 0.8414709848078965 0.5403023058681398 + ], + ) + p2 = [pse, pse] + if prod_type === ProductRepr + @test allocate(PSE2, p2) isa + Vector{ProductRepr{Tuple{SVector{2,Float64},SMatrix{2,2,Float64,4}}}} + else + @test allocate(PSE2, p2) isa Vector{ + ArrayPartition{ + Float64, + Tuple{SVector{2,Float64},SMatrix{2,2,Float64,4}}, + }, + } + end + + pse_ap = ArrayPartition( + SA[1.0, 2.0], + SA[ + 0.5403023058681398 -0.8414709848078965 + 0.8414709848078965 0.5403023058681398 + ], + ) + p2_ap = [pse_ap, pse_ap] + @test allocate(PSE2, p2_ap) isa Vector{ + ArrayPartition{Float64,Tuple{SVector{2,Float64},SMatrix{2,2,Float64,4}}}, + } + end end end diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index d133306a81..684460d73c 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -101,6 +101,10 @@ using RecursiveArrayTools: ArrayPartition @test p1.parts[1][1] == 0.0 copyto!(p1c, p1) @test p1c.parts[1][1] == 0.0 + + p1c.parts[1][1] = -123.0 + copyto!(p1ap, p1c) + @test p1ap.x[1][1] == -123.0 end @testset "some ArrayPartition functions" begin @@ -284,112 +288,113 @@ using RecursiveArrayTools: ArrayPartition pts_r2 = [[0.0, 0.0], [1.0, 0.0], [0.0, 0.1]] angles = (0.0, π / 2, 2π / 3) pts_rot = [[cos(ϕ) sin(ϕ); -sin(ϕ) cos(ϕ)] for ϕ in angles] - pts = [ProductRepr(p[1], p[2], p[3]) for p in zip(pts_sphere, pts_r2, pts_rot)] - test_manifold( - Mser, - pts, - test_injectivity_radius=false, - is_tangent_atol_multiplier=1, - exp_log_atol_multiplier=1, - test_inplace=true, - test_rand_point=true, - test_rand_tvector=true, - ) - - @testset "product vector transport" begin - p = ProductRepr([1.0, 0.0, 0.0], [0.0, 0.0]) - q = ProductRepr([0.0, 1.0, 0.0], [2.0, 0.0]) - X = log(Mse, p, q) - m = ProductVectorTransport(ParallelTransport(), ParallelTransport()) - Y = vector_transport_to(Mse, p, X, q, m) - Z = -log(Mse, q, p) - @test isapprox(Mse, q, Y, Z) - end + for prod_type in [ProductRepr, ArrayPartition] + pts = [prod_type(p[1], p[2], p[3]) for p in zip(pts_sphere, pts_r2, pts_rot)] + test_manifold( + Mser, + pts, + test_injectivity_radius=false, + is_tangent_atol_multiplier=1, + exp_log_atol_multiplier=1, + test_inplace=true, + test_rand_point=true, + test_rand_tvector=true, + ) - @testset "Implicit product vector transport" begin - p = ProductRepr([1.0, 0.0, 0.0], [0.0, 0.0]) - q = ProductRepr([0.0, 1.0, 0.0], [2.0, 0.0]) - X = log(Mse, p, q) - for m in [ParallelTransport(), SchildsLadderTransport(), PoleLadderTransport()] + @testset "product vector transport" begin + p = prod_type([1.0, 0.0, 0.0], [0.0, 0.0]) + q = prod_type([0.0, 1.0, 0.0], [2.0, 0.0]) + X = log(Mse, p, q) + m = ProductVectorTransport(ParallelTransport(), ParallelTransport()) Y = vector_transport_to(Mse, p, X, q, m) - Z1 = vector_transport_to( + Z = -log(Mse, q, p) + @test isapprox(Mse, q, Y, Z) + end + + @testset "Implicit product vector transport" begin + p = prod_type([1.0, 0.0, 0.0], [0.0, 0.0]) + q = prod_type([0.0, 1.0, 0.0], [2.0, 0.0]) + X = log(Mse, p, q) + for m in [ParallelTransport(), SchildsLadderTransport(), PoleLadderTransport()] + Y = vector_transport_to(Mse, p, X, q, m) + Z1 = vector_transport_to( + Mse.manifolds[1], + submanifold_component.([p, X, q], Ref(1))..., + m, + ) + Z2 = vector_transport_to( + Mse.manifolds[2], + submanifold_component.([p, X, q], Ref(2))..., + m, + ) + Z = prod_type(Z1, Z2) + @test isapprox(Mse, q, Y, Z) + Y2 = allocate(Mse, Y) + vector_transport_to!(Mse, Y2, p, X, q, m) + @test isapprox(Mse, q, Y2, Z) + end + for m in [ParallelTransport(), SchildsLadderTransport(), PoleLadderTransport()] + Y = vector_transport_direction(Mse, p, X, X, m) + Z1 = vector_transport_direction( + Mse.manifolds[1], + submanifold_component.([p, X, X], Ref(1))..., + m, + ) + Z2 = vector_transport_direction( + Mse.manifolds[2], + submanifold_component.([p, X, X], Ref(2))..., + m, + ) + Z = prod_type(Z1, Z2) + @test isapprox(Mse, q, Y, Z) + end + end + @testset "Parallel transport" begin + p = prod_type([1.0, 0.0, 0.0], [0.0, 0.0]) + q = prod_type([0.0, 1.0, 0.0], [2.0, 0.0]) + X = log(Mse, p, q) + # to + Y = parallel_transport_to(Mse, p, X, q) + Z1 = parallel_transport_to( Mse.manifolds[1], submanifold_component.([p, X, q], Ref(1))..., - m, ) - Z2 = vector_transport_to( + Z2 = parallel_transport_to( Mse.manifolds[2], submanifold_component.([p, X, q], Ref(2))..., - m, ) - Z = ProductRepr(Z1, Z2) + Z = prod_type(Z1, Z2) @test isapprox(Mse, q, Y, Z) - Y2 = allocate(Mse, Y) - vector_transport_to!(Mse, Y2, p, X, q, m) - @test isapprox(Mse, q, Y2, Z) - end - for m in [ParallelTransport(), SchildsLadderTransport(), PoleLadderTransport()] - Y = vector_transport_direction(Mse, p, X, X, m) - Z1 = vector_transport_direction( + Ym = allocate(Y) + parallel_transport_to!(Mse, Ym, p, X, q) + @test isapprox(Mse, q, Y, Z) + + # direction + Y = parallel_transport_direction(Mse, p, X, X) + Z1 = parallel_transport_direction( Mse.manifolds[1], submanifold_component.([p, X, X], Ref(1))..., - m, ) - Z2 = vector_transport_direction( + Z2 = parallel_transport_direction( Mse.manifolds[2], submanifold_component.([p, X, X], Ref(2))..., - m, ) - Z = ProductRepr(Z1, Z2) + Z = prod_type(Z1, Z2) @test isapprox(Mse, q, Y, Z) + Ym = allocate(Y) + parallel_transport_direction!(Mse, Ym, p, X, X) + @test isapprox(Mse, q, Ym, Z) end end - @testset "Parallel transport" begin - p = ProductRepr([1.0, 0.0, 0.0], [0.0, 0.0]) - q = ProductRepr([0.0, 1.0, 0.0], [2.0, 0.0]) - X = log(Mse, p, q) - # to - Y = parallel_transport_to(Mse, p, X, q) - Z1 = parallel_transport_to( - Mse.manifolds[1], - submanifold_component.([p, X, q], Ref(1))..., - ) - Z2 = parallel_transport_to( - Mse.manifolds[2], - submanifold_component.([p, X, q], Ref(2))..., - ) - Z = ProductRepr(Z1, Z2) - @test isapprox(Mse, q, Y, Z) - Ym = allocate(Y) - parallel_transport_to!(Mse, Ym, p, X, q) - @test isapprox(Mse, q, Y, Z) - - # direction - Y = parallel_transport_direction(Mse, p, X, X) - Z1 = parallel_transport_direction( - Mse.manifolds[1], - submanifold_component.([p, X, X], Ref(1))..., - ) - Z2 = parallel_transport_direction( - Mse.manifolds[2], - submanifold_component.([p, X, X], Ref(2))..., - ) - Z = ProductRepr(Z1, Z2) - @test isapprox(Mse, q, Y, Z) - Ym = allocate(Y) - parallel_transport_direction!(Mse, Ym, p, X, X) - @test isapprox(Mse, q, Ym, Z) - end @testset "ProductRepr" begin @test (@inferred convert( ProductRepr{Tuple{T,Float64,T} where T}, ProductRepr(9, 10, 11), )) == ProductRepr(9, 10.0, 11) - @test (@inferred convert(ProductRepr, ProductRepr(9, 10, 11))) === - ProductRepr(9, 10, 11) - p = ProductRepr([1.0, 0.0, 0.0], [0.0, 0.0]) + @test (@inferred convert(ProductRepr, p)) === p + @test p == ProductRepr([1.0, 0.0, 0.0], [0.0, 0.0]) @test submanifold_component(Mse, p, 1) === p.parts[1] @test submanifold_component(Mse, p, Val(1)) === p.parts[1] @@ -581,7 +586,7 @@ using RecursiveArrayTools: ArrayPartition @testset "empty allocation" begin p = allocate_result(Mse, uniform_distribution) - @test isa(p, ProductRepr) + @test isa(p, ArrayPartition) @test size(p[Mse, 1]) == (3,) @test size(p[Mse, 2]) == (2,) end @@ -593,36 +598,38 @@ using RecursiveArrayTools: ArrayPartition @test is_point(Mss, rand(uniform_distribution(Mss, p))) end - @testset "Atlas & Induced Basis" begin - M = ProductManifold(Euclidean(2), Euclidean(2)) - p = ProductRepr(zeros(2), ones(2)) - X = ProductRepr(ones(2), 2 .* ones(2)) - A = RetractionAtlas() - a = get_parameters(M, A, p, p) - p2 = get_point(M, A, p, a) - @test all(p2.parts .== p.parts) - end + for TP in [ProductRepr, ArrayPartition] + @testset "Atlas & Induced Basis" begin + M = ProductManifold(Euclidean(2), Euclidean(2)) + p = TP(zeros(2), ones(2)) + X = TP(ones(2), 2 .* ones(2)) + A = RetractionAtlas() + a = get_parameters(M, A, p, p) + p2 = get_point(M, A, p, a) + @test all(submanifold_components(p2) .== submanifold_components(p)) + end - @testset "metric conversion" begin - M = SymmetricPositiveDefinite(3) - N = ProductManifold(M, M) - e = EuclideanMetric() - p = [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1] - q = [2.0 0.0 0.0; 0.0 2.0 0.0; 0.0 0.0 1] - P = ProductRepr(p, q) - X = ProductRepr(log(M, p, q), log(M, q, p)) - Y = change_metric(N, e, P, X) - Yc = ProductRepr( - change_metric(M, e, p, log(M, p, q)), - change_metric(M, e, q, log(M, q, p)), - ) - @test norm(N, P, Y - Yc) ≈ 0 - Z = change_representer(N, e, P, X) - Zc = ProductRepr( - change_representer(M, e, p, log(M, p, q)), - change_representer(M, e, q, log(M, q, p)), - ) - @test norm(N, P, Z - Zc) ≈ 0 + @testset "metric conversion" begin + M = SymmetricPositiveDefinite(3) + N = ProductManifold(M, M) + e = EuclideanMetric() + p = [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1] + q = [2.0 0.0 0.0; 0.0 2.0 0.0; 0.0 0.0 1] + P = TP(p, q) + X = TP(log(M, p, q), log(M, q, p)) + Y = change_metric(N, e, P, X) + Yc = TP( + change_metric(M, e, p, log(M, p, q)), + change_metric(M, e, q, log(M, q, p)), + ) + @test norm(N, P, Y - Yc) ≈ 0 + Z = change_representer(N, e, P, X) + Zc = TP( + change_representer(M, e, p, log(M, p, q)), + change_representer(M, e, q, log(M, q, p)), + ) + @test norm(N, P, Z - Zc) ≈ 0 + end end @testset "default retraction, inverse retraction and VT" begin