From fe4f1f7d1b4f99683af00c9e29e52e31fad09331 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 10 Jul 2023 21:56:22 +0200 Subject: [PATCH 01/81] Add action of SE(n) on R^n --- docs/src/manifolds/group.md | 8 + ext/ManifoldsTestExt/tests_group.jl | 4 +- src/Manifolds.jl | 3 + src/groups/rotation_action.jl | 2 +- src/groups/rotation_translation_action.jl | 313 +++++++++++++++++++++ test/groups/rotation_translation_action.jl | 113 ++++++++ test/runtests.jl | 1 + 7 files changed, 441 insertions(+), 3 deletions(-) create mode 100644 src/groups/rotation_translation_action.jl create mode 100644 test/groups/rotation_translation_action.jl diff --git a/docs/src/manifolds/group.md b/docs/src/manifolds/group.md index 4ae63967f9..b2251b76be 100644 --- a/docs/src/manifolds/group.md +++ b/docs/src/manifolds/group.md @@ -235,6 +235,14 @@ Pages = ["groups/translation_action.jl"] Order = [:type, :function] ``` +### Rotation-translation action (special Euclidean) + +```@autodocs +Modules = [Manifolds] +Pages = ["groups/rotation_translation_action.jl"] +Order = [:type, :function] +``` + ## Metrics on groups Lie groups by default typically forward all metric-related operations like exponential or logarithmic map to the underlying manifold, for example [`SpecialOrthogonal`](@ref) uses methods for [`Rotations`](@ref) (which is, incidentally, bi-invariant), or [`SpecialEuclidean`](@ref) uses product metric of the translation and rotation parts (which is not invariant under group operation). diff --git a/ext/ManifoldsTestExt/tests_group.jl b/ext/ManifoldsTestExt/tests_group.jl index 5c833e0445..48c41a7427 100644 --- a/ext/ManifoldsTestExt/tests_group.jl +++ b/ext/ManifoldsTestExt/tests_group.jl @@ -741,10 +741,10 @@ function test_action( eX = allocate(X) Test.@test apply_diff!(A, eX, e, m, X) === eX - Test.@test isapprox(G, m, eX, X; atol=atol) + Test.@test isapprox(M, m, eX, X; atol=atol) eX = allocate(X) Test.@test inverse_apply_diff!(A, eX, e, m, X) === eX - Test.@test isapprox(G, m, eX, X; atol=atol) + Test.@test isapprox(M, m, eX, X; atol=atol) end end end diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 647e2f2717..5c61f2a0d2 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -463,6 +463,8 @@ include("groups/rotation_action.jl") include("groups/special_euclidean.jl") +include("groups/rotation_translation_action.jl") + # final utilities include("trait_recursion_breaking.jl") @@ -880,6 +882,7 @@ export AbstractGroupAction, RightForwardAction, RightInvariantMetric, RotationAction, + RotationTranslationAction, SemidirectProductGroup, SpecialEuclidean, SpecialLinear, diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index a601d3e568..8b13a72bc5 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -30,7 +30,7 @@ const RotationActionOnVector{N,F,TAD} = RotationAction{ <:Union{Euclidean{Tuple{N},F},TranslationGroup{Tuple{N},F}}, SpecialOrthogonal{N}, TAD, -} +} where {TAD<:ActionDirection} base_group(A::RotationAction) = A.SOn diff --git a/src/groups/rotation_translation_action.jl b/src/groups/rotation_translation_action.jl new file mode 100644 index 0000000000..7045ac8dc5 --- /dev/null +++ b/src/groups/rotation_translation_action.jl @@ -0,0 +1,313 @@ +@doc raw""" + RotationTranslationAction( + M::AbstractManifold, + SOn::SpecialEuclidean, + AD::ActionDirection = LeftForwardAction(), + ) + +Space of actions of the [`SpecialEuclidean`](@ref) group $\mathrm{SE}(n)$ on a +Euclidean-like manifold `M` of dimension `n`. + +Left forward action corresponds to active transformations while right forward actions +can be identified with passive transformations for a particular choice of a basis. +""" +struct RotationTranslationAction{ + TM<:AbstractManifold, + TSE<:SpecialEuclidean, + TAD<:ActionDirection, +} <: AbstractGroupAction{TAD} + manifold::TM + SEn::TSE +end + +function RotationTranslationAction( + M::AbstractManifold, + SEn::SpecialEuclidean, + ::TAD=LeftForwardAction(), +) where {TAD<:ActionDirection} + return RotationTranslationAction{typeof(M),typeof(SEn),TAD}(M, SEn) +end + +function Base.show(io::IO, A::RotationTranslationAction) + return print(io, "RotationTranslationAction($(A.manifold), $(A.SEn), $(direction(A)))") +end + +const RotationTranslationActionOnVector{N,F,TAD} = RotationTranslationAction{ + <:Union{Euclidean{Tuple{N},F},TranslationGroup{Tuple{N},F}}, + SpecialEuclidean{N}, + TAD, +} where {TAD<:ActionDirection} + +base_group(A::RotationTranslationAction) = A.SEn + +group_manifold(A::RotationTranslationAction) = A.manifold + +function switch_direction( + A::RotationTranslationAction{TM,TSO,TAD}, + ::LeftRightSwitch=LeftRightSwitch(), +) where {TM<:AbstractManifold,TSO<:SpecialEuclidean,TAD<:ActionDirection} + return RotationTranslationAction( + A.manifold, + A.SEn, + switch_direction(TAD(), LeftRightSwitch()), + ) +end + +function apply( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + a::ArrayPartition, + p, +) where {N,F} + return a.x[2] * p + a.x[1] +end +function apply( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + a::SpecialEuclideanIdentity{N}, + p, +) where {N,F} + return p +end +function apply( + ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + a::ArrayPartition, + p, +) where {N,F} + return a.x[2] \ (p - a.x[1]) +end +function apply( + ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + a::SpecialEuclideanIdentity{N}, + p, +) where {N,F} + return p +end + +function apply!( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + q, + a::ArrayPartition, + p, +) where {N,F} + mul!(q, a.x[2], p) + q .+= a.x[1] + return q +end +function apply!( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + q, + a::SpecialEuclideanIdentity{N}, + p, +) where {N,F} + copyto!(q, p) + return q +end + +function inverse_apply( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + a::ArrayPartition, + p, +) where {N,F} + return a.x[2] \ (p - a.x[1]) +end +function inverse_apply( + ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + a::ArrayPartition, + p, +) where {N,F} + return a.x[2] * p + a.x[1] +end + +function apply_diff( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + a::ArrayPartition, + p, + X, +) where {N,F} + return a.x[2] * X +end +function apply_diff( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::SpecialEuclideanIdentity{N}, + p, + X, +) where {N,F} + return X +end +function apply_diff( + ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + a::ArrayPartition, + p, + X, +) where {N,F} + return a.x[2] \ X +end +function apply_diff( + ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + a::SpecialEuclideanIdentity{N}, + p, + X, +) where {N,F} + return X +end + +function apply_diff!( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + Y, + a::ArrayPartition, + p, + X, +) where {N,F} + return mul!(Y, a.x[2], X) +end +function apply_diff!( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + Y, + a::SpecialEuclideanIdentity{N}, + p, + X, +) where {N,F} + return copyto!(Y, X) +end +function apply_diff!( + ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + Y, + a::ArrayPartition, + p, + X, +) where {N,F} + Y .= a.x[2] \ X + return Y +end +function apply_diff!( + ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + Y, + a::SpecialEuclideanIdentity{N}, + p, + X, +) where {N,F} + return copyto!(Y, X) +end + +function apply_diff_group( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::SpecialEuclideanIdentity{N}, + X, + p, +) where {N,F} + return X.x[2] * p +end + +function apply_diff_group!( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + Y, + ::SpecialEuclideanIdentity{N}, + X::ArrayPartition, + p, +) where {N,F} + Y .= X.x[2] * p + return Y +end + +function inverse_apply_diff( + ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + a::ArrayPartition, + p, + X, +) where {N,F} + return a.x[2] \ X +end +function inverse_apply_diff( + ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + a::ArrayPartition, + p, + X, +) where {N,F} + return a.x[2] * X +end + +### + +@doc raw""" + ColumnwiseSpecialEuclideanAction{ + TM<:AbstractManifold, + TSE<:SpecialEuclidean, + TAD<:ActionDirection, + } <: AbstractGroupAction{TAD} + +Action of the special Euclidean group [`SpecialEuclidean`](@ref) +of type `SE` columns of points on a matrix manifold `M`. + +# Constructor + + ColumnwiseSpecialEuclideanAction( + M::AbstractManifold, + SE::SpecialEuclidean, + AD::ActionDirection = LeftForwardAction(), + ) +""" +struct ColumnwiseSpecialEuclideanAction{ + TM<:AbstractManifold, + TSE<:SpecialEuclidean, + TAD<:ActionDirection, +} <: AbstractGroupAction{TAD} + manifold::TM + SE::TSE +end + +function ColumnwiseSpecialEuclideanAction( + M::AbstractManifold, + SE::SpecialEuclidean, + ::TAD=LeftForwardAction(), +) where {TAD<:ActionDirection} + return ColumnwiseSpecialEuclideanAction{typeof(M),typeof(SE),TAD}(M, SE) +end + +const LeftColumnwiseSpecialEuclideanAction{TM<:AbstractManifold,TSE<:SpecialEuclidean} = + ColumnwiseSpecialEuclideanAction{TM,TSE,LeftForwardAction} + +function apply(::LeftColumnwiseSpecialEuclideanAction, a::ArrayPartition, p) + return a.x[2] * p .+ a.x[1] +end +function apply(::LeftColumnwiseSpecialEuclideanAction, ::SpecialEuclideanIdentity, p) + return p +end + +function apply!(::LeftColumnwiseSpecialEuclideanAction, q, a::ArrayPartition, p) + map((qrow, prow) -> mul!(qrow, a.x[2], prow), eachcol(q), eachcol(p)) + q .+= a.x[1] + return q +end +function apply!(::LeftColumnwiseSpecialEuclideanAction, q, a::SpecialEuclideanIdentity, p) + copyto!(q, p) + return q +end + +base_group(A::LeftColumnwiseSpecialEuclideanAction) = A.SE + +group_manifold(A::LeftColumnwiseSpecialEuclideanAction) = A.manifold + +function inverse_apply(::LeftColumnwiseSpecialEuclideanAction, a::ArrayPartition, p) + return a.x[2] \ (p .- a.x[1]) +end + +@doc raw""" + optimal_alignment(A::LeftColumnwiseSpecialEuclideanAction, p, q) + +Compute optimal alignment of `p` to `q` under the [`LeftColumnwiseSpecialEuclideanAction`](@ref). +The algorithm, in sequence, computes optimal translation and optimal rotation +""" +function optimal_alignment( + A::LeftColumnwiseSpecialEuclideanAction{<:AbstractManifold,<:SpecialEuclidean{N}}, + p, + q, +) where {N} + tr_opt = mean(q; dims=1) - mean(p; dims=1) + p_moved = p + tr_opt + + Ostar = optimal_alignment( + ColumnwiseMultiplicationAction(A.manifold, SpecialOrthogonal(N)), + p_moved, + q, + ) + return ArrayPartition(tr_opt, Ostar) +end diff --git a/test/groups/rotation_translation_action.jl b/test/groups/rotation_translation_action.jl new file mode 100644 index 0000000000..6949026a15 --- /dev/null +++ b/test/groups/rotation_translation_action.jl @@ -0,0 +1,113 @@ + +include("../utils.jl") +include("group_utils.jl") + +@testset "Rotation action" begin + G = SpecialEuclidean(2) + M = base_manifold(G) + A_left = RotationTranslationAction(Euclidean(2), G) + A_right = RotationTranslationAction(Euclidean(2), G, RightForwardAction()) + + @test repr(A_left) == + "RotationTranslationAction($(repr(Euclidean(2))), $(repr(G)), LeftForwardAction())" + @test repr(A_right) == + "RotationTranslationAction($(repr(Euclidean(2))), $(repr(G)), RightForwardAction())" + + types_a = [ArrayPartition{Float64,Tuple{Vector{Float64},Matrix{Float64}}}] + + types_m = [Vector{Float64}] + + @test group_manifold(A_left) == Euclidean(2) + @test base_group(A_left) == G + @test isa(A_left, AbstractGroupAction{<:LeftForwardAction}) + @test base_manifold(G) == M + + for (i, T_A, T_M) in zip(1:length(types_a), types_a, types_m) + angles = (0.0, π / 2, 2π / 3, π / 4) + translations = [[1.0, 0.0], [0.0, -2.0], [-1.0, 2.0]] + a_pts = + convert.( + T_A, + [ + ArrayPartition(t, [cos(ϕ) -sin(ϕ); sin(ϕ) cos(ϕ)]) for + (t, ϕ) in zip(translations, angles) + ], + ) + a_X_pts = map(a -> log_lie(G, a), a_pts) + m_pts = convert.(T_M, [[0.0, 1.0], [-1.0, 0.0], [1.0, 1.0]]) + X_pts = convert.(T_M, [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]) + + atol = if eltype(T_M) == Float32 + 2e-7 + else + 1e-15 + end + test_action( + A_left, + a_pts, + m_pts, + X_pts; + test_optimal_alignment=false, + test_diff=true, + atol=atol, + ) + + test_action( + A_right, + a_pts, + m_pts, + X_pts; + test_optimal_alignment=false, + test_diff=true, + atol=atol, + ) + + @testset "apply_diff_group" begin + @test apply_diff_group(A_left, Identity(G), a_X_pts[1], m_pts[1]) ≈ + a_X_pts[1].x[2] * m_pts[1] + Y = similar(m_pts[1]) + apply_diff_group!(A_left, Y, Identity(G), a_X_pts[1], m_pts[1]) + @test Y ≈ a_X_pts[1].x[2] * m_pts[1] + end + end +end + +@testset "Matrix columnwise multiplication action" begin + M = Euclidean(2, 3) + G = SpecialEuclidean(2) + p1 = [ + 0.4385117672460505 -0.6877826444042382 0.24927087715818771 + -0.3830259932279294 0.35347460720654283 0.029551386021386548 + ] + p2 = [ + -0.42693314765896473 -0.3268567431952937 0.7537898908542584 + 0.3054740561061169 -0.18962848284149897 -0.11584557326461796 + ] + A = Manifolds.ColumnwiseSpecialEuclideanAction(M, G) + + @test group_manifold(A) === M + @test base_group(A) === SpecialEuclidean(2) + + a = ArrayPartition( + [1.0, 2.0], + [0.5851302132737501 -0.8109393525500014; 0.8109393525500014 0.5851302132737504], + ) + @test isapprox( + apply(A, a, p1), + [ + 1.567197334849809 0.3109111254828243 1.1218915396673668 + 2.1314863675092206 1.6490786599533187 2.2194349725374605 + ], + ) + @test isapprox( + inverse_apply(A, a, p1), + [ + -2.2610332854401007 -2.3228048546690494 -2.0371886150121097 + -0.9390476037204165 0.40525761065242144 -0.5441732289245019 + ], + ) + @test apply(A, Identity(G), p1) === p1 + q = similar(p1) + apply!(A, q, a, p1) + @test isapprox(q, apply(A, a, p1)) +end diff --git a/test/runtests.jl b/test/runtests.jl index 372bf55221..f9ff618ed4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -208,6 +208,7 @@ include("utils.jl") include_test("groups/group_operation_action.jl") include_test("groups/rotation_action.jl") include_test("groups/translation_action.jl") + include_test("groups/rotation_translation_action.jl") include_test("groups/connections.jl") include_test("groups/metric.jl") end From 6ed9547d89c75557f4e3bfca83eee6cbafd4bd00 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 11 Jul 2023 17:27:36 +0200 Subject: [PATCH 02/81] some docs --- docs/make.jl | 5 +- src/groups/rotation_translation_action.jl | 2 +- tutorials/rigid-body-dynamics.qmd | 127 ++++++++++++++++++++++ 3 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 tutorials/rigid-body-dynamics.qmd diff --git a/docs/make.jl b/docs/make.jl index 211b581203..1dd48b59fd 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -87,7 +87,10 @@ makedocs( sitename="Manifolds.jl", pages=[ "Home" => "index.md", - "How to..." => ["🚀 Get Started with `Manifolds.jl`" => "tutorials/getstarted.md"], + "How to..." => [ + "🚀 Get Started with `Manifolds.jl`" => "tutorials/getstarted.md", + "Do rigid body dynamics with geometry" => "tutorials/rigid-body-dynamics.md", + ], "Manifolds" => [ "Basic manifolds" => [ "Centered matrices" => "manifolds/centeredmatrices.md", diff --git a/src/groups/rotation_translation_action.jl b/src/groups/rotation_translation_action.jl index 7045ac8dc5..4c588b948c 100644 --- a/src/groups/rotation_translation_action.jl +++ b/src/groups/rotation_translation_action.jl @@ -293,7 +293,7 @@ end @doc raw""" optimal_alignment(A::LeftColumnwiseSpecialEuclideanAction, p, q) -Compute optimal alignment of `p` to `q` under the [`LeftColumnwiseSpecialEuclideanAction`](@ref). +Compute optimal alignment of `p` to `q` under the forward left [`ColumnwiseSpecialEuclideanAction`](@ref). The algorithm, in sequence, computes optimal translation and optimal rotation """ function optimal_alignment( diff --git a/tutorials/rigid-body-dynamics.qmd b/tutorials/rigid-body-dynamics.qmd new file mode 100644 index 0000000000..bb5cf75174 --- /dev/null +++ b/tutorials/rigid-body-dynamics.qmd @@ -0,0 +1,127 @@ +--- +title: Rigid body dynamics +--- + +In this tutorial we will learn how rigid body dynamics relates to concepts from Riemannian geometry and Lie groups. + +```{julia} +#| echo: false +#| code-fold: true +#| output: false +using Pkg; +cd(@__DIR__) +Pkg.activate("."); # for reproducibility use the local tutorial environment. +using Markdown +``` + +In this tutorial we need to load a few libraries: +```{julia} +using Manifolds, LinearAlgebra, StaticArrays, RecursiveArrayTools, Plots +``` + +## 2-dimensional introduction + +We will start by considering 2-D rigid bodies. They are characterized by three numbers: position along $x$ and $x$ axes and an angle. +Geometrically it can be represented by elements of the special euclidean group in 2 dimensions, $\operatorname{SE}(2)$: + +```{julia} +SE2 = SpecialEuclidean(2) +se2_id = identity_element(SE2) +R2 = Euclidean(2) +``` + +For example, an object at $(2, 5)$ and at angle $\pi/3$ is represented by +```{julia} +p = exp(SE2, se2_id, hat(SE2, se2_id, [2.0, 5.0, pi/3])) +``` + +What does it do, exactly? This is one of many ways of creating a points on $\operatorname{SE}(2)$. +We can clearly recognize the numbers representing position and orientation of our object. +The `hat` function converts them to the Lie algebra of $\operatorname{SE}(2)$, denoted $\mathfrak{se}(2)$, while `exp` turns an element of $\mathfrak{se}(2)$ to an element of $\operatorname{SE}(2)$. +There are many ways of mapping between these two structures but `exp` is the one we need at the moment. + +Now we will draw our object as an arrow, with position represented by the central-left point in the default orientation. + +```{julia} +function draw_arrow_at(p; plt=nothing, arrow_kwargs=(;)) + if plt === nothing + plt = plot(; aspect_ratio=:equal) + end + # constructing matrix where columns are vertices of our arrow + pts = reduce(hcat, [[0.0, -1], [2.0, -1], [2.0, -2], [4.0, 0], [2.0, 2], [2.0, 1], [0.0, 1], [0.0, -1]]) + # action that transforms the arrow + A = Manifolds.ColumnwiseSpecialEuclideanAction(R2, SE2) + # applying the transformation + pts_pos = apply(A, p, pts) + # drawing the arrow + arr = Shape(pts_pos[1, :], pts_pos[2, :]) + plot!(plt, arr; arrow_kwargs...) +end +plt1 = plot(; aspect_ratio=:equal) +draw_arrow_at(se2_id; plt=plt1, arrow_kwargs=(; label="default position")) +draw_arrow_at(p; plt=plt1, arrow_kwargs=(; label="arrow at p")) +plt1 +``` + +### Simple transformations in global coordinates + +There are several ways of transforming position and orientation of an object. +The first one translates and rotates an object by composition. +In the following example, translation by $(-1, -2)$ and rotation by $-\pi/6$ are applied. + +```{julia} +p_tr = exp(SE2, se2_id, hat(SE2, se2_id, [-1.0, -2.0, -pi/6])) +p_transformed = compose(SE2, p_tr, p) +plt2 = plot(; aspect_ratio=:equal) +draw_arrow_at(p; plt=plt2, arrow_kwargs=(; label="arrow at p")) +draw_arrow_at(p_transformed; plt=plt2, arrow_kwargs=(; label="arrow after transformation")) +plt2 +``` + +Transformation is applied using group composition operation of $\operatorname{SE}(2)$. +`p_transformed` corresponds to active transformation, represented also by [left-forward](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Manifolds.LeftForwardAction) [group operation action](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Manifolds.GroupOperationAction): + +```{julia} +G_LF = GroupOperationAction(SE2, LeftForwardAction()) +p_transformed_2 = apply(G_LF, p_tr, p) +isapprox(p_transformed, p_transformed_2) +``` + +Note that translation and rotation happen in the global coordinates, not local ones. +We will cover local coordinates later. + +Basis transformation in the passive transformation can be understood as [right-forward](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Manifolds.RightForwardAction) group operation action. +First we need to get vectors constituting the orthonormal basis of tangent space to $\operatorname{SE}(2)$ at $p$. +These vectors are elements of the special Euclidean Lie algebra $\mathfrak{se}(2)$. +```{julia} +B_SE2 = get_basis(SE2, p, DefaultOrthonormalBasis()) +B_SE2_vecs = get_vectors(SE2, p, B_SE2) +``` + +Next we can use the right-forward action to transform these vectors. + +```{julia} +G_RF = GroupOperationAction(SE2, RightForwardAction()) +B_transformed = [apply(G_LF, p_tr, X_i) for X_i in B_SE2_vecs] +``` + +Notice that active transformation does not require a basis of the tangent space while the passive one does. + +### Local transformations + +Performing local transformations of an object requires different geometric tools. +Moving and rotating an object from its current position can be performed using an exponential map. +Its input are the current position and rotation (a point on $\operatorname{SE}(2)$) and a tangent vector that corresponds to the desired change. + +```{julia} +X = hat(SE2, se2_id, [-1.0, -2.0, -pi/6]) +``` + +The inverse problem, i.e. figuring out what transformation needs to be applied to transform one position and orientation to another one, is solved by a logarithmic map. + +### A freely moving object + + +### Effects of external forces + + From de9fd2c0fd06111579b4d26ac2c6022840ee10f8 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 11 Jul 2023 17:45:44 +0200 Subject: [PATCH 03/81] formatting --- docs/make.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 1dd48b59fd..4742313a3c 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -90,7 +90,7 @@ makedocs( "How to..." => [ "🚀 Get Started with `Manifolds.jl`" => "tutorials/getstarted.md", "Do rigid body dynamics with geometry" => "tutorials/rigid-body-dynamics.md", - ], + ], "Manifolds" => [ "Basic manifolds" => [ "Centered matrices" => "manifolds/centeredmatrices.md", From 2dff3575276f4fba2ee1528966c39f1228c226c2 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 12 Jul 2023 14:44:08 +0200 Subject: [PATCH 04/81] expand tutorial --- tutorials/Project.toml | 2 + tutorials/rigid-body-dynamics.qmd | 68 ++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/tutorials/Project.toml b/tutorials/Project.toml index 1d0d4c7d2f..b842cae408 100644 --- a/tutorials/Project.toml +++ b/tutorials/Project.toml @@ -11,6 +11,7 @@ MultivariateStats = "6f286f6a-111f-5878-ab1e-185364afe411" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] CSV = "0.10" @@ -19,3 +20,4 @@ IJulia = "1" Manifolds = "0.8.46" ManifoldsBase = "0.14.5" MultivariateStats = "0.10" +StaticArrays = "1" diff --git a/tutorials/rigid-body-dynamics.qmd b/tutorials/rigid-body-dynamics.qmd index bb5cf75174..bc590b9365 100644 --- a/tutorials/rigid-body-dynamics.qmd +++ b/tutorials/rigid-body-dynamics.qmd @@ -48,7 +48,7 @@ function draw_arrow_at(p; plt=nothing, arrow_kwargs=(;)) plt = plot(; aspect_ratio=:equal) end # constructing matrix where columns are vertices of our arrow - pts = reduce(hcat, [[0.0, -1], [2.0, -1], [2.0, -2], [4.0, 0], [2.0, 2], [2.0, 1], [0.0, 1], [0.0, -1]]) + pts = reduce(hcat, [[0.0, -0.125], [0.5, -0.125], [0.5, -0.25], [1.0, 0.0], [0.5, 0.25], [0.5, 0.125], [0.0, 0.125], [0.0, -0.125]]) # action that transforms the arrow A = Manifolds.ColumnwiseSpecialEuclideanAction(R2, SE2) # applying the transformation @@ -117,11 +117,77 @@ Its input are the current position and rotation (a point on $\operatorname{SE}(2 X = hat(SE2, se2_id, [-1.0, -2.0, -pi/6]) ``` +Let's see how it affects our arrow: + +```{julia} +p_exp = exp(SE2, p, X) +println("Old position: ", p) +println("New position: ", p_exp) +plt3 = plot(; aspect_ratio=:equal) +draw_arrow_at(p; plt=plt3, arrow_kwargs=(; label="arrow at p")) +draw_arrow_at(p_exp; plt=plt3, arrow_kwargs=(; label="arrow at exp(M, p, X)")) +plt3 +``` + +As we can see, the arrow moved exactly as expected. + The inverse problem, i.e. figuring out what transformation needs to be applied to transform one position and orientation to another one, is solved by a logarithmic map. +Additionally, we can use [`vee`](@ref) to get back the coordinates of a tangent vector: +```{julia} +X_from_log = log(SE2, p, p_exp) +vee(SE2, p, X_from_log) +``` + +which gives us back the same coordinates that we used for `X`. ### A freely moving object +A freely moving object moves at a constant velocity and rotates with a constant angular velocity. +Both velocity and angular velocity can be represented by tangent vectors. +The following plot shows what happens to an object in such conditions. + +```{julia} +ts = 0.0:0.5:10.0 +pts = [exp(SE2, p, t * X) for t in ts] +plt4 = plot(; aspect_ratio=:equal) +for (t, p) in zip(ts, pts) + draw_arrow_at(p; plt=plt4, arrow_kwargs=(; label="arrow at t=$t")) +end +plt4 +``` + +As we can see, the arrow moves at velocity defined by `X`: -1 space units per time unit in the $x$ direction, -2 space units per time unit in the $y$ direction and rotates by $\pi/6$ clockwise per time unit. +Any kind of free movement can be expressed using a tangent vector. +You may notice that our object actually follows a geodesic in the special Euclidean group. ### Effects of external forces + +## Technical notes + +These technical notes elaborate on some aspects of the tutorial that a curious reader may find interesting but are not necessary to effectively work with `Manifolds.jl` + +### Different exponential maps and connections + +The exponential map we used in this tutorial is not the only one available on Lie groups. +Another choice is the so-called ``[Lie group exponential](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Manifolds.exp_lie-Tuple{SpecialEuclidean,%20Any})``{=commonmark}. +In the case of the special Euclidean group, Lie group exponential mixes the translation and rotation parts of a tangent vector: + +```{julia} +ts = 0.0:0.25:1.0 +pts = [exp_lie(SE2, t * X) for t in ts] +plt5 = plot(; aspect_ratio=:equal) +for (t, p) in zip(ts, pts) + draw_arrow_at(p; plt=plt5, arrow_kwargs=(; label="arrow at t=$t")) +end +plt5 +``` + +Note also that the default choice of connection for Lie groups is not the only one. +There are also ``[Cartan-Schouten connections](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Cartan-Schouten-connections)``{=commonmark}. +They give the same exponential and logarithmic maps but a different parallel transport. + +### Jet bundles? + +Yes, [📖 jet bundles](https://en.wikipedia.org/wiki/Jet_bundle) are more mathematically enticing objects for describing dynamics but practical details of their application haven't been worked out yet. From 1fa3ac794d2d6b5a925f7765c6777483156e2631 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 12 Jul 2023 19:22:10 +0200 Subject: [PATCH 05/81] Start describing dynamics --- tutorials/rigid-body-dynamics.qmd | 55 ++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/tutorials/rigid-body-dynamics.qmd b/tutorials/rigid-body-dynamics.qmd index bc590b9365..0828884419 100644 --- a/tutorials/rigid-body-dynamics.qmd +++ b/tutorials/rigid-body-dynamics.qmd @@ -162,7 +162,46 @@ You may notice that our object actually follows a geodesic in the special Euclid ### Effects of external forces +::: {.callout-caution} + +This tutorial, but most importantly this section, does not aim to follow a particular physical model of forces. +The aim is to get a description that can be reasonably used for motion estimation from noisy sensors. +However, the language used here is fairly close to what geometric descriptions of physical models use so likely it could be developed much futher. +Original author of this tutorial is not a physicist though so take it with a grain of salt. + +::: + +An external force acting upon a rigid body makes it accelerate or changes its angular velocity. +There are many possible geometric descriptions of these phenomena. +In this tutorial we will use the most simple one, i.e. representing acceleration as an additional tangent vector. +This ignores a lot of geometric content that could be exploited but it lets us move forward towards representing and analysing uncertainties caused by measurement inaccuracy. + +The freely moving object followed a trajectory $\gamma(t)=\exp_p(tX)$ +We can think of it as an object in the tangent bundle $T\operatorname{SE}(2)$ (though see technical note about cotangent bundle). +We could do again the trick with representing change as a tangent vector $(X_p, X_X) \in T_{(p, X)} T\operatorname{SE}(2)$ but it is a redundant representation: $X_p$ describes change in $p$ which is already described by $X$. +So all we need is the $X_X$ part of the tangent vector (to the tangent bundle). +Using the metric of $\operatorname{SE}(2)$ we can construct an isomorphism between the space in which $X_X$ lives in and the tangent space $T_p\operatorname{SE}(2)$. + +Finally we determined that an accelerating object can be described by a triple $(p, X, X_X)$. +We do not yet have a complete description of the dynamics: how does the triple evolve with time? +A freely moving object followed a geodesic. +An accelerating object could also just follow a geodesic but we would have to manufacture an appropriate affine connection (see: [📖 general relativity](https://en.wikipedia.org/wiki/General_relativity)). +We are trying to keep things relatively simple so we will use the following ODE: +```math +\begin{aligned} +\dot{p} &= X \\ +\dot{X} &= X_X\\ +\dot{X_X} &= 0 +\end{aligned} +``` + +To solve the ODE we still need an affine connection on the manifold $(p, X, X_X)$ lives on. +A nice way of describing that manifold in `Manifolds.jl` is by using a vector bundle where each fiber corresponds to direct sum of tangent spaces. +There is, however, one small caveat: `X_X` was brought from the double tangent bundle and depends on the connection we (should have) selected for the tangent bundle $T\operatorname{SE}(2)$. +So we may use the connection from $\operatorname{SE}(2)$ but we may also use the Levi-Civita connection of the Sasaki metric on $T\operatorname{SE}(2)$. +We shall explore this choice later in the tutorial. +For simplicity again we will use a first order forward Euler discretization to solve our ODE (see [technical note](@sec-forward-euler) if you disapprove). ## Technical notes @@ -190,4 +229,18 @@ They give the same exponential and logarithmic maps but a different parallel tra ### Jet bundles? -Yes, [📖 jet bundles](https://en.wikipedia.org/wiki/Jet_bundle) are more mathematically enticing objects for describing dynamics but practical details of their application haven't been worked out yet. +Yes, [📖 jet bundles](https://en.wikipedia.org/wiki/Jet_bundle) are more mathematically enticing objects for describing dynamics but practical details of their implementation haven't been worked out yet. + +### Phase space as a contangent bundle + +Phase space in classical mechanics is typically defined as a contangent bundle. +Cotangent bundles are, however, hard to use for computation. +So we select Riesz representers of cotangent vectors according to the metric of our manifold. +In this way we get an isomorphism between tangent and cotangent bundles. + +Note that a generic description of a phase space doesn't require a Riemannian metric. +Generic tools come from symplectic geometry which imposes a less rich structure. + +### Forward Euler? Really? {#sec-forward-euler} + +Yes, for simplicity. For a start. I may try a higher order or a symplectic solver later. You can do that too! From c4f5df3906910ae523e79b3c32882e87f6fcebac Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 26 Jul 2023 16:00:15 +0200 Subject: [PATCH 06/81] fiber bundles pt. 1 --- src/Manifolds.jl | 2 + src/manifolds/Fiber.jl | 398 ++++++++++++++ src/manifolds/FiberBundle.jl | 433 +++++++++++++++ src/manifolds/PowerManifold.jl | 4 - src/manifolds/ProductManifold.jl | 2 +- src/manifolds/VectorBundle.jl | 865 +++--------------------------- test/manifolds/fiber.jl | 44 ++ test/manifolds/vector_bundle.jl | 45 +- test/runtests.jl | 1 + tutorials/rigid-body-dynamics.qmd | 9 +- 10 files changed, 968 insertions(+), 835 deletions(-) create mode 100644 src/manifolds/Fiber.jl create mode 100644 src/manifolds/FiberBundle.jl create mode 100644 test/manifolds/fiber.jl diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 5c61f2a0d2..942ae06c94 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -334,6 +334,8 @@ include("manifold_fallbacks.jl") include("manifolds/ConnectionManifold.jl") include("manifolds/MetricManifold.jl") include("manifolds/QuotientManifold.jl") +include("manifolds/Fiber.jl") +include("manifolds/FiberBundle.jl") include("manifolds/VectorBundle.jl") include("groups/group.jl") diff --git a/src/manifolds/Fiber.jl b/src/manifolds/Fiber.jl new file mode 100644 index 0000000000..6f225ebb3f --- /dev/null +++ b/src/manifolds/Fiber.jl @@ -0,0 +1,398 @@ + +""" + TensorProductType(spaces::VectorSpaceType...) + +Vector space type corresponding to the tensor product of given vector space +types. +""" +struct TensorProductType{TS<:Tuple} <: VectorSpaceType + spaces::TS +end + +TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) + +""" + abstract type FiberType end + +An abstract type for fiber types. +""" +abstract type FiberType end + +struct VectorSpaceFiberType{TVS<:VectorSpaceType} <: FiberType + fiber::TVS +end + +function Base.show(io::IO, vsf::VectorSpaceFiberType) + return print(io, "VectorSpaceFiberType($(vsf.fiber))") +end + +const TangentFiberType = VectorSpaceFiberType{TangentSpaceType} + +const CotangentFiberType = VectorSpaceFiberType{CotangentSpaceType} + +const TangentFiber = VectorSpaceFiberType{TangentSpaceType}(TangentSpace) +const CotangentFiber = VectorSpaceFiberType{CotangentSpaceType}(CotangentSpace) + +""" + BundleFibers(fiber::FiberType, M::AbstractManifold) + +Type representing a family of vector spaces (fibers) of a vector bundle over `M` +with vector spaces of type `fiber`. In contrast with `FiberBundle`, operations +on `BundleFibers` expect point-like and vector-like parts to be +passed separately instead of being bundled together. It can be thought of +as a representation of vector spaces from a vector bundle but without +storing the point at which a vector space is attached (which is specified +separately in various functions). +""" +struct BundleFibers{TF<:FiberType,TM<:AbstractManifold} + fiber::TF + manifold::TM +end + +""" + VectorBundleFibers{TVS,TM} + +Alias for [`BundleFibers`](@ref) when the fiber is a vector space. +""" +const VectorBundleFibers{TVS,TM} = BundleFibers{ + VectorSpaceFiberType{TVS}, + TM, +} where {TVS<:VectorSpaceType,TM<:AbstractManifold} + +function VectorBundleFibers(fiber::VectorSpaceType, M::AbstractManifold) + return BundleFibers(VectorSpaceFiberType(fiber), M) +end + +const TangentBundleFibers{M} = BundleFibers{TangentFiberType,M} where {M<:AbstractManifold} + +TangentBundleFibers(M::AbstractManifold) = BundleFibers(TangentFiber, M) + +const CotangentBundleFibers{M} = + BundleFibers{CotangentFiberType,M} where {M<:AbstractManifold} + +CotangentBundleFibers(M::AbstractManifold) = BundleFibers(CotangentFiber, M) + +""" + FiberAtPoint{ + 𝔽, + TFiber<:BundleFibers{<:FiberType,<:AbstractManifold{𝔽}}, + TX, + } <: AbstractManifold{𝔽} + +A fiber of a [`FiberBundle`](@ref) at a point `p` on the manifold. +This is modelled using [`BundleFibers`](@ref) with only a fiber part +and fixing the point-like part to be just `p`. + +This fiber itself is also a `manifold`. For vector fibers it's by default flat and hence +isometric to the [`Euclidean`](@ref) manifold. + +# Constructor + + FiberAtPoint(fiber::BundleFibers, p) + +A fiber of type `fiber` at point `p` from the manifold `fiber.manifold`. +""" +struct FiberAtPoint{𝔽,TFiber<:BundleFibers{<:FiberType,<:AbstractManifold{𝔽}},TX} <: + AbstractManifold{𝔽} + fiber::TFiber + point::TX +end + +""" + VectorSpaceAtPoint{𝔽,TFiber} + +Alias for [`FiberAtPoint`](@ref) when the fiber is a vector space. +""" +const VectorSpaceAtPoint{𝔽,TFiber} = FiberAtPoint{ + 𝔽, + TFiber, +} where {TFiber<:VectorBundleFibers{<:FiberType,<:AbstractManifold{𝔽}}} + +function VectorSpaceAtPoint(M::AbstractManifold, fiber::VectorSpaceFiberType, p) + return FiberAtPoint(BundleFibers(fiber, M), p) +end +VectorSpaceAtPoint(fiber::BundleFibers{<:VectorSpaceFiberType}, p) = FiberAtPoint(fiber, p) + +""" + TangentSpaceAtPoint{𝔽,M} + +Alias for [`VectorSpaceAtPoint`](@ref) for the tangent space at a point. +""" +const TangentSpaceAtPoint{𝔽,M} = + FiberAtPoint{𝔽,TangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} + +""" + TangentSpaceAtPoint(M::AbstractManifold, p) + +Return an object of type [`VectorSpaceAtPoint`](@ref) representing tangent +space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. +""" +TangentSpaceAtPoint(M::AbstractManifold, p) = VectorSpaceAtPoint(M, TangentFiber, p) + +""" + TangentSpace(M::AbstractManifold, p) + +Return a [`TangentSpaceAtPoint`](@ref Manifolds.TangentSpaceAtPoint) representing tangent +space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. +""" +TangentSpace(M::AbstractManifold, p) = TangentSpaceAtPoint(M, p) + +const CotangentSpaceAtPoint{𝔽,M} = + VectorSpaceAtPoint{CotangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} + +""" + CotangentSpaceAtPoint(M::AbstractManifold, p) + +Return an object of type [`VectorSpaceAtPoint`](@ref) representing cotangent +space at `p`. +""" +function CotangentSpaceAtPoint(M::AbstractManifold, p) + return VectorSpaceAtPoint(M, CotangentFiber, p) +end + +""" + allocate_result(B::BundleFibers, f, x...) + +Allocates an array for the result of function `f` that is +an element of the vector space of type `B.fiber` on manifold `B.manifold` +and arguments `x...` for implementing the non-modifying operation +using the modifying operation. +""" +@inline function allocate_result(B::BundleFibers, f::TF, x...) where {TF} + if length(x) == 0 + # TODO: this may be incorrect when point and tangent vector representation are + # different for the manifold but there is no easy and generic way around that + return allocate_result(B.manifold, f) + else + T = allocate_result_type(B, f, x) + return allocate(x[1], T) + end +end + +""" + allocate_result_type(B::BundleFibers, f, args::NTuple{N,Any}) where N + +Return type of element of the array that will represent the result of +function `f` for representing an operation with result in the vector space `fiber` +for manifold `M` on given arguments (passed at a tuple). +""" +@inline function allocate_result_type( + ::BundleFibers, + f::TF, + args::NTuple{N,Any}, +) where {TF,N} + return typeof(mapreduce(eti -> one(number_eltype(eti)), +, args)) +end + +base_manifold(B::BundleFibers) = base_manifold(B.manifold) +base_manifold(B::FiberAtPoint) = base_manifold(B.fiber) + +""" + distance(M::TangentSpaceAtPoint, p, q) + +Distance between vectors `p` and `q` from the vector space `M`. It is calculated as the norm +of their difference. +""" +function distance(M::TangentSpaceAtPoint, p, q) + return norm(M.fiber.manifold, M.point, q - p) +end + +function embed!(M::TangentSpaceAtPoint, q, p) + return embed!(M.fiber.manifold, q, M.point, p) +end +function embed!(M::TangentSpaceAtPoint, Y, p, X) + return embed!(M.fiber.manifold, Y, M.point, X) +end + +@doc raw""" + exp(M::TangentSpaceAtPoint, p, X) + +Exponential map of tangent vectors `X` and `p` from the tangent space `M`. It is +calculated as their sum. +""" +exp(::TangentSpaceAtPoint, ::Any, ::Any) + +function exp!(M::TangentSpaceAtPoint, q, p, X) + copyto!(M.fiber.manifold, q, p + X) + return q +end + +function fiber_dimension(B::BundleFibers) + return fiber_dimension(B.manifold, B.fiber) +end +fiber_dimension(M::TangentBundleFibers) = manifold_dimension(M.manifold) +fiber_dimension(M::CotangentBundleFibers) = manifold_dimension(M.manifold) +fiber_dimension(M::AbstractManifold, ::CotangentSpaceType) = manifold_dimension(M) +fiber_dimension(M::AbstractManifold, ::TangentSpaceType) = manifold_dimension(M) + +function get_basis(M::TangentSpaceAtPoint, p, B::CachedBasis) + return invoke( + get_basis, + Tuple{AbstractManifold,Any,CachedBasis}, + M.fiber.manifold, + M.point, + B, + ) +end +function get_basis(M::TangentSpaceAtPoint, p, B::AbstractBasis{<:Any,TangentSpaceType}) + return get_basis(M.fiber.manifold, M.point, B) +end + +function get_coordinates(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) + return get_coordinates(M.fiber.manifold, M.point, X, B) +end + +function get_coordinates!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) + return get_coordinates!(M.fiber.manifold, Y, M.point, X, B) +end + +function get_vector(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) + return get_vector(M.fiber.manifold, M.point, X, B) +end + +function get_vector!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) + return get_vector!(M.fiber.manifold, Y, M.point, X, B) +end + +function get_vectors(M::TangentSpaceAtPoint, p, B::CachedBasis) + return get_vectors(M.fiber.manifold, M.point, B) +end + +@doc raw""" + injectivity_radius(M::TangentSpaceAtPoint) + +Return the injectivity radius on the [`TangentSpaceAtPoint`](@ref Manifolds.TangentSpaceAtPoint) `M`, which is $∞$. +""" +injectivity_radius(::TangentSpaceAtPoint) = Inf + +""" + inner(M::TangentSpaceAtPoint, p, X, Y) + +Inner product of vectors `X` and `Y` from the tangent space at `M`. +""" +function inner(M::TangentSpaceAtPoint, p, X, Y) + return inner(M.fiber.manifold, M.point, X, Y) +end + +""" + is_flat(::TangentSpaceAtPoint) + +Return true. [`TangentSpaceAtPoint`](@ref Manifolds.TangentSpaceAtPoint) is a flat manifold. +""" +is_flat(::TangentSpaceAtPoint) = true + +function _isapprox(M::TangentSpaceAtPoint, X, Y; kwargs...) + return isapprox(M.fiber.manifold, M.point, X, Y; kwargs...) +end + +""" + log(M::TangentSpaceAtPoint, p, q) + +Logarithmic map on the tangent space manifold `M`, calculated as the difference of tangent +vectors `q` and `p` from `M`. +""" +log(::TangentSpaceAtPoint, ::Any...) +function log!(::TangentSpaceAtPoint, X, p, q) + copyto!(X, q - p) + return X +end + +function manifold_dimension(M::FiberAtPoint) + return fiber_dimension(M.fiber) +end + +LinearAlgebra.norm(M::VectorSpaceAtPoint, p, X) = norm(M.fiber.manifold, M.point, X) + +function parallel_transport_to!(M::TangentSpaceAtPoint, Y, p, X, q) + return copyto!(M.fiber.manifold, Y, p, X) +end + +@doc raw""" + project(M::TangentSpaceAtPoint, p) + +Project the point `p` from the tangent space `M`, that is project the vector `p` +tangent at `M.point`. +""" +project(::TangentSpaceAtPoint, ::Any) + +function project!(M::TangentSpaceAtPoint, q, p) + return project!(M.fiber.manifold, q, M.point, p) +end + +@doc raw""" + project(M::TangentSpaceAtPoint, p, X) + +Project the vector `X` from the tangent space `M`, that is project the vector `X` +tangent at `M.point`. +""" +project(::TangentSpaceAtPoint, ::Any, ::Any) + +function project!(M::TangentSpaceAtPoint, Y, p, X) + return project!(M.fiber.manifold, Y, M.point, X) +end + +function Random.rand!(rng::AbstractRNG, M::TangentSpaceAtPoint, X; vector_at=nothing) + rand!(rng, M.fiber.manifold, X; vector_at=M.point) + return X +end + +function representation_size(B::TangentSpaceAtPoint) + return representation_size(B.fiber.manifold) +end + +function Base.show(io::IO, fiber::BundleFibers) + return print(io, "BundleFibers($(fiber.fiber), $(fiber.manifold))") +end +function Base.show(io::IO, ::MIME"text/plain", vs::FiberAtPoint) + summary(io, vs) + println(io, "\nFiber:") + pre = " " + sf = sprint(show, "text/plain", vs.fiber; context=io, sizehint=0) + sf = replace(sf, '\n' => "\n$(pre)") + println(io, pre, sf) + println(io, "Base point:") + sp = sprint(show, "text/plain", vs.point; context=io, sizehint=0) + sp = replace(sp, '\n' => "\n$(pre)") + return print(io, pre, sp) +end +function Base.show(io::IO, ::MIME"text/plain", TpM::TangentSpaceAtPoint) + println(io, "Tangent space to the manifold $(base_manifold(TpM)) at point:") + pre = " " + sp = sprint(show, "text/plain", TpM.point; context=io, sizehint=0) + sp = replace(sp, '\n' => "\n$(pre)") + return print(io, pre, sp) +end + +function vector_transport_to!( + M::TangentSpaceAtPoint, + Y, + p, + X, + q, + m::AbstractVectorTransportMethod, +) + return copyto!(M.fiber.manifold, Y, p, X) +end + +""" + zero_vector(B::BundleFibers, p) + +Compute the zero vector from the vector space of type `B.fiber` at point `p` +from manifold `B.manifold`. +""" +function zero_vector(B::BundleFibers, p) + X = allocate_result(B, zero_vector, p) + return zero_vector!(B, X, p) +end + +@doc raw""" + zero_vector(M::TangentSpaceAtPoint, p) + +Zero tangent vector at point `p` from the tangent space `M`, that is the zero tangent vector +at point `M.point`. +""" +zero_vector(::TangentSpaceAtPoint, ::Any...) + +function zero_vector!(M::TangentSpaceAtPoint, X, p) + return zero_vector!(M.fiber.manifold, X, M.point) +end diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl new file mode 100644 index 0000000000..8aeaadaebe --- /dev/null +++ b/src/manifolds/FiberBundle.jl @@ -0,0 +1,433 @@ + +@doc raw""" + FiberBundleProductVectorTransport{ + TMP<:AbstractVectorTransportMethod, + TMV<:AbstractVectorTransportMethod, + } <: AbstractVectorTransportMethod + +Vector transport type on [`FiberBundle`](@ref). `method_point` is used for vector transport +of the point part and `method_fiber` is used for transport of the fiber part. + +The vector transport is derived as a product manifold-style vector transport. The considered +product manifold is the product between the manifold $\mathcal M$ and the topological vector +space isometric to the fiber. + +# Constructor + + FiberBundleProductVectorTransport( + method_point::AbstractVectorTransportMethod, + method_fiber::AbstractVectorTransportMethod, + ) + FiberBundleProductVectorTransport() + +By default both methods are set to `ParallelTransport`. +""" +struct FiberBundleProductVectorTransport{ + TMP<:AbstractVectorTransportMethod, + TMV<:AbstractVectorTransportMethod, +} <: AbstractVectorTransportMethod + method_point::TMP + method_fiber::TMV +end + +function FiberBundleProductVectorTransport() + return FiberBundleProductVectorTransport(ParallelTransport(), ParallelTransport()) +end +function FiberBundleProductVectorTransport(M::AbstractManifold) + m = default_vector_transport_method(M) + return FiberBundleProductVectorTransport(m, m) +end + +""" + FiberBundle{𝔽,TVS<:FiberType,TM<:AbstractManifold{𝔽},TVT<:FiberBundleProductVectorTransport} <: AbstractManifold{𝔽} + +Vector bundle on a [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M` of type [`VectorSpaceType`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases.html#ManifoldsBase.VectorSpaceType). + +# Constructor + + FiberBundle(M::AbstractManifold, type::FiberType) +""" +struct FiberBundle{ + 𝔽, + TF<:FiberType, + TM<:AbstractManifold{𝔽}, + TVT<:FiberBundleProductVectorTransport, +} <: AbstractManifold{𝔽} + type::TF + manifold::TM + fiber::BundleFibers{TF,TM} + vector_transport::TVT +end + +vector_bundle_transport(::FiberType, M::AbstractManifold) = ParallelTransport() + +function FiberBundle( + fiber::TVS, + M::TM, + vtm::FiberBundleProductVectorTransport, +) where {TVS<:FiberType,TM<:AbstractManifold{𝔽}} where {𝔽} + return FiberBundle{𝔽,TVS,TM,typeof(vtm)}(fiber, M, BundleFibers(fiber, M), vtm) +end +function FiberBundle(fiber::FiberType, M::AbstractManifold) + vtmm = vector_bundle_transport(fiber, M) + vtbm = FiberBundleProductVectorTransport(vtmm, vtmm) + return FiberBundle(fiber, M, vtbm) +end + +struct FiberBundleBasisData{BBasis<:CachedBasis,TBasis<:CachedBasis} + base_basis::BBasis + fiber_basis::TBasis +end + +@doc raw""" + struct FiberBundleInverseProductRetraction <: AbstractInverseRetractionMethod end + +Inverse retraction of the point `y` at point `p` from vector bundle `B` over manifold +`B.fiber` (denoted $\mathcal M$). The inverse retraction is derived as a product manifold-style +approximation to the logarithmic map in the Sasaki metric. The considered product manifold +is the product between the manifold $\mathcal M$ and the topological vector space isometric +to the fiber. + +Notation: + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the + canonical projection of that vector bundle $B$. + Similarly, $q = (x_q, V_q)$. + +The inverse retraction is calculated as + +$\operatorname{retr}^{-1}_p q = (\operatorname{retr}^{-1}_{x_p}(x_q), V_{\operatorname{retr}^{-1}} - V_p)$ + +where $V_{\operatorname{retr}^{-1}}$ is the result of vector transport of $V_q$ to the point $x_p$. +The difference $V_{\operatorname{retr}^{-1}} - V_p$ corresponds to the logarithmic map in +the vector space $F$. + +See also [`FiberBundleProductRetraction`](@ref). +""" +struct FiberBundleInverseProductRetraction <: AbstractInverseRetractionMethod end + +@doc raw""" + struct FiberBundleProductRetraction <: AbstractRetractionMethod end + +Product retraction map of tangent vector $X$ at point $p$ from vector bundle `B` over +manifold `B.fiber` (denoted $\mathcal M$). The retraction is derived as a product manifold-style +approximation to the exponential map in the Sasaki metric. The considered product manifold +is the product between the manifold $\mathcal M$ and the topological vector space isometric +to the fiber. + +Notation: + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the + canonical projection of that vector bundle $B$. + * The tangent vector $X = (V_{X,M}, V_{X,F}) ∈ T_pB$ where + $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}\mathcal M$ and + $V_{X,F}$ is a tangent vector from the tangent space $T_{V_p}F$ (isomorphic to $F$). + +The retraction is calculated as + +$\operatorname{retr}_p(X) = (\exp_{x_p}(V_{X,M}), V_{\exp})$ + +where $V_{\exp}$ is the result of vector transport of $V_p + V_{X,F}$ +to the point $\exp_{x_p}(V_{X,M})$. +The sum $V_p + V_{X,F}$ corresponds to the exponential map in the vector space $F$. + +See also [`FiberBundleInverseProductRetraction`](@ref). +""" +struct FiberBundleProductRetraction <: AbstractRetractionMethod end + +function get_basis(M::FiberBundle, p, B::AbstractBasis) + xp1 = submanifold_component(p, Val(1)) + base_basis = get_basis(M.manifold, xp1, B) + fiber_basis = get_basis(M.fiber, xp1, B) + return CachedBasis(B, FiberBundleBasisData(base_basis, fiber_basis)) +end +function get_basis(M::FiberBundle, p, B::CachedBasis) + return invoke(get_basis, Tuple{AbstractManifold,Any,CachedBasis}, M, p, B) +end + +function get_basis(M::FiberBundle, p, B::DiagonalizingOrthonormalBasis) + xp1 = submanifold_component(p, Val(1)) + bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(1))) + b1 = get_basis(M.manifold, xp1, bv1) + bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(2))) + b2 = get_basis(M.fiber, xp1, bv2) + return CachedBasis(B, FiberBundleBasisData(b1, b2)) +end + +function get_coordinates(M::FiberBundle, p, X, B::AbstractBasis) + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + n = manifold_dimension(M.manifold) + return vcat( + get_coordinates(M.manifold, px, VXM, B), + get_coordinates(M.fiber, px, VXF, B), + ) +end + +function get_coordinates!(M::FiberBundle, Y, p, X, B::AbstractBasis) + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + n = manifold_dimension(M.manifold) + get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B) + get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B) + return Y +end + +function get_coordinates( + M::FiberBundle, + p, + X, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + return vcat( + get_coordinates(M.manifold, px, VXM, B.data.base_basis), + get_coordinates(M.fiber, px, VXF, B.data.fiber_basis), + ) +end + +function get_coordinates!( + M::FiberBundle, + Y, + p, + X, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + n = manifold_dimension(M.manifold) + get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B.data.base_basis) + get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B.data.fiber_basis) + return Y +end + +function get_vector(M::FiberBundle, p, X, B::AbstractBasis) + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + return ArrayPartition( + get_vector(M.manifold, xp1, X[1:n], B), + get_vector(M.fiber, xp1, X[(n + 1):end], B), + ) +end + +function get_vector!(M::FiberBundle, Y, p, X, B::AbstractBasis) + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + get_vector!(M.manifold, submanifold_component(Y, Val(1)), xp1, X[1:n], B) + get_vector!(M.fiber, submanifold_component(Y, Val(2)), xp1, X[(n + 1):end], B) + return Y +end + +function get_vector( + M::FiberBundle, + p, + X, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + 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.fiber_basis), + ) +end + +function get_vector!( + M::FiberBundle, + Y, + p, + X, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + get_vector!( + M.manifold, + submanifold_component(Y, Val(1)), + xp1, + X[1:n], + B.data.base_basis, + ) + get_vector!( + M.fiber, + submanifold_component(Y, Val(2)), + xp1, + X[(n + 1):end], + B.data.fiber_basis, + ) + return Y +end + +function get_vectors( + M::FiberBundle, + p::ProductRepr, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + xp1 = submanifold_component(p, Val(1)) + zero_m = zero_vector(M.manifold, xp1) + zero_f = zero_vector(M.fiber, xp1) + vs = typeof(ProductRepr(zero_m, zero_f))[] + for bv in get_vectors(M.manifold, xp1, B.data.base_basis) + push!(vs, ProductRepr(bv, zero_f)) + end + for bv in get_vectors(M.fiber, xp1, B.data.fiber_basis) + push!(vs, ProductRepr(zero_m, bv)) + end + return vs +end +function get_vectors( + M::FiberBundle, + p::ArrayPartition, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:Manifolds.FiberBundleBasisData}, +) where {𝔽} + xp1 = submanifold_component(p, Val(1)) + zero_m = zero_vector(M.manifold, xp1) + zero_f = zero_vector(M.fiber, xp1) + vs = typeof(ArrayPartition(zero_m, zero_f))[] + for bv in get_vectors(M.manifold, xp1, B.data.base_basis) + push!(vs, ArrayPartition(bv, zero_f)) + end + for bv in get_vectors(M.fiber, xp1, B.data.fiber_basis) + push!(vs, ArrayPartition(zero_m, bv)) + end + return vs +end +function get_vectors(M::BundleFibers, p, B::CachedBasis) + return get_vectors(M.manifold, p, B) +end + +""" + getindex(p::ProductRepr, M::FiberBundle, s::Symbol) + p[M::FiberBundle, s] + +Access the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` by +using the symbols `:point` and `:vector` or `:fiber` for the base and vector or fiber +component, respectively. +""" +@inline function Base.getindex(p::ProductRepr, M::FiberBundle, s::Symbol) + (s === :point) && return submanifold_component(M, p, Val(1)) + (s === :vector || s === :fiber) && return submanifold_component(M, p, Val(2)) + return throw(DomainError(s, "unknown component $s on $M.")) +end +""" + getindex(p::ArrayPartition, M::FiberBundle, s::Symbol) + p[M::FiberBundle, s] + +Access the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` by +using the symbols `:point` and `:vector` or `:fiber` for the base and vector or fiber +component, respectively. +""" +@inline function Base.getindex(p::ArrayPartition, M::FiberBundle, s::Symbol) + (s === :point) && return p.x[1] + (s === :vector || s === :fiber) && return p.x[2] + return throw(DomainError(s, "unknown component $s on $M.")) +end + +function _isapprox(B::FiberBundle, p, q; kwargs...) + xp, Vp = submanifold_components(B.manifold, p) + xq, Vq = submanifold_components(B.manifold, q) + return isapprox(B.manifold, xp, xq; kwargs...) && + isapprox(FiberAtPoint(B.fiber, xp), Vp, Vq; kwargs...) +end +function _isapprox(B::FiberBundle, p, X, Y; kwargs...) + px, Vx = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + VYM, VYF = submanifold_components(B.manifold, Y) + return isapprox(B.manifold, VXM, VYM; kwargs...) && + isapprox(FiberAtPoint(B.fiber, px), VXF, VYF; kwargs...) +end + +function manifold_dimension(B::FiberBundle) + return manifold_dimension(B.manifold) + fiber_dimension(B.fiber) +end + +function Random.rand!(rng::AbstractRNG, M::FiberBundle, pX; vector_at=nothing) + pXM, pXF = submanifold_components(M.manifold, pX) + if vector_at === nothing + rand!(rng, M.manifold, pXM) + rand!(rng, FiberAtPoint(M.fiber, pXM), pXF) + else + vector_atM, vector_atF = submanifold_components(M.manifold, vector_at) + rand!(rng, M.manifold, pXM; vector_at=vector_atM) + rand!(rng, FiberAtPoint(M.fiber, pXM), pXF; vector_at=vector_atF) + end + return pX +end + +""" + setindex!(p::ProductRepr, val, M::FiberBundle, s::Symbol) + p[M::VectorBundle, s] = val + +Set the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` to `val` by +using the symbols `:point` and `:fiber` or `:vector` for the base and fiber or vector +component, respectively. + +!!! note + + The *content* of element of `p` is replaced, not the element itself. +""" +@inline function Base.setindex!(x::ProductRepr, val, M::FiberBundle, s::Symbol) + if s === :point + return copyto!(submanifold_component(M, x, Val(1)), val) + elseif s === :vector || s === :fiber + return copyto!(submanifold_component(M, x, Val(2)), val) + else + throw(DomainError(s, "unknown component $s on $M.")) + end +end +""" + setindex!(p::ArrayPartition, val, M::FiberBundle, s::Symbol) + p[M::VectorBundle, s] = val + +Set the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` to `val` by +using the symbols `:point` and `:fiber` or `:vector` for the base and fiber or vector +component, respectively. + +!!! note + + The *content* of element of `p` is replaced, not the element itself. +""" +@inline function Base.setindex!(x::ArrayPartition, val, M::FiberBundle, s::Symbol) + if s === :point + return copyto!(x.x[1], val) + elseif s === :vector || s === :fiber + return copyto!(x.x[2], val) + else + throw(DomainError(s, "unknown component $s on $M.")) + end +end + +@inline function Base.view(x::ArrayPartition, M::FiberBundle, s::Symbol) + (s === :point) && return x.x[1] + (s === :vector || s === :fiber) && return x.x[2] + throw(DomainError(s, "unknown component $s on $M.")) +end + +@doc raw""" + zero_vector(B::FiberBundle, p) + +Zero tangent vector at point `p` from the fiber bundle `B` +over manifold `B.fiber` (denoted $\mathcal M$). The zero vector belongs to the space $T_{p}B$ + +Notation: + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the + canonical projection of that vector bundle $B$. + +The zero vector is calculated as + +$\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)$ + +where $\mathbf{0}_{x_p}$ is the zero tangent vector from $T_{x_p}\mathcal M$ and +$\mathbf{0}_F$ is the zero element of the vector space $F$. +""" +zero_vector(::FiberBundle, ::Any...) + +function zero_vector!(B::FiberBundle, X, p) + xp, Vp = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + zero_vector!(B.manifold, VXM, xp) + zero_vector!(B.fiber, VXF, Vp) + return X +end diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index bc5c3d6a9b..0acfe0caf5 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -290,10 +290,6 @@ end Distributions.support(tvd::PowerFVectorDistribution) = FVectorSupport(tvd.type, tvd.point) Distributions.support(d::PowerPointDistribution) = MPointSupport(d.manifold) -function vector_bundle_transport(fiber::VectorSpaceType, M::PowerManifold) - return ParallelTransport() -end - @inline function _write( ::PowerManifoldMultidimensional, rep_size::Tuple, diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index eec31d32d9..553ecc8532 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -1515,7 +1515,7 @@ function uniform_distribution(M::ProductManifold, p) ) end -function vector_bundle_transport(::VectorSpaceType, M::ProductManifold) +function vector_bundle_transport(::FiberType, M::ProductManifold) return ProductVectorTransport(map(_ -> ParallelTransport(), M.manifolds)) end diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 774822760c..fbd02c4244 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -1,107 +1,4 @@ -""" - TensorProductType(spaces::VectorSpaceType...) - -Vector space type corresponding to the tensor product of given vector space -types. -""" -struct TensorProductType{TS<:Tuple} <: VectorSpaceType - spaces::TS -end - -TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) - -""" - VectorBundleFibers(fiber::VectorSpaceType, M::AbstractManifold) - -Type representing a family of vector spaces (fibers) of a vector bundle over `M` -with vector spaces of type `fiber`. In contrast with `VectorBundle`, operations -on `VectorBundleFibers` expect point-like and vector-like parts to be -passed separately instead of being bundled together. It can be thought of -as a representation of vector spaces from a vector bundle but without -storing the point at which a vector space is attached (which is specified -separately in various functions). -""" -struct VectorBundleFibers{TVS<:VectorSpaceType,TM<:AbstractManifold} - fiber::TVS - manifold::TM -end - -const TangentBundleFibers{M} = - VectorBundleFibers{TangentSpaceType,M} where {M<:AbstractManifold} - -TangentBundleFibers(M::AbstractManifold) = VectorBundleFibers(TangentSpace, M) - -const CotangentBundleFibers{M} = - VectorBundleFibers{CotangentSpaceType,M} where {M<:AbstractManifold} - -CotangentBundleFibers(M::AbstractManifold) = VectorBundleFibers(CotangentSpace, M) - -""" - VectorSpaceAtPoint{ - 𝔽, - TFiber<:VectorBundleFibers{<:VectorSpaceType,<:AbstractManifold{𝔽}}, - TX, - } <: AbstractManifold{𝔽} - -A vector space at a point `p` on the manifold. -This is modelled using [`VectorBundleFibers`](@ref) with only a vector-like part -and fixing the point-like part to be just `p`. - -This vector space itself is also a `manifold`. Especially, it's flat and hence isometric -to the [`Euclidean`](@ref) manifold. - -# Constructor - VectorSpaceAtPoint(fiber::VectorBundleFibers, p) - -A vector space (fiber type `fiber` of a vector bundle) at point `p` from -the manifold `fiber.manifold`. -""" -struct VectorSpaceAtPoint{ - 𝔽, - TFiber<:VectorBundleFibers{<:VectorSpaceType,<:AbstractManifold{𝔽}}, - TX, -} <: AbstractManifold{𝔽} - fiber::TFiber - point::TX -end - -""" - TangentSpaceAtPoint{M} - -Alias for [`VectorSpaceAtPoint`](@ref) for the tangent space at a point. -""" -const TangentSpaceAtPoint{M} = - VectorSpaceAtPoint{𝔽,TangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} - -""" - TangentSpaceAtPoint(M::AbstractManifold, p) - -Return an object of type [`VectorSpaceAtPoint`](@ref) representing tangent -space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. -""" -TangentSpaceAtPoint(M::AbstractManifold, p) = VectorSpaceAtPoint(TangentBundleFibers(M), p) - -""" - TangentSpace(M::AbstractManifold, p) - -Return a [`TangentSpaceAtPoint`](@ref Manifolds.TangentSpaceAtPoint) representing tangent space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. -""" -TangentSpace(M::AbstractManifold, p) = VectorSpaceAtPoint(TangentBundleFibers(M), p) - -const CotangentSpaceAtPoint{M} = - VectorSpaceAtPoint{𝔽,CotangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} - -""" - CotangentSpaceAtPoint(M::AbstractManifold, p) - -Return an object of type [`VectorSpaceAtPoint`](@ref) representing cotangent -space at `p`. -""" -function CotangentSpaceAtPoint(M::AbstractManifold, p) - return VectorSpaceAtPoint(CotangentBundleFibers(M), p) -end - @doc raw""" struct SasakiRetraction <: AbstractRetractionMethod end @@ -130,80 +27,33 @@ struct SasakiRetraction <: AbstractRetractionMethod L::Int end -@doc raw""" - VectorBundleProductVectorTransport{ - TMP<:AbstractVectorTransportMethod, - TMV<:AbstractVectorTransportMethod, - } <: AbstractVectorTransportMethod - -Vector transport type on [`VectorBundle`](@ref). `method_point` is used for vector transport -of the point part and `method_vector` is used for transport of the vector part. - -The vector transport is derived as a product manifold-style vector transport. The considered -product manifold is the product between the manifold $\mathcal M$ and the topological vector -space isometric to the fiber. - -# Constructor - - VectorBundleProductVectorTransport( - method_point::AbstractVectorTransportMethod, - method_vector::AbstractVectorTransportMethod, - ) - VectorBundleProductVectorTransport() - -By default both methods are set to `ParallelTransport`. """ -struct VectorBundleProductVectorTransport{ - TMP<:AbstractVectorTransportMethod, - TMV<:AbstractVectorTransportMethod, -} <: AbstractVectorTransportMethod - method_point::TMP - method_vector::TMV -end - -function VectorBundleProductVectorTransport() - return VectorBundleProductVectorTransport(ParallelTransport(), ParallelTransport()) -end + const VectorBundleVectorTransport = FiberBundleProductVectorTransport +Deprecated: an alias for `FiberBundleProductVectorTransport`. """ - const VectorBundleVectorTransport = VectorBundleProductVectorTransport - -Deprecated: an alias for `VectorBundleProductVectorTransport`. -""" -const VectorBundleVectorTransport = VectorBundleProductVectorTransport - -""" - VectorBundle{𝔽,TVS<:VectorSpaceType,TM<:AbstractManifold{𝔽}} <: AbstractManifold{𝔽} - -Vector bundle on a [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M` of type [`VectorSpaceType`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases.html#ManifoldsBase.VectorSpaceType). +const VectorBundleVectorTransport = FiberBundleProductVectorTransport -# Constructor - - VectorBundle(M::AbstractManifold, type::VectorSpaceType) -""" -struct VectorBundle{ +const VectorBundle{𝔽,TVS,TM,TVT} = FiberBundle{ 𝔽, + VectorSpaceFiberType{TVS}, + TM, + TVT, +} where { TVS<:VectorSpaceType, TM<:AbstractManifold{𝔽}, - TVT<:VectorBundleProductVectorTransport, -} <: AbstractManifold{𝔽} - type::TVS - manifold::TM - fiber::VectorBundleFibers{TVS,TM} - vector_transport::TVT -end + TVT<:FiberBundleProductVectorTransport, +} function VectorBundle( - fiber::TVS, - M::TM, - vtm::VectorBundleProductVectorTransport, -) where {TVS<:VectorSpaceType,TM<:AbstractManifold{𝔽}} where {𝔽} - return VectorBundle{𝔽,TVS,TM,typeof(vtm)}(fiber, M, VectorBundleFibers(fiber, M), vtm) + vst::VectorSpaceType, + M::AbstractManifold, + vtm::FiberBundleProductVectorTransport, +) + return FiberBundle(VectorSpaceFiberType(vst), M, vtm) end -function VectorBundle(fiber::VectorSpaceType, M::AbstractManifold) - vtmm = vector_bundle_transport(fiber, M) - vtbm = VectorBundleProductVectorTransport(vtmm, vtmm) - return VectorBundle(fiber, M, vtbm) +function VectorBundle(vst::VectorSpaceType, M::AbstractManifold) + return FiberBundle(VectorSpaceFiberType(vst), M) end """ @@ -211,9 +61,9 @@ end Tangent bundle for manifold of type `M`, as a manifold with the Sasaki metric [^Sasaki1958]. -Exact retraction and inverse retraction can be approximated using [`VectorBundleProductRetraction`](@ref), -[`VectorBundleInverseProductRetraction`](@ref) and [`SasakiRetraction`](@ref). -[`VectorBundleProductVectorTransport`](@ref) can be used as a vector transport. +Exact retraction and inverse retraction can be approximated using [`FiberBundleProductRetraction`](@ref), +[`FiberBundleInverseProductRetraction`](@ref) and [`SasakiRetraction`](@ref). +[`FiberBundleProductVectorTransport`](@ref) can be used as a vector transport. [^Sasaki1958]: > S. Sasaki, “On the differential geometry of tangent bundles of Riemannian manifolds,” @@ -222,13 +72,13 @@ Exact retraction and inverse retraction can be approximated using [`VectorBundle # Constructors TangentBundle(M::AbstractManifold) - TangentBundle(M::AbstractManifold, vtm::VectorBundleProductVectorTransport) + TangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) """ const TangentBundle{𝔽,M} = VectorBundle{𝔽,TangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} TangentBundle(M::AbstractManifold) = VectorBundle(TangentSpace, M) -function TangentBundle(M::AbstractManifold, vtm::VectorBundleProductVectorTransport) +function TangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) return VectorBundle(TangentSpace, M, vtm) end @@ -236,73 +86,10 @@ const CotangentBundle{𝔽,M} = VectorBundle{𝔽,CotangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} CotangentBundle(M::AbstractManifold) = VectorBundle(CotangentSpace, M) -function CotangentBundle(M::AbstractManifold, vtm::VectorBundleProductVectorTransport) +function CotangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) return VectorBundle(CotangentSpace, M, vtm) end -struct VectorBundleBasisData{BBasis<:CachedBasis,TBasis<:CachedBasis} - base_basis::BBasis - vec_basis::TBasis -end - -@doc raw""" - struct VectorBundleInverseProductRetraction <: AbstractInverseRetractionMethod end - -Inverse retraction of the point `y` at point `p` from vector bundle `B` over manifold -`B.fiber` (denoted $\mathcal M$). The inverse retraction is derived as a product manifold-style -approximation to the logarithmic map in the Sasaki metric. The considered product manifold -is the product between the manifold $\mathcal M$ and the topological vector space isometric -to the fiber. - -Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - Similarly, $q = (x_q, V_q)$. - -The inverse retraction is calculated as - -$\operatorname{retr}^{-1}_p q = (\operatorname{retr}^{-1}_{x_p}(x_q), V_{\operatorname{retr}^{-1}} - V_p)$ - -where $V_{\operatorname{retr}^{-1}}$ is the result of vector transport of $V_q$ to the point $x_p$. -The difference $V_{\operatorname{retr}^{-1}} - V_p$ corresponds to the logarithmic map in -the vector space $F$. - -See also [`VectorBundleProductRetraction`](@ref). -""" -struct VectorBundleInverseProductRetraction <: AbstractInverseRetractionMethod end - -@doc raw""" - struct VectorBundleProductRetraction <: AbstractRetractionMethod end - -Product retraction map of tangent vector $X$ at point $p$ from vector bundle `B` over -manifold `B.fiber` (denoted $\mathcal M$). The retraction is derived as a product manifold-style -approximation to the exponential map in the Sasaki metric. The considered product manifold -is the product between the manifold $\mathcal M$ and the topological vector space isometric -to the fiber. - -Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - * The tangent vector $X = (V_{X,M}, V_{X,F}) ∈ T_pB$ where - $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}\mathcal M$ and - $V_{X,F}$ is a tangent vector from the tangent space $T_{V_p}F$ (isomorphic to $F$). - -The retraction is calculated as - -$\operatorname{retr}_p(X) = (\exp_{x_p}(V_{X,M}), V_{\exp})$ - -where $V_{\exp}$ is the result of vector transport of $V_p + V_{X,F}$ -to the point $\exp_{x_p}(V_{X,M})$. -The sum $V_p + V_{X,F}$ corresponds to the exponential map in the vector space $F$. - -See also [`VectorBundleInverseProductRetraction`](@ref). -""" -struct VectorBundleProductRetraction <: AbstractRetractionMethod end - -base_manifold(B::VectorBundleFibers) = base_manifold(B.manifold) -base_manifold(B::VectorSpaceAtPoint) = base_manifold(B.fiber) base_manifold(B::VectorBundle) = base_manifold(B.manifold) """ @@ -315,89 +102,29 @@ of `p` is attached. bundle_projection(B::VectorBundle, p) = submanifold_component(B.manifold, p, Val(1)) function default_inverse_retraction_method(::TangentBundle) - return VectorBundleInverseProductRetraction() + return FiberBundleInverseProductRetraction() end function default_retraction_method(::TangentBundle) - return VectorBundleProductRetraction() + return FiberBundleProductRetraction() end function default_vector_transport_method(B::TangentBundle) default_vt_m = default_vector_transport_method(B.manifold) - return VectorBundleProductVectorTransport(default_vt_m, default_vt_m) + return FiberBundleProductVectorTransport(default_vt_m, default_vt_m) end """ - distance(B::VectorBundleFibers, p, X, Y) + distance(B::BundleFibers, p, X, Y) Distance between vectors `X` and `Y` from the vector space at point `p` from the manifold `B.manifold`, that is the base manifold of `M`. """ distance(B::VectorBundleFibers, p, X, Y) = norm(B, p, X - Y) -""" - distance(M::TangentSpaceAtPoint, p, q) - -Distance between vectors `p` and `q` from the vector space `M`. It is calculated as the norm -of their difference. -""" -function distance(M::TangentSpaceAtPoint, p, q) - return norm(M.fiber.manifold, M.point, q - p) -end - -function embed!(M::TangentSpaceAtPoint, q, p) - return embed!(M.fiber.manifold, q, M.point, p) -end -function embed!(M::TangentSpaceAtPoint, Y, p, X) - return embed!(M.fiber.manifold, Y, M.point, X) -end - -@doc raw""" - exp(M::TangentSpaceAtPoint, p, X) - -Exponential map of tangent vectors `X` and `p` from the tangent space `M`. It is -calculated as their sum. -""" -exp(::TangentSpaceAtPoint, ::Any, ::Any) - -function exp!(M::TangentSpaceAtPoint, q, p, X) - copyto!(M.fiber.manifold, q, p + X) - return q -end - -function get_basis(M::VectorBundle, p, B::AbstractBasis) - xp1 = submanifold_component(p, Val(1)) - base_basis = get_basis(M.manifold, xp1, B) - vec_basis = get_basis(M.fiber, xp1, B) - return CachedBasis(B, VectorBundleBasisData(base_basis, vec_basis)) -end -function get_basis(M::VectorBundle, p, B::CachedBasis) - return invoke(get_basis, Tuple{AbstractManifold,Any,CachedBasis}, M, p, B) -end -function get_basis(M::TangentSpaceAtPoint, p, B::CachedBasis) - return invoke( - get_basis, - Tuple{AbstractManifold,Any,CachedBasis}, - M.fiber.manifold, - M.point, - B, - ) -end - -function get_basis(M::VectorBundle, p, B::DiagonalizingOrthonormalBasis) - xp1 = submanifold_component(p, Val(1)) - bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(1))) - b1 = get_basis(M.manifold, xp1, bv1) - bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(2))) - b2 = get_basis(M.fiber, xp1, bv2) - return CachedBasis(B, VectorBundleBasisData(b1, b2)) -end function get_basis(M::TangentBundleFibers, p, B::AbstractBasis{<:Any,TangentSpaceType}) return get_basis(M.manifold, p, B) end -function get_basis(M::TangentSpaceAtPoint, p, B::AbstractBasis{<:Any,TangentSpaceType}) - return get_basis(M.fiber.manifold, M.point, B) -end function get_coordinates(M::TangentBundleFibers, p, X, B::AbstractBasis) return get_coordinates(M.manifold, p, X, B) @@ -407,205 +134,13 @@ function get_coordinates!(M::TangentBundleFibers, Y, p, X, B::AbstractBasis) return get_coordinates!(M.manifold, Y, p, X, B) end -function get_coordinates(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) - return get_coordinates(M.fiber.manifold, M.point, X, B) -end - -function get_coordinates!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) - return get_coordinates!(M.fiber.manifold, Y, M.point, X, B) -end - -function get_coordinates(M::VectorBundle, p, X, B::AbstractBasis) - px, Vx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - n = manifold_dimension(M.manifold) - return vcat( - get_coordinates(M.manifold, px, VXM, B), - get_coordinates(M.fiber, px, VXF, B), - ) -end - -function get_coordinates!(M::VectorBundle, Y, p, X, B::AbstractBasis) - px, Vx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - n = manifold_dimension(M.manifold) - get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B) - get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B) - return Y -end - -function get_coordinates( - M::VectorBundle, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:VectorBundleBasisData}, -) where {𝔽} - px, Vx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - return vcat( - get_coordinates(M.manifold, px, VXM, B.data.base_basis), - get_coordinates(M.fiber, px, VXF, B.data.vec_basis), - ) -end - -function get_coordinates!( - M::VectorBundle, - Y, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:VectorBundleBasisData}, -) where {𝔽} - px, Vx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - n = manifold_dimension(M.manifold) - get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B.data.base_basis) - get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B.data.vec_basis) - return Y -end - -function get_vector(M::VectorBundle, p, X, B::AbstractBasis) - n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - return ArrayPartition( - get_vector(M.manifold, xp1, X[1:n], B), - get_vector(M.fiber, xp1, X[(n + 1):end], B), - ) -end - -function get_vector!(M::VectorBundle, Y, p, X, B::AbstractBasis) - n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - get_vector!(M.manifold, submanifold_component(Y, Val(1)), xp1, X[1:n], B) - get_vector!(M.fiber, submanifold_component(Y, Val(2)), xp1, X[(n + 1):end], B) - return Y -end - -function get_vector( - M::VectorBundle, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:VectorBundleBasisData}, -) where {𝔽} - n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - 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), - ) -end - -function get_vector!( - M::VectorBundle, - Y, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:VectorBundleBasisData}, -) where {𝔽} - n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - get_vector!( - M.manifold, - submanifold_component(Y, Val(1)), - xp1, - X[1:n], - B.data.base_basis, - ) - get_vector!( - M.fiber, - submanifold_component(Y, Val(2)), - xp1, - X[(n + 1):end], - B.data.vec_basis, - ) - return Y -end - function get_vector(M::TangentBundleFibers, p, X, B::AbstractBasis) return get_vector(M.manifold, p, X, B) end -function get_vector(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) - return get_vector(M.fiber.manifold, M.point, X, B) -end function get_vector!(M::TangentBundleFibers, Y, p, X, B::AbstractBasis) return get_vector!(M.manifold, Y, p, X, B) end -function get_vector!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) - return get_vector!(M.fiber.manifold, Y, M.point, X, B) -end - -function get_vectors( - M::VectorBundle, - p::ProductRepr, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:VectorBundleBasisData}, -) where {𝔽} - xp1 = submanifold_component(p, Val(1)) - zero_m = zero_vector(M.manifold, xp1) - zero_f = zero_vector(M.fiber, xp1) - vs = typeof(ProductRepr(zero_m, zero_f))[] - for bv in get_vectors(M.manifold, xp1, B.data.base_basis) - push!(vs, ProductRepr(bv, zero_f)) - end - for bv in get_vectors(M.fiber, xp1, B.data.vec_basis) - push!(vs, ProductRepr(zero_m, bv)) - end - return vs -end -function get_vectors( - M::VectorBundle, - p::ArrayPartition, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:Manifolds.VectorBundleBasisData}, -) where {𝔽} - xp1 = submanifold_component(p, Val(1)) - zero_m = zero_vector(M.manifold, xp1) - zero_f = zero_vector(M.fiber, xp1) - vs = typeof(ArrayPartition(zero_m, zero_f))[] - for bv in get_vectors(M.manifold, xp1, B.data.base_basis) - push!(vs, ArrayPartition(bv, zero_f)) - end - for bv in get_vectors(M.fiber, xp1, B.data.vec_basis) - push!(vs, ArrayPartition(zero_m, bv)) - end - return vs -end -function get_vectors(M::VectorBundleFibers, p, B::CachedBasis) - return get_vectors(M.manifold, p, B) -end -function get_vectors(M::TangentSpaceAtPoint, p, B::CachedBasis) - return get_vectors(M.fiber.manifold, M.point, B) -end - -""" - getindex(p::ProductRepr, M::VectorBundle, s::Symbol) - p[M::VectorBundle, s] - -Access the element(s) at index `s` of a point `p` on a [`VectorBundle`](@ref) `M` by -using the symbols `:point` and `:vector` for the base and vector component, respectively. -""" -@inline function Base.getindex(p::ProductRepr, M::VectorBundle, s::Symbol) - (s === :point) && return submanifold_component(M, p, Val(1)) - (s === :vector) && return submanifold_component(M, p, Val(2)) - return throw(DomainError(s, "unknown component $s on $M.")) -end -""" - getindex(p::ArrayPartition, M::VectorBundle, s::Symbol) - p[M::VectorBundle, s] - -Access the element(s) at index `s` of a point `p` on a [`VectorBundle`](@ref) `M` by -using the symbols `:point` and `:vector` for the base and vector component, respectively. -""" -@inline function Base.getindex(p::ArrayPartition, M::VectorBundle, s::Symbol) - (s === :point) && return p.x[1] - (s === :vector) && return p.x[2] - return throw(DomainError(s, "unknown component $s on $M.")) -end - -@doc raw""" - injectivity_radius(M::TangentSpaceAtPoint) - -Return the injectivity radius on the [`TangentSpaceAtPoint`](@ref Manifolds.TangentSpaceAtPoint) `M`, which is $∞$. -""" -injectivity_radius(::TangentSpaceAtPoint) = Inf """ injectivity_radius(M::TangentBundle) @@ -623,12 +158,12 @@ function injectivity_radius(M::TangentBundle) end """ - inner(B::VectorBundleFibers, p, X, Y) + inner(B::BundleFibers, p, X, Y) Inner product of vectors `X` and `Y` from the vector space of type `B.fiber` at point `p` from manifold `B.manifold`. """ -function inner(B::VectorBundleFibers, p, X, Y) +function inner(B::BundleFibers, p, X, Y) return error( "inner not defined for vector space family of type $(typeof(B)), " * "point of type $(typeof(p)) and " * @@ -658,37 +193,27 @@ The inner product is calculated as $⟨X, Y⟩_p = ⟨V_{X,M}, V_{Y,M}⟩_{x_p} + ⟨V_{X,F}, V_{Y,F}⟩_{V_p}.$ """ -function inner(B::VectorBundle, p, X, Y) +function inner(B::FiberBundle, p, X, Y) px, Vx = submanifold_components(B.manifold, p) VXM, VXF = submanifold_components(B.manifold, X) VYM, VYF = submanifold_components(B.manifold, Y) # for tangent bundle Vx is discarded by the method of inner for TangentSpaceAtPoint # and px is actually used as the base point - return inner(B.manifold, px, VXM, VYM) + - inner(VectorSpaceAtPoint(B.fiber, px), Vx, VXF, VYF) + return inner(B.manifold, px, VXM, VYM) + inner(FiberAtPoint(B.fiber, px), Vx, VXF, VYF) end -""" - inner(M::TangentSpaceAtPoint, p, X, Y) - -Inner product of vectors `X` and `Y` from the tangent space at `M`. -""" -function inner(M::TangentSpaceAtPoint, p, X, Y) - return inner(M.fiber.manifold, M.point, X, Y) -end - -function _inverse_retract(M::VectorBundle, p, q, ::VectorBundleInverseProductRetraction) +function _inverse_retract(M::FiberBundle, p, q, ::FiberBundleInverseProductRetraction) return inverse_retract_product(M, p, q) end -function _inverse_retract!(M::VectorBundle, X, p, q, ::VectorBundleInverseProductRetraction) +function _inverse_retract!(M::FiberBundle, X, p, q, ::FiberBundleInverseProductRetraction) return inverse_retract_product!(M, X, p, q) end """ inverse_retract_product(M::VectorBundle, p, q) -Compute the allocating variant of the [`VectorBundleInverseProductRetraction`](@ref), +Compute the allocating variant of the [`FiberBundleInverseProductRetraction`](@ref), which by default allocates and calls `inverse_retract_product!`. """ function inverse_retract_product(M::VectorBundle, p, q) @@ -701,34 +226,11 @@ function inverse_retract_product!(B::VectorBundle, X, p, q) py, Vy = submanifold_components(B.manifold, q) VXM, VXF = submanifold_components(B.manifold, X) log!(B.manifold, VXM, px, py) - vector_transport_to!(B.fiber, VXF, py, Vy, px, B.vector_transport.method_vector) + vector_transport_to!(B.fiber, VXF, py, Vy, px, B.vector_transport.method_fiber) copyto!(VXF, VXF - Vx) return X end -function _isapprox(B::VectorBundle, p, q; kwargs...) - xp, Vp = submanifold_components(B.manifold, p) - xq, Vq = submanifold_components(B.manifold, q) - return isapprox(B.manifold, xp, xq; kwargs...) && - isapprox(VectorSpaceAtPoint(B.fiber, xp), Vp, Vq; kwargs...) -end -function _isapprox(B::VectorBundle, p, X, Y; kwargs...) - px, Vx = submanifold_components(B.manifold, p) - VXM, VXF = submanifold_components(B.manifold, X) - VYM, VYF = submanifold_components(B.manifold, Y) - return isapprox(B.manifold, VXM, VYM; kwargs...) && - isapprox(VectorSpaceAtPoint(B.fiber, px), VXF, VYF; kwargs...) -end -function _isapprox(M::TangentSpaceAtPoint, X, Y; kwargs...) - return isapprox(M.fiber.manifold, M.point, X, Y; kwargs...) -end - -""" - is_flat(::TangentSpaceAtPoint) - -Return true. [`TangentSpaceAtPoint`](@ref Manifolds.TangentSpaceAtPoint) is a flat manifold. -""" -is_flat(::TangentSpaceAtPoint) = true """ is_flat(::VectorBundle) @@ -737,37 +239,13 @@ Return true if the underlying manifold of [`VectorBundle`](@ref) `M` is flat. is_flat(M::VectorBundle) = is_flat(M.manifold) """ - log(M::TangentSpaceAtPoint, p, q) - -Logarithmic map on the tangent space manifold `M`, calculated as the difference of tangent -vectors `q` and `p` from `M`. -""" -log(::TangentSpaceAtPoint, ::Any...) -function log!(::TangentSpaceAtPoint, X, p, q) - copyto!(X, q - p) - return X -end - -function manifold_dimension(B::VectorBundle) - return manifold_dimension(B.manifold) + vector_space_dimension(B.fiber) -end -function manifold_dimension(M::VectorSpaceAtPoint) - return vector_space_dimension(M.fiber) -end - -""" - norm(B::VectorBundleFibers, p, q) + norm(B::BundleFibers, p, q) Norm of the vector `X` from the vector space of type `B.fiber` at point `p` from manifold `B.manifold`. """ LinearAlgebra.norm(B::VectorBundleFibers, p, X) = sqrt(inner(B, p, X, X)) LinearAlgebra.norm(B::TangentBundleFibers, p, X) = norm(B.manifold, p, X) -LinearAlgebra.norm(M::VectorSpaceAtPoint, p, X) = norm(M.fiber.manifold, M.point, X) - -function parallel_transport_to!(M::TangentSpaceAtPoint, Y, p, X, q) - return copyto!(M.fiber.manifold, Y, p, X) -end @doc raw""" project(B::VectorBundle, p) @@ -786,14 +264,6 @@ and then projecting the vector $V_p$ to the tangent space $T_{x_p}\mathcal M$. """ project(::VectorBundle, ::Any) -@doc raw""" - project(M::TangentSpaceAtPoint, p) - -Project the point `p` from the tangent space `M`, that is project the vector `p` -tangent at `M.point`. -""" -project(::TangentSpaceAtPoint, ::Any) - function project!(B::VectorBundle, q, p) px, pVx = submanifold_components(B.manifold, p) qx, qVx = submanifold_components(B.manifold, q) @@ -801,9 +271,6 @@ function project!(B::VectorBundle, q, p) project!(B.manifold, qVx, qx, pVx) return q end -function project!(M::TangentSpaceAtPoint, q, p) - return project!(M.fiber.manifold, q, M.point, p) -end @doc raw""" project(B::VectorBundle, p, X) @@ -824,13 +291,6 @@ The projection is calculated by projecting $V_{X,M}$ to tangent space $T_{x_p}\m and then projecting the vector $V_{X,F}$ to the fiber $F$. """ project(::VectorBundle, ::Any, ::Any) -@doc raw""" - project(M::TangentSpaceAtPoint, p, X) - -Project the vector `X` from the tangent space `M`, that is project the vector `X` -tangent at `M.point`. -""" -project(::TangentSpaceAtPoint, ::Any, ::Any) function project!(B::VectorBundle, Y, p, X) px, Vx = submanifold_components(B.manifold, p) @@ -840,16 +300,13 @@ function project!(B::VectorBundle, Y, p, X) project!(B.manifold, VYF, px, VXF) return Y end -function project!(M::TangentSpaceAtPoint, Y, p, X) - return project!(M.fiber.manifold, Y, M.point, X) -end """ - project(B::VectorBundleFibers, p, X) + project(B::BundleFibers, p, X) Project vector `X` from the vector space of type `B.fiber` at point `p`. """ -function project(B::VectorBundleFibers, p, X) +function project(B::BundleFibers, p, X) Y = allocate_result(B, project, p, X) return project!(B, Y, p, X) end @@ -857,41 +314,24 @@ end function project!(B::TangentBundleFibers, Y, p, X) return project!(B.manifold, Y, p, X) end -function project!(B::VectorBundleFibers, Y, p, X) +function project!(B::BundleFibers, Y, p, X) return error( "project! not implemented for vector space family of type $(typeof(B)), output vector of type $(typeof(Y)) and input vector at point $(typeof(p)) with type of w $(typeof(X)).", ) end -function Random.rand!(rng::AbstractRNG, M::VectorBundle, pX; vector_at=nothing) - pXM, pXF = submanifold_components(M.manifold, pX) - if vector_at === nothing - rand!(rng, M.manifold, pXM) - rand!(rng, VectorSpaceAtPoint(M.fiber, pXM), pXF) - else - vector_atM, vector_atF = submanifold_components(M.manifold, vector_at) - rand!(rng, M.manifold, pXM; vector_at=vector_atM) - rand!(rng, VectorSpaceAtPoint(M.fiber, pXM), pXF; vector_at=vector_atF) - end - return pX -end -function Random.rand!(rng::AbstractRNG, M::TangentSpaceAtPoint, X; vector_at=nothing) - rand!(rng, M.fiber.manifold, X; vector_at=M.point) - return X -end - -function _retract(M::VectorBundle, p, X, t::Number, ::VectorBundleProductRetraction) +function _retract(M::VectorBundle, p, X, t::Number, ::FiberBundleProductRetraction) return retract_product(M, p, X, t) end -function _retract!(M::VectorBundle, q, p, X, t::Number, ::VectorBundleProductRetraction) +function _retract!(M::VectorBundle, q, p, X, t::Number, ::FiberBundleProductRetraction) return retract_product!(M, q, p, X, t) end """ retract_product(M::VectorBundle, p, q, t::Number) -Compute the allocating variant of the [`VectorBundleProductRetraction`](@ref), +Compute the allocating variant of the [`FiberBundleProductRetraction`](@ref), which by default allocates and calls `retract_product!`. """ function retract_product(M::VectorBundle, p, X, t::Number) @@ -971,101 +411,25 @@ function retract_sasaki!(B::TangentBundle, q, p, X, t::Number, m::SasakiRetracti return q end -""" - setindex!(p::ProductRepr, val, M::VectorBundle, s::Symbol) - p[M::VectorBundle, s] = val - -Set the element(s) at index `s` of a point `p` on a [`VectorBundle`](@ref) `M` to `val` by -using the symbols `:point` and `:vector` for the base and vector component, respectively. - -!!! note - - The *content* of element of `p` is replaced, not the element itself. -""" -@inline function Base.setindex!(x::ProductRepr, val, M::VectorBundle, s::Symbol) - if s === :point - return copyto!(submanifold_component(M, x, Val(1)), val) - elseif s === :vector - return copyto!(submanifold_component(M, x, Val(2)), val) - else - throw(DomainError(s, "unknown component $s on $M.")) - end -end -""" - setindex!(p::ArrayPartition, val, M::VectorBundle, s::Symbol) - p[M::VectorBundle, s] = val - -Set the element(s) at index `s` of a point `p` on a [`VectorBundle`](@ref) `M` to `val` by -using the symbols `:point` and `:vector` for the base and vector component, respectively. - -!!! note - - The *content* of element of `p` is replaced, not the element itself. -""" -@inline function Base.setindex!(x::ArrayPartition, val, M::VectorBundle, s::Symbol) - if s === :point - return copyto!(x.x[1], val) - elseif s === :vector - return copyto!(x.x[2], val) - else - throw(DomainError(s, "unknown component $s on $M.")) - end -end - -function representation_size(B::VectorBundleFibers{<:TCoTSpaceType}) +function representation_size(B::CotangentBundleFibers) return representation_size(B.manifold) end -function representation_size(B::TangentSpaceAtPoint) - return representation_size(B.fiber.manifold) +function representation_size(B::TangentBundleFibers) + return representation_size(B.manifold) end function Base.show(io::IO, tpt::TensorProductType) return print(io, "TensorProductType(", join(tpt.spaces, ", "), ")") end -function Base.show(io::IO, fiber::VectorBundleFibers) - return print(io, "VectorBundleFibers($(fiber.fiber), $(fiber.manifold))") -end -function Base.show(io::IO, ::MIME"text/plain", vs::VectorSpaceAtPoint) - summary(io, vs) - println(io, "\nFiber:") - pre = " " - sf = sprint(show, "text/plain", vs.fiber; context=io, sizehint=0) - sf = replace(sf, '\n' => "\n$(pre)") - println(io, pre, sf) - println(io, "Base point:") - sp = sprint(show, "text/plain", vs.point; context=io, sizehint=0) - sp = replace(sp, '\n' => "\n$(pre)") - return print(io, pre, sp) +function Base.show(io::IO, vb::VectorBundle) + return print(io, "VectorBundle($(vb.type.fiber), $(vb.manifold))") end -function Base.show(io::IO, ::MIME"text/plain", TpM::TangentSpaceAtPoint) - println(io, "Tangent space to the manifold $(base_manifold(TpM)) at point:") - pre = " " - sp = sprint(show, "text/plain", TpM.point; context=io, sizehint=0) - sp = replace(sp, '\n' => "\n$(pre)") - return print(io, pre, sp) +function Base.show(io::IO, vbf::VectorBundleFibers) + return print(io, "VectorBundleFibers($(vbf.fiber.fiber), $(vbf.manifold))") end -Base.show(io::IO, vb::VectorBundle) = print(io, "VectorBundle($(vb.type), $(vb.manifold))") Base.show(io::IO, vb::TangentBundle) = print(io, "TangentBundle($(vb.manifold))") Base.show(io::IO, vb::CotangentBundle) = print(io, "CotangentBundle($(vb.manifold))") -""" - allocate_result(B::VectorBundleFibers, f, x...) - -Allocates an array for the result of function `f` that is -an element of the vector space of type `B.fiber` on manifold `B.manifold` -and arguments `x...` for implementing the non-modifying operation -using the modifying operation. -""" -@inline function allocate_result(B::VectorBundleFibers, f::TF, x...) where {TF} - if length(x) == 0 - # TODO: this may be incorrect when point and tangent vector representation are - # different for the manifold but there is no easy and generic way around that - return allocate_result(B.manifold, f) - else - T = allocate_result_type(B, f, x) - return allocate(x[1], T) - end -end @inline function allocate_result(B::TangentBundleFibers, f::typeof(zero_vector), x...) return allocate_result(B.manifold, f, x...) end @@ -1074,41 +438,32 @@ end end """ - allocate_result_type(B::VectorBundleFibers, f, args::NTuple{N,Any}) where N + fiber_bundle_transport(fiber::FiberType, M::AbstractManifold) -Return type of element of the array that will represent the result of -function `f` for representing an operation with result in the vector space `fiber` -for manifold `M` on given arguments (passed at a tuple). -""" -@inline function allocate_result_type( - ::VectorBundleFibers, - f::TF, - args::NTuple{N,Any}, -) where {TF,N} - return typeof(mapreduce(eti -> one(number_eltype(eti)), +, args)) -end - -""" - vector_bundle_transport(fiber::VectorSpaceType, M::AbstractManifold) - -Determine the vector tranport used for [`exp`](@ref exp(::VectorBundle, ::Any...)) and -[`log`](@ref log(::VectorBundle, ::Any...)) maps on a vector bundle with vector space type +Determine the vector tranport used for [`exp`](@ref exp(::FiberBundle, ::Any...)) and +[`log`](@ref log(::FiberBundle, ::Any...)) maps on a vector bundle with fiber type `fiber` and manifold `M`. """ -vector_bundle_transport(::VectorSpaceType, ::AbstractManifold) = ParallelTransport() - -function vector_space_dimension(B::VectorBundleFibers) - return vector_space_dimension(B.manifold, B.fiber) -end +fiber_bundle_transport(::VectorSpaceType, ::AbstractManifold) = ParallelTransport() function vector_space_dimension(M::AbstractManifold, V::TensorProductType) dim = 1 for space in V.spaces - dim *= vector_space_dimension(M, space) + dim *= fiber_dimension(M, space) end return dim end +function vector_space_dimension(B::TangentBundleFibers) + return manifold_dimension(B.manifold) +end +function vector_space_dimension(B::CotangentBundleFibers) + return manifold_dimension(B.manifold) +end +function vector_space_dimension(B::VectorBundleFibers) + return vector_space_dimension(B.manifold, B.fiber.fiber) +end + function vector_transport_direction(M::VectorBundle, p, X, d) return vector_transport_direction(M, p, X, d, M.vector_transport) end @@ -1127,14 +482,14 @@ function _vector_transport_direction( p, X, d, - m::VectorBundleProductVectorTransport, + m::FiberBundleProductVectorTransport, ) px, pVx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) dx, dVx = submanifold_components(M.manifold, d) return ArrayPartition( vector_transport_direction(M.manifold, px, VXM, dx, m.method_point), - vector_transport_direction(M.fiber, px, VXF, dx, m.method_vector), + vector_transport_direction(M.fiber, px, VXF, dx, m.method_fiber), ) end @@ -1148,29 +503,29 @@ function _vector_transport_direction!( p, X, d, - m::VectorBundleProductVectorTransport, + m::FiberBundleProductVectorTransport, ) VYM, VYF = submanifold_components(M.manifold, Y) px, pVx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) dx, dVx = submanifold_components(M.manifold, d) vector_transport_direction!(M.manifold, VYM, px, VXM, dx, m.method_point), - vector_transport_direction!(M.manifold, VYF, px, VXF, dx, m.method_vector), + vector_transport_direction!(M.manifold, VYF, px, VXF, dx, m.method_fiber), return Y end @doc raw""" - vector_transport_to(M::VectorBundle, p, X, q, m::VectorBundleProductVectorTransport) + vector_transport_to(M::VectorBundle, p, X, q, m::FiberBundleProductVectorTransport) Compute the vector transport the tangent vector `X`at `p` to `q` on the -[`VectorBundle`](@ref) `M` using the [`VectorBundleProductVectorTransport`](@ref) `m`. +[`VectorBundle`](@ref) `M` using the [`FiberBundleProductVectorTransport`](@ref) `m`. """ vector_transport_to( ::VectorBundle, ::Any, ::Any, ::Any, - ::VectorBundleProductVectorTransport, + ::FiberBundleProductVectorTransport, ) function _vector_transport_to( @@ -1178,14 +533,14 @@ function _vector_transport_to( p, X, q, - m::VectorBundleProductVectorTransport, + m::FiberBundleProductVectorTransport, ) px, pVx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) qx, qVx = submanifold_components(M.manifold, q) return ArrayPartition( vector_transport_to(M.manifold, px, VXM, qx, m.method_point), - vector_transport_to(M.manifold, px, VXF, qx, m.method_vector), + vector_transport_to(M.manifold, px, VXF, qx, m.method_fiber), ) end @@ -1202,26 +557,16 @@ function vector_transport_to!( p, X, q, - m::VectorBundleProductVectorTransport, + m::FiberBundleProductVectorTransport, ) px, pVx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) VYM, VYF = submanifold_components(M.manifold, Y) qx, qVx = submanifold_components(M.manifold, q) vector_transport_to!(M.manifold, VYM, px, VXM, qx, m.method_point) - vector_transport_to!(M.manifold, VYF, px, VXF, qx, m.method_vector) + vector_transport_to!(M.manifold, VYF, px, VXF, qx, m.method_fiber) return Y end -function vector_transport_to!( - M::TangentSpaceAtPoint, - Y, - p, - X, - q, - m::AbstractVectorTransportMethod, -) - return copyto!(M.fiber.manifold, Y, p, X) -end function vector_transport_to!( M::TangentBundleFibers, Y, @@ -1234,30 +579,13 @@ function vector_transport_to!( return Y end -@inline function Base.view(x::ArrayPartition, M::VectorBundle, s::Symbol) - (s === :point) && return x.x[1] - (s === :vector) && return x.x[2] - throw(DomainError(s, "unknown component $s on $M.")) -end - """ - zero_vector(B::VectorBundleFibers, p) - -Compute the zero vector from the vector space of type `B.fiber` at point `p` -from manifold `B.manifold`. -""" -function zero_vector(B::VectorBundleFibers, p) - X = allocate_result(B, zero_vector, p) - return zero_vector!(B, X, p) -end - -""" - zero_vector!(B::VectorBundleFibers, X, p) + zero_vector!(B::BundleFibers, X, p) Save the zero vector from the vector space of type `B.fiber` at point `p` from manifold `B.manifold` to `X`. """ -function zero_vector!(B::VectorBundleFibers, X, p) +function zero_vector!(B::BundleFibers, X, p) return error( "zero_vector! not implemented for vector space family of type $(typeof(B)).", ) @@ -1265,42 +593,3 @@ end function zero_vector!(B::TangentBundleFibers, X, p) return zero_vector!(B.manifold, X, p) end - -@doc raw""" - zero_vector(B::VectorBundle, p) - -Zero tangent vector at point `p` from the vector bundle `B` -over manifold `B.fiber` (denoted $\mathcal M$). The zero vector belongs to the space $T_{p}B$ - -Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - -The zero vector is calculated as - -$\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)$ - -where $\mathbf{0}_{x_p}$ is the zero tangent vector from $T_{x_p}\mathcal M$ and -$\mathbf{0}_F$ is the zero element of the vector space $F$. -""" -zero_vector(::VectorBundle, ::Any...) - -@doc raw""" - zero_vector(M::TangentSpaceAtPoint, p) - -Zero tangent vector at point `p` from the tangent space `M`, that is the zero tangent vector -at point `M.point`. -""" -zero_vector(::TangentSpaceAtPoint, ::Any...) - -function zero_vector!(B::VectorBundle, X, p) - xp, Vp = submanifold_components(B.manifold, p) - VXM, VXF = submanifold_components(B.manifold, X) - zero_vector!(B.manifold, VXM, xp) - zero_vector!(B.fiber, VXF, Vp) - return X -end -function zero_vector!(M::TangentSpaceAtPoint, X, p) - return zero_vector!(M.fiber.manifold, X, M.point) -end diff --git a/test/manifolds/fiber.jl b/test/manifolds/fiber.jl new file mode 100644 index 0000000000..ce052a0149 --- /dev/null +++ b/test/manifolds/fiber.jl @@ -0,0 +1,44 @@ +include("../utils.jl") + +using RecursiveArrayTools + +struct TestVectorSpaceType <: VectorSpaceType end + +@testset "spaces at point" begin + M = Sphere(2) + @testset "tangent and cotangent space" begin + p = [1.0, 0.0, 0.0] + t_p = TangentSpaceAtPoint(M, p) + t_p2 = TangentSpace(M, p) + @test t_p == t_p2 + ct_p = CotangentSpaceAtPoint(M, p) + t_ps = sprint(show, "text/plain", t_p) + sp = sprint(show, "text/plain", p) + sp = replace(sp, '\n' => "\n ") + t_ps_test = "Tangent space to the manifold $(M) at point:\n $(sp)" + @test t_ps == t_ps_test + @test base_manifold(t_p) == M + @test base_manifold(ct_p) == M + @test t_p.fiber.manifold == M + @test ct_p.fiber.manifold == M + @test t_p.fiber.fiber == Manifolds.TangentFiber + @test ct_p.fiber.fiber == Manifolds.CotangentFiber + @test t_p.point == p + @test ct_p.point == p + @test injectivity_radius(t_p) == Inf + @test representation_size(t_p) == representation_size(M) + X = [0.0, 0.0, 1.0] + @test embed(t_p, X) == X + @test embed(t_p, X, X) == X + # generic vector space at + fiber = VectorBundleFibers(TestVectorSpaceType(), M) + X_p = Manifolds.FiberAtPoint(fiber, p) + X_ps = sprint(show, "text/plain", X_p) + fiber_s = sprint(show, "text/plain", fiber) + X_ps_test = "$(typeof(X_p))\nFiber:\n $(fiber_s)\nBase point:\n $(sp)" + @test X_ps == X_ps_test + @test_throws ErrorException project(fiber, p, X) + @test_throws ErrorException norm(fiber, p, X) + @test_throws ErrorException distance(fiber, p, X, X) + end +end diff --git a/test/manifolds/vector_bundle.jl b/test/manifolds/vector_bundle.jl index b8d8018fd2..2660882ab0 100644 --- a/test/manifolds/vector_bundle.jl +++ b/test/manifolds/vector_bundle.jl @@ -6,8 +6,8 @@ struct TestVectorSpaceType <: VectorSpaceType end @testset "Tangent bundle" begin M = Sphere(2) - m_prod_retr = Manifolds.VectorBundleProductRetraction() - m_prod_invretr = Manifolds.VectorBundleInverseProductRetraction() + m_prod_retr = Manifolds.FiberBundleProductRetraction() + m_prod_invretr = Manifolds.FiberBundleInverseProductRetraction() m_sasaki = SasakiRetraction(5) @testset "Nice access to vector bundle components" begin @@ -62,7 +62,7 @@ struct TestVectorSpaceType <: VectorSpaceType end @test default_inverse_retraction_method(TB) === m_prod_invretr @test default_retraction_method(TB) == m_prod_retr @test default_vector_transport_method(TB) isa - Manifolds.VectorBundleProductVectorTransport + Manifolds.FiberBundleProductVectorTransport CTB = CotangentBundle(M) @test sprint(show, CTB) == "CotangentBundle(Sphere(2, ℝ))" @test sprint(show, VectorBundle(TestVectorSpaceType(), M)) == @@ -145,7 +145,7 @@ struct TestVectorSpaceType <: VectorSpaceType end pts_tb[1], Xir, pts_tb[2], - Manifolds.VectorBundleProductVectorTransport(), + Manifolds.FiberBundleProductVectorTransport(), ) @test is_vector(TB, pts_tb[2], Xir2) @@ -183,41 +183,6 @@ struct TestVectorSpaceType <: VectorSpaceType end VectorBundle{ℝ,Manifolds.CotangentSpaceType,Sphere{2,ℝ}} @test base_manifold(TangentBundle(M)) == M - @testset "spaces at point" begin - p = [1.0, 0.0, 0.0] - t_p = TangentSpaceAtPoint(M, p) - t_p2 = TangentSpace(M, p) - @test t_p == t_p2 - ct_p = CotangentSpaceAtPoint(M, p) - t_ps = sprint(show, "text/plain", t_p) - sp = sprint(show, "text/plain", p) - sp = replace(sp, '\n' => "\n ") - t_ps_test = "Tangent space to the manifold $(M) at point:\n $(sp)" - @test t_ps == t_ps_test - @test base_manifold(t_p) == M - @test base_manifold(ct_p) == M - @test t_p.fiber.manifold == M - @test ct_p.fiber.manifold == M - @test t_p.fiber.fiber == TangentSpace - @test ct_p.fiber.fiber == CotangentSpace - @test t_p.point == p - @test ct_p.point == p - @test injectivity_radius(t_p) == Inf - @test representation_size(t_p) == representation_size(M) - X = [0.0, 0.0, 1.0] - @test embed(t_p, X) == X - @test embed(t_p, X, X) == X - # generic vector space at - fiber = VectorBundleFibers(TestVectorSpaceType(), M) - X_p = VectorSpaceAtPoint(fiber, p) - X_ps = sprint(show, "text/plain", X_p) - fiber_s = sprint(show, "text/plain", fiber) - X_ps_test = "$(typeof(X_p))\nFiber:\n $(fiber_s)\nBase point:\n $(sp)" - @test X_ps == X_ps_test - @test_throws ErrorException project(fiber, p, X) - @test_throws ErrorException norm(fiber, p, X) - @test_throws ErrorException distance(fiber, p, X, X) - end @testset "tensor product" begin TT = Manifolds.TensorProductType(TangentSpace, TangentSpace) @@ -260,7 +225,7 @@ struct TestVectorSpaceType <: VectorSpaceType end ) ppt = ParallelTransport() - tbvt = Manifolds.VectorBundleProductVectorTransport(ppt, ppt) + tbvt = Manifolds.FiberBundleProductVectorTransport(ppt, ppt) @test TangentBundle(M, tbvt).vector_transport === tbvt @test CotangentBundle(M, tbvt).vector_transport === tbvt @test VectorBundle(TangentSpace, M, tbvt).vector_transport === tbvt diff --git a/test/runtests.jl b/test/runtests.jl index f9ff618ed4..455c6499f2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -176,6 +176,7 @@ include("utils.jl") include_test("manifolds/product_manifold.jl") include_test("manifolds/power_manifold.jl") include_test("manifolds/quotient_manifold.jl") + include_test("manifolds/fiber.jl") include_test("manifolds/vector_bundle.jl") include_test("manifolds/graph.jl") diff --git a/tutorials/rigid-body-dynamics.qmd b/tutorials/rigid-body-dynamics.qmd index 0828884419..6b65c5007a 100644 --- a/tutorials/rigid-body-dynamics.qmd +++ b/tutorials/rigid-body-dynamics.qmd @@ -201,7 +201,12 @@ There is, however, one small caveat: `X_X` was brought from the double tangent b So we may use the connection from $\operatorname{SE}(2)$ but we may also use the Levi-Civita connection of the Sasaki metric on $T\operatorname{SE}(2)$. We shall explore this choice later in the tutorial. -For simplicity again we will use a first order forward Euler discretization to solve our ODE (see [technical note](@sec-forward-euler) if you disapprove). +For simplicity again we will use a first order forward Euler discretization to solve our ODE (see `[technical note](@ref sec-forward-euler)`{=commonmark} if you disapprove). +For the initial conditions in our example we will use `p` and `X` from earlier examples and add `X_X`: +```{julia} +X_X = hat(SE2, se2_id, [0.5, -1.0, pi/6]) +``` + ## Technical notes @@ -241,6 +246,6 @@ In this way we get an isomorphism between tangent and cotangent bundles. Note that a generic description of a phase space doesn't require a Riemannian metric. Generic tools come from symplectic geometry which imposes a less rich structure. -### Forward Euler? Really? {#sec-forward-euler} +### `[Forward Euler? Really?](@id sec-forward-euler)`{=commonmark} Yes, for simplicity. For a start. I may try a higher order or a symplectic solver later. You can do that too! From 950a74e83b7ec155b9fa974cc7e2049d37e9094b Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 26 Jul 2023 19:40:36 +0200 Subject: [PATCH 07/81] fix product manifold --- src/manifolds/Fiber.jl | 3 +++ test/manifolds/product_manifold.jl | 18 +++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/manifolds/Fiber.jl b/src/manifolds/Fiber.jl index 6f225ebb3f..2b03b0a256 100644 --- a/src/manifolds/Fiber.jl +++ b/src/manifolds/Fiber.jl @@ -62,6 +62,9 @@ const VectorBundleFibers{TVS,TM} = BundleFibers{ function VectorBundleFibers(fiber::VectorSpaceType, M::AbstractManifold) return BundleFibers(VectorSpaceFiberType(fiber), M) end +function VectorBundleFibers(fiber::VectorSpaceFiberType, M::AbstractManifold) + return BundleFibers(fiber, M) +end const TangentBundleFibers{M} = BundleFibers{TangentFiberType,M} where {M<:AbstractManifold} diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index c9eb3fddc1..fff71cc907 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -646,36 +646,36 @@ using RecursiveArrayTools: ArrayPartition } @test Manifolds.default_retraction_method(Mstb) === ProductRetraction( ExponentialRetraction(), - Manifolds.VectorBundleProductRetraction(), + Manifolds.FiberBundleProductRetraction(), ) @test Manifolds.default_retraction_method(Mstb, T_p_ap) === ProductRetraction( ExponentialRetraction(), - Manifolds.VectorBundleProductRetraction(), + Manifolds.FiberBundleProductRetraction(), ) @test Manifolds.default_retraction_method(Mstb, T_p_pr) === ProductRetraction( ExponentialRetraction(), - Manifolds.VectorBundleProductRetraction(), + Manifolds.FiberBundleProductRetraction(), ) @test Manifolds.default_inverse_retraction_method(Mstb) === Manifolds.InverseProductRetraction( LogarithmicInverseRetraction(), - Manifolds.VectorBundleInverseProductRetraction(), + Manifolds.FiberBundleInverseProductRetraction(), ) @test Manifolds.default_inverse_retraction_method(Mstb, T_p_ap) === Manifolds.InverseProductRetraction( LogarithmicInverseRetraction(), - Manifolds.VectorBundleInverseProductRetraction(), + Manifolds.FiberBundleInverseProductRetraction(), ) @test Manifolds.default_inverse_retraction_method(Mstb, T_p_pr) === Manifolds.InverseProductRetraction( LogarithmicInverseRetraction(), - Manifolds.VectorBundleInverseProductRetraction(), + Manifolds.FiberBundleInverseProductRetraction(), ) @test Manifolds.default_vector_transport_method(Mstb) === ProductVectorTransport( ParallelTransport(), - Manifolds.VectorBundleProductVectorTransport( + Manifolds.FiberBundleProductVectorTransport( ParallelTransport(), ParallelTransport(), ), @@ -683,7 +683,7 @@ using RecursiveArrayTools: ArrayPartition @test Manifolds.default_vector_transport_method(Mstb, T_p_pr) === ProductVectorTransport( ParallelTransport(), - Manifolds.VectorBundleProductVectorTransport( + Manifolds.FiberBundleProductVectorTransport( ParallelTransport(), ParallelTransport(), ), @@ -691,7 +691,7 @@ using RecursiveArrayTools: ArrayPartition @test Manifolds.default_vector_transport_method(Mstb, T_p_pr) === ProductVectorTransport( ParallelTransport(), - Manifolds.VectorBundleProductVectorTransport( + Manifolds.FiberBundleProductVectorTransport( ParallelTransport(), ParallelTransport(), ), From de7d281d186927339a225779d9ae9620d649cc6b Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Thu, 27 Jul 2023 15:50:01 +0200 Subject: [PATCH 08/81] direct sum bundle --- src/Manifolds.jl | 1 + src/manifolds/DirectSumBundle.jl | 229 ++++++++++++++++++++++++++++ src/manifolds/Fiber.jl | 1 + src/manifolds/FiberBundle.jl | 2 +- test/ambiguities.jl | 4 +- test/manifolds/direct_sum_bundle.jl | 119 +++++++++++++++ test/runtests.jl | 1 + 7 files changed, 354 insertions(+), 3 deletions(-) create mode 100644 src/manifolds/DirectSumBundle.jl create mode 100644 test/manifolds/direct_sum_bundle.jl diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 942ae06c94..95435259bb 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -337,6 +337,7 @@ include("manifolds/QuotientManifold.jl") include("manifolds/Fiber.jl") include("manifolds/FiberBundle.jl") include("manifolds/VectorBundle.jl") +include("manifolds/DirectSumBundle.jl") include("groups/group.jl") # Features I: Which are extended on Meta Manifolds diff --git a/src/manifolds/DirectSumBundle.jl b/src/manifolds/DirectSumBundle.jl new file mode 100644 index 0000000000..6cd05db62a --- /dev/null +++ b/src/manifolds/DirectSumBundle.jl @@ -0,0 +1,229 @@ + +struct DirectSumType{TS<:Tuple} <: VectorSpaceType + spaces::TS +end + +DirectSumType(spaces::VectorSpaceType...) = DirectSumType{typeof(spaces)}(spaces) + +function fiber_dimension(M::AbstractManifold, V::DirectSumType) + dim = 0 + for space in V.spaces + dim += fiber_dimension(M, space) + end + return dim +end + +function vector_space_dimension(M::AbstractManifold, V::DirectSumType) + dim = 0 + for space in V.spaces + dim += vector_space_dimension(M, space) + end + return dim +end + +const MultitangentBundle{N,𝔽,M} = VectorBundle{ + 𝔽, + DirectSumType{NTuple{N,TangentSpaceType}}, + M, +} where {𝔽,M<:AbstractManifold{𝔽}} + +function MultitangentBundle{N}(M::AbstractManifold) where {N} + return VectorBundle(DirectSumType(ntuple(f -> TangentSpace, N)), M) +end + +const MultitangentBundleFibers{N,M} = VectorBundleFibers{ + DirectSumType{NTuple{N,TangentSpaceType}}, + M, +} where {M<:AbstractManifold} + +function MultitangentBundleFibers{N}(M::AbstractManifold) where {N} + return VectorBundleFibers(DirectSumType(ntuple(f -> TangentSpace, N)), M) +end + +const MultitangentSpaceAtPoint{N,M} = + VectorSpaceAtPoint{𝔽,MultitangentBundleFibers{N,M}} where {𝔽,M<:AbstractManifold{𝔽}} +function MultitangentSpaceAtPoint{N}(M::AbstractManifold, p) where {N} + return VectorSpaceAtPoint(MultitangentBundleFibers{N}(M), p) +end + +@inline function allocate_result(B::MultitangentBundleFibers{N}, f::TF) where {N,TF} + return ArrayPartition(ntuple(_ -> allocate_result(B.manifold, f), Val(N))...) +end + +function default_inverse_retraction_method(::MultitangentBundle) + return FiberBundleInverseProductRetraction() +end + +function default_retraction_method(::MultitangentBundle) + return FiberBundleProductRetraction() +end + +function default_vector_transport_method(B::MultitangentBundle) + default_vt_m = default_vector_transport_method(B.manifold) + return FiberBundleProductVectorTransport(default_vt_m, default_vt_m) +end + +function get_basis(M::MultitangentBundleFibers, p, B::AbstractBasis{<:Any,TangentSpaceType}) + return get_basis(M.manifold, p, B) +end + +function get_coordinates(M::MultitangentBundleFibers, p, X, B::AbstractBasis) + return reduce(vcat, map(Xp -> get_coordinates(M.manifold, p, Xp, B), X.x)) +end + +function get_coordinates!(M::MultitangentBundleFibers, Y, p, X, B::AbstractBasis) + Y .= reduce(vcat, map(Xp -> get_coordinates(M.manifold, p, Xp, B), X.x)) + return Y +end + +function get_vector(M::MultitangentBundleFibers{N}, p, Xc, B::AbstractBasis) where {N} + d = manifold_dimension(M.manifold) + c_parts = ntuple(n -> view(Xc, (1 + ((n - 1) * d)):(n * d)), Val(N)) + return ArrayPartition(map(Xp -> get_vector(M.manifold, p, Xp, B), c_parts)...) +end + +function get_vector!(M::MultitangentBundleFibers{N}, Y, p, Xc, B::AbstractBasis) where {N} + d = manifold_dimension(M.manifold) + c_parts = ntuple(n -> view(Xc, (1 + ((n - 1) * d)):(n * d)), Val(N)) + map((Yp, Xcp) -> get_vector!(M.manifold, Yp, p, Xcp, B), Y.x, c_parts) + return Y +end + +function get_vectors(M::MultitangentBundleFibers{N}, p, B::CachedBasis) where {N} + bvs = get_vectors(M.manifold, p, B) + X0 = zero_vector(M.manifold, p) + vs = ArrayPartition{eltype(X0),NTuple{N,typeof(X0)}}[] + for i in 1:N + Xs1 = ntuple(_ -> X0, i - 1) + Xs2 = ntuple(_ -> X0, N - i) + for X in bvs + push!(vs, ArrayPartition(Xs1..., X, Xs2...)) + end + end + return vs +end + +function inner(M::MultitangentSpaceAtPoint, p, X, Y) + return sum(map((Xp, Yp) -> inner(M.fiber.manifold, M.point, Xp, Yp), X.x, Y.x)) +end +function inner(M::MultitangentBundleFibers, p, X, Y) + return sum(map((Xp, Yp) -> inner(M.manifold, p, X, Y), X.x, Y.x)) +end + +function Random.rand!(rng::AbstractRNG, M::MultitangentSpaceAtPoint, X; vector_at=nothing) + map(X.x) do Xp + return rand!(rng, M.fiber.manifold, Xp; vector_at=M.point) + end + return X +end + +function retract_product!(B::MultitangentBundle, q, p, X, t::Number) + tX = t * X + xp, Xps = submanifold_components(B.manifold, p) + xq, Xqs = submanifold_components(B.manifold, q) + VXM, VXFs = submanifold_components(B.manifold, tX) + # this temporary avoids overwriting `p` when `q` and `p` occupy the same memory + xqt = exp(B.manifold, xp, VXM) + map(Xps.x, Xqs.x, VXFs.x) do Xp, Xq, VXF + return vector_transport_direction!( + B.manifold, + Xq, + xp, + Xp + VXF, + VXM, + B.vector_transport.method_point, + ) + end + copyto!(B.manifold, xq, xqt) + return q +end + +function vector_transport_direction( + M::MultitangentBundleFibers, + p, + X, + d, + m::AbstractVectorTransportMethod, +) + return ArrayPartition(map(X.x) do VXF + return vector_transport_direction(M.manifold, p, VXF, d, m) + end) +end + +function _vector_transport_direction!( + M::MultitangentBundle, + Y, + p, + X, + d, + m::FiberBundleProductVectorTransport, +) + VYM, VYFs = submanifold_components(M.manifold, Y) + px, pVxs = submanifold_components(M.manifold, p) + VXM, VXFs = submanifold_components(M.manifold, X) + dx, dVxs = submanifold_components(M.manifold, d) + vector_transport_direction!(M.manifold, VYM, px, VXM, dx, m.method_point) + map(VYFs.x, VXFs.x) do VYF, VXF + return vector_transport_direction!(M.manifold, VYF, px, VXF, dx, m.method_fiber) + end + return Y +end + +function _vector_transport_to( + M::MultitangentBundle, + p, + X, + q, + m::FiberBundleProductVectorTransport, +) + px, pVx = submanifold_components(M.manifold, p) + VXM, VXFs = submanifold_components(M.manifold, X) + qx, qVx = submanifold_components(M.manifold, q) + vectors = map(VXFs.x) do VXF + return vector_transport_to(M.manifold, px, VXF, qx, m.method_fiber) + end + return ArrayPartition( + vector_transport_to(M.manifold, px, VXM, qx, m.method_point), + ArrayPartition(vectors...), + ) +end + +function vector_transport_to!( + M::MultitangentBundle, + Y, + p, + X, + q, + m::FiberBundleProductVectorTransport, +) + px, pVx = submanifold_components(M.manifold, p) + VXM, VXFs = submanifold_components(M.manifold, X) + VYM, VYFs = submanifold_components(M.manifold, Y) + qx, qVx = submanifold_components(M.manifold, q) + vector_transport_to!(M.manifold, VYM, px, VXM, qx, m.method_point) + map(VYFs.x, VXFs.x) do VYF, VXF + return vector_transport_to!(M.manifold, VYF, px, VXF, qx, m.method_fiber) + end + return Y +end +function vector_transport_to!( + M::MultitangentBundleFibers, + Y, + p, + X, + q, + m::AbstractVectorTransportMethod, +) + map(Y.x, X.x) do VYF, VXF + return vector_transport_to!(M.manifold, VYF, p, VXF, q, m) + end + return Y +end + +function zero_vector(B::MultitangentBundleFibers{N}, p) where {N} + return ArrayPartition(ntuple(_ -> zero_vector(B.manifold, p), Val(N))...) +end + +function zero_vector!(::MultitangentBundleFibers, X, p) + return fill!(X, 0) +end diff --git a/src/manifolds/Fiber.jl b/src/manifolds/Fiber.jl index 2b03b0a256..f8b5be78ba 100644 --- a/src/manifolds/Fiber.jl +++ b/src/manifolds/Fiber.jl @@ -225,6 +225,7 @@ function fiber_dimension(B::BundleFibers) end fiber_dimension(M::TangentBundleFibers) = manifold_dimension(M.manifold) fiber_dimension(M::CotangentBundleFibers) = manifold_dimension(M.manifold) +fiber_dimension(M::AbstractManifold, V::VectorSpaceFiberType) = fiber_dimension(M, V.fiber) fiber_dimension(M::AbstractManifold, ::CotangentSpaceType) = manifold_dimension(M) fiber_dimension(M::AbstractManifold, ::TangentSpaceType) = manifold_dimension(M) diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index 8aeaadaebe..9a926abbf2 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -202,7 +202,7 @@ function get_coordinates!( return Y end -function get_vector(M::FiberBundle, p, X, B::AbstractBasis) +function _get_vector(M::FiberBundle, p, X, B::AbstractBasis) n = manifold_dimension(M.manifold) xp1 = submanifold_component(p, Val(1)) return ArrayPartition( diff --git a/test/ambiguities.jl b/test/ambiguities.jl index 4c94b54232..9af12a6e78 100644 --- a/test/ambiguities.jl +++ b/test/ambiguities.jl @@ -1,5 +1,5 @@ @testset "Ambiguities" begin - if VERSION.prerelease == () && !Sys.iswindows() && VERSION < v"1.9.0" + if VERSION.prerelease == () && !Sys.iswindows() && VERSION < v"1.10.0" mbs = Test.detect_ambiguities(ManifoldsBase) # Interims solution until we follow what was proposed in # https://discourse.julialang.org/t/avoid-ambiguities-with-individual-number-element-identity/62465/2 @@ -17,7 +17,7 @@ # Interims solution until we follow what was proposed in # https://discourse.julialang.org/t/avoid-ambiguities-with-individual-number-element-identity/62465/2 fms = filter(x -> !any(has_type_in_signature.(x, Identity)), ms) - FMS_LIMIT = 39 + FMS_LIMIT = 45 println("Number of Manifolds.jl ambiguities: $(length(fms))") if length(fms) > FMS_LIMIT for amb in fms diff --git a/test/manifolds/direct_sum_bundle.jl b/test/manifolds/direct_sum_bundle.jl new file mode 100644 index 0000000000..c37f54af68 --- /dev/null +++ b/test/manifolds/direct_sum_bundle.jl @@ -0,0 +1,119 @@ +using Test +using Manifolds +using RecursiveArrayTools + +@testset "Multitangent bundle" begin + M = Sphere(2) + m_prod_retr = Manifolds.FiberBundleProductRetraction() + m_prod_invretr = Manifolds.FiberBundleInverseProductRetraction() + m_sasaki = SasakiRetraction(5) + + @testset "Nice access to vector bundle components" begin + TB = Manifolds.MultitangentBundle{2}(M) + p = ArrayPartition( + [1.0, 0.0, 0.0], + ArrayPartition([0.0, 2.0, 4.0], [0.0, -1.0, 0.0]), + ) + @test p[TB, :point] === p.x[1] + p[TB, :point] = [0.0, 1.0, 0.0] + @test p.x[1] == [0.0, 1.0, 0.0] + @test_throws DomainError p[TB, :error] + @test_throws DomainError p[TB, :error] = [1, 2, 3] + + @test view(p, TB, :point) === p.x[1] + view(p, TB, :point) .= [2.0, 3.0, 5.0] + @test p.x[1] == [2.0, 3.0, 5.0] + @test_throws DomainError view(p, TB, :error) + end + + p = [1.0, 0.0, 0.0] + TB = Manifolds.MultitangentBundle{2}(M) + # @test sprint(show, TB) == "TangentBundle(Sphere(2, ℝ))" + @test base_manifold(TB) == M + @test manifold_dimension(TB) == 3 * manifold_dimension(M) + @test representation_size(TB) === nothing + @test default_inverse_retraction_method(TB) === m_prod_invretr + @test default_retraction_method(TB) == m_prod_retr + @test default_vector_transport_method(TB) isa + Manifolds.FiberBundleProductVectorTransport + + @testset "Type" begin + pts_tb = [ + ArrayPartition( + [1.0, 0.0, 0.0], + ArrayPartition([0.0, -1.0, -1.0], [0.0, -1.0, -1.0]), + ), + ArrayPartition( + [0.0, 1.0, 0.0], + ArrayPartition([2.0, 0.0, 1.0], [-1.0, 0.0, -2.0]), + ), + ArrayPartition( + [1.0, 0.0, 0.0], + ArrayPartition([0.0, 2.0, -1.0], [0.0, -2.0, -1.0]), + ), + ] + + for pt in pts_tb + @test bundle_projection(TB, pt) ≈ pt.x[1] + end + X12_prod = inverse_retract(TB, pts_tb[1], pts_tb[2], m_prod_invretr) + X13_prod = inverse_retract(TB, pts_tb[1], pts_tb[3], m_prod_invretr) + basis_types = + (DefaultOrthonormalBasis(), get_basis(TB, pts_tb[1], DefaultOrthonormalBasis())) + test_manifold( + TB, + pts_tb, + default_inverse_retraction_method=m_prod_invretr, + default_retraction_method=m_prod_retr, + inverse_retraction_methods=[m_prod_invretr], + retraction_methods=[m_prod_retr], + test_exp_log=false, + test_injectivity_radius=false, + test_tangent_vector_broadcasting=false, + test_default_vector_transport=true, + vector_transport_methods=[], + basis_types_vecs=basis_types, + projection_atol_multiplier=4, + test_inplace=true, + test_representation_size=false, + test_rand_point=true, + test_rand_tvector=true, + ) + + Xir = allocate(pts_tb[1]) + inverse_retract!(TB, Xir, pts_tb[1], pts_tb[2], m_prod_invretr) + @test isapprox(TB, pts_tb[1], Xir, X12_prod) + @test isapprox( + norm(TB.fiber, pts_tb[1][TB, :point], pts_tb[1][TB, :vector]), + sqrt( + inner( + TB.fiber, + pts_tb[1][TB, :point], + pts_tb[1][TB, :vector], + pts_tb[1][TB, :vector], + ), + ), + ) + @test isapprox( + distance( + TB.fiber, + pts_tb[1][TB, :point], + pts_tb[1][TB, :vector], + ArrayPartition([0.0, 2.0, 3.0], [0.0, 2.0, 2.0]), + ), + 9.273618495495704, + ) + Xir2 = allocate(pts_tb[1]) + vector_transport_to!( + TB, + Xir2, + pts_tb[1], + Xir, + pts_tb[2], + Manifolds.FiberBundleProductVectorTransport(), + ) + @test is_vector(TB, pts_tb[2], Xir2) + end + + @test base_manifold(TangentBundle(M)) == M +end diff --git a/test/runtests.jl b/test/runtests.jl index 455c6499f2..fe76ea0cf9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -178,6 +178,7 @@ include("utils.jl") include_test("manifolds/quotient_manifold.jl") include_test("manifolds/fiber.jl") include_test("manifolds/vector_bundle.jl") + include_test("manifolds/direct_sum_bundle.jl") include_test("manifolds/graph.jl") include_test("metric.jl") From b4cf7ac0f5a10c7ca97ad583e4c822453fb12ae7 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Fri, 18 Aug 2023 16:59:41 +0200 Subject: [PATCH 09/81] optionally static sizes: part 1 --- NEWS.md | 19 + Project.toml | 4 +- src/Manifolds.jl | 5 +- src/groups/general_unitary_groups.jl | 40 +- src/groups/orthogonal.jl | 12 +- src/groups/special_euclidean.jl | 78 +-- src/groups/special_orthogonal.jl | 12 +- src/groups/special_unitary.jl | 22 +- src/groups/translation_group.jl | 25 +- src/groups/unitary.jl | 36 +- src/manifolds/Euclidean.jl | 155 ++++-- src/manifolds/Flag.jl | 37 +- src/manifolds/FlagOrthogonal.jl | 22 +- src/manifolds/FlagStiefel.jl | 19 +- src/manifolds/GeneralUnitaryMatrices.jl | 335 +++++++++---- src/manifolds/Grassmann.jl | 36 +- src/manifolds/GrassmannProjector.jl | 81 ++-- src/manifolds/GrassmannStiefel.jl | 40 +- src/manifolds/Lorentz.jl | 20 +- src/manifolds/Oblique.jl | 50 +- src/manifolds/Orthogonal.jl | 13 +- src/manifolds/ProjectiveSpace.jl | 60 ++- src/manifolds/Rotations.jl | 70 +-- src/manifolds/SkewHermitian.jl | 87 ++-- src/manifolds/Sphere.jl | 91 ++-- src/manifolds/Symmetric.jl | 61 ++- src/manifolds/Unitary.jl | 77 ++- test/groups/general_unitary_groups.jl | 22 +- test/groups/special_euclidean.jl | 613 ++++++++++++------------ test/groups/translation_group.jl | 2 + test/manifolds/centered_matrices.jl | 2 +- test/manifolds/euclidean.jl | 267 ++++++----- test/manifolds/flag.jl | 2 +- test/manifolds/hyperbolic.jl | 2 +- test/manifolds/oblique.jl | 6 +- test/manifolds/projective_space.jl | 4 +- test/manifolds/rotations.jl | 16 +- test/manifolds/skewhermitian.jl | 3 +- test/manifolds/sphere.jl | 10 +- test/manifolds/symmetric.jl | 2 +- test/utils.jl | 2 +- 41 files changed, 1486 insertions(+), 974 deletions(-) create mode 100644 NEWS.md diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000000..ab21d8b049 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,19 @@ +# Changelog + +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] + +### Added + +- Vector bundles are generalized to fiber bundles. + +### 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}`. diff --git a/Project.toml b/Project.toml index 0612503698..b7a93bd4c5 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.74" +version = "0.8.75" [deps] Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" @@ -49,7 +49,7 @@ Graphs = "1.4" HybridArrays = "0.4" Kronecker = "0.4, 0.5" ManifoldDiff = "0.3.3" -ManifoldsBase = "0.14.1" +ManifoldsBase = "0.14.9" MatrixEquations = "2.2" OrdinaryDiffEq = "6.31" Plots = "1" diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 95435259bb..7bfcd87c5a 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -266,6 +266,7 @@ using ManifoldsBase: TCoTSpaceType, TFVector, TVector, + TypeParameter, ValidationManifold, ValidationMPoint, ValidationTVector, @@ -276,6 +277,7 @@ using ManifoldsBase: combine_allocation_promotion_functions, geodesic, geodesic!, + get_parameter, merge_traits, next_trait, number_system, @@ -284,7 +286,8 @@ using ManifoldsBase: shortest_geodesic, shortest_geodesic!, size_to_tuple, - trait + trait, + wrap_type_parameter using ManifoldDiff: ManifoldDiff using ManifoldDiff: default_differential_backend, diff --git a/src/groups/general_unitary_groups.jl b/src/groups/general_unitary_groups.jl index 04ec75eb41..a745f48e4d 100644 --- a/src/groups/general_unitary_groups.jl +++ b/src/groups/general_unitary_groups.jl @@ -1,13 +1,15 @@ @doc raw""" - GeneralUnitaryMultiplicationGroup{n,𝔽,M} = GroupManifold{𝔽,M,MultiplicationOperation} + GeneralUnitaryMultiplicationGroup{T,𝔽,M} = GroupManifold{𝔽,M,MultiplicationOperation} A generic type for Lie groups based on a unitary property and matrix multiplcation, see e.g. [`Orthogonal`](@ref), [`SpecialOrthogonal`](@ref), [`Unitary`](@ref), and [`SpecialUnitary`](@ref) """ -struct GeneralUnitaryMultiplicationGroup{n,𝔽,S} <: AbstractDecoratorManifold{𝔽} - manifold::GeneralUnitaryMatrices{n,𝔽,S} +struct GeneralUnitaryMultiplicationGroup{T,𝔽,S} <: AbstractDecoratorManifold{𝔽} + manifold::GeneralUnitaryMatrices{T,𝔽,S} end +get_n(M::GeneralUnitaryMultiplicationGroup) = get_n(M.manifold) + @inline function active_traits(f, ::GeneralUnitaryMultiplicationGroup, args...) if is_metric_function(f) #pass to Rotations by default - but keep Group Decorator for the retraction @@ -45,8 +47,8 @@ end decorated_manifold(G::GeneralUnitaryMultiplicationGroup) = G.manifold @doc raw""" - exp_lie(G::Orthogonal{2}, X) - exp_lie(G::SpecialOrthogonal{2}, X) + exp_lie(G::Orthogonal{TypeParameter{Tuple{2}}}, X) + exp_lie(G::SpecialOrthogonal{TypeParameter{Tuple{2}}}, X) Compute the Lie group exponential map on the [`Orthogonal`](@ref)`(2)` or [`SpecialOrthogonal`](@ref)`(2)` group. Given ``X = \begin{pmatrix} 0 & -θ \\ θ & 0 \end{pmatrix}``, the group exponential is @@ -55,11 +57,11 @@ Given ``X = \begin{pmatrix} 0 & -θ \\ θ & 0 \end{pmatrix}``, the group exponen \exp_e \colon X ↦ \begin{pmatrix} \cos θ & -\sin θ \\ \sin θ & \cos θ \end{pmatrix}. ``` """ -exp_lie(::GeneralUnitaryMultiplicationGroup{2,ℝ}, X) +exp_lie(::GeneralUnitaryMultiplicationGroup{TypeParameter{Tuple{2}},ℝ}, X) @doc raw""" - exp_lie(G::Orthogonal{4}, X) - exp_lie(G::SpecialOrthogonal{4}, X) + exp_lie(G::Orthogonal{TypeParameter{Tuple{4}}}, X) + exp_lie(G::SpecialOrthogonal{TypeParameter{Tuple{4}}}, X) Compute the group exponential map on the [`Orthogonal`](@ref)`(4)` or the [`SpecialOrthogonal`](@ref) group. The algorithm used is a more numerically stable form of those proposed in [^Gallier2002], [^Andrica2013]. @@ -75,9 +77,9 @@ The algorithm used is a more numerically stable form of those proposed in [^Gall > Balkan Journal of Geometry and Its Applications (2013), 18(2), pp. 1-2. > [pdf](https://www.emis.de/journals/BJGA/v18n2/B18-2-an.pdf). """ -exp_lie(::GeneralUnitaryMultiplicationGroup{4,ℝ}, X) +exp_lie(::GeneralUnitaryMultiplicationGroup{TypeParameter{Tuple{4}},ℝ}, X) -function exp_lie!(::GeneralUnitaryMultiplicationGroup{2,ℝ}, q, X) +function exp_lie!(::GeneralUnitaryMultiplicationGroup{TypeParameter{Tuple{2}},ℝ}, q, X) @assert size(X) == (2, 2) @inbounds θ = (X[2, 1] - X[1, 2]) / 2 sinθ, cosθ = sincos(θ) @@ -89,7 +91,7 @@ function exp_lie!(::GeneralUnitaryMultiplicationGroup{2,ℝ}, q, X) end return q end -function exp_lie!(::GeneralUnitaryMultiplicationGroup{3,ℝ}, q, X) +function exp_lie!(::GeneralUnitaryMultiplicationGroup{TypeParameter{Tuple{3}},ℝ}, q, X) θ = norm(X) / sqrt(2) if θ ≈ 0 a = 1 - θ^2 / 6 @@ -103,7 +105,7 @@ function exp_lie!(::GeneralUnitaryMultiplicationGroup{3,ℝ}, q, X) mul!(q, X, X, b, true) return q end -function exp_lie!(::GeneralUnitaryMultiplicationGroup{4,ℝ}, q, X) +function exp_lie!(::GeneralUnitaryMultiplicationGroup{TypeParameter{Tuple{4}},ℝ}, q, X) T = eltype(X) α, β = angles_4d_skew_sym_matrix(X) sinα, cosα = sincos(α) @@ -209,23 +211,23 @@ function log!( end function log_lie!( - G::GeneralUnitaryMultiplicationGroup{n,ℝ}, + G::GeneralUnitaryMultiplicationGroup{<:Any,ℝ}, X::AbstractMatrix, q::AbstractMatrix, -) where {n} +) log_safe!(X, q) return project!(G, X, Identity(G), X) end function log_lie!( - ::GeneralUnitaryMultiplicationGroup{n,ℝ}, + ::GeneralUnitaryMultiplicationGroup{<:Any,ℝ}, X, ::Identity{MultiplicationOperation}, -) where {n} +) fill!(X, 0) return X end function log_lie!( - G::GeneralUnitaryMultiplicationGroup{2,ℝ}, + G::GeneralUnitaryMultiplicationGroup{TypeParameter{Tuple{2}},ℝ}, X::AbstractMatrix, q::AbstractMatrix, ) @@ -234,7 +236,7 @@ function log_lie!( return get_vector!(G, X, Identity(G), θ, DefaultOrthogonalBasis()) end function log_lie!( - G::GeneralUnitaryMultiplicationGroup{3,ℝ}, + G::GeneralUnitaryMultiplicationGroup{TypeParameter{Tuple{3}},ℝ}, X::AbstractMatrix, q::AbstractMatrix, ) @@ -251,7 +253,7 @@ function log_lie!( return project!(G, X, e, X) end function log_lie!( - G::GeneralUnitaryMultiplicationGroup{4,ℝ}, + G::GeneralUnitaryMultiplicationGroup{TypeParameter{Tuple{4}},ℝ}, X::AbstractMatrix, q::AbstractMatrix, ) diff --git a/src/groups/orthogonal.jl b/src/groups/orthogonal.jl index ade8de09f5..4940e8a569 100644 --- a/src/groups/orthogonal.jl +++ b/src/groups/orthogonal.jl @@ -9,6 +9,14 @@ Orthogonal group $\mathrm{O}(n)$ represented by [`OrthogonalMatrices`](@ref). """ const Orthogonal{n} = GeneralUnitaryMultiplicationGroup{n,ℝ,AbsoluteDeterminantOneMatrices} -Orthogonal(n) = Orthogonal{n}(OrthogonalMatrices(n)) +function Orthogonal(n; parameter::Symbol=:field) + return GeneralUnitaryMultiplicationGroup(OrthogonalMatrices(n; parameter=parameter)) +end -show(io::IO, ::Orthogonal{n}) where {n} = print(io, "Orthogonal($(n))") +function Base.show(io::IO, ::Orthogonal{TypeParameter{Tuple{n}}}) where {n} + return print(io, "Orthogonal($(n); parameter=:type)") +end +function Base.show(io::IO, M::Orthogonal{Tuple{Int}}) + n = get_n(M) + return print(io, "Orthogonal($(n))") +end diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 4733702056..ffc2ce0f4a 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -25,19 +25,19 @@ $\mathrm{T}(n) × \mathrm{SO}(n)$. For group-specific functions, they may also b represented as affine matrices with size `(n + 1, n + 1)` (see [`affine_matrix`](@ref)), for which the group operation is [`MultiplicationOperation`](@ref). """ -const SpecialEuclidean{N} = SemidirectProductGroup{ +const SpecialEuclidean{T} = SemidirectProductGroup{ ℝ, - TranslationGroup{Tuple{N},ℝ}, - SpecialOrthogonal{N}, - RotationAction{TranslationGroup{Tuple{N},ℝ},SpecialOrthogonal{N},LeftForwardAction}, + TranslationGroup{T,ℝ}, + SpecialOrthogonal{T}, + RotationAction{TranslationGroup{T,ℝ},SpecialOrthogonal{T},LeftForwardAction}, } const SpecialEuclideanManifold{N} = - ProductManifold{ℝ,Tuple{TranslationGroup{Tuple{N},ℝ},SpecialOrthogonal{N}}} + ProductManifold{ℝ,Tuple{TranslationGroup{N,ℝ},SpecialOrthogonal{N}}} -function SpecialEuclidean(n) - Tn = TranslationGroup(n) - SOn = SpecialOrthogonal(n) +function SpecialEuclidean(n; parameter::Symbol=:type) + Tn = TranslationGroup(n; parameter=parameter) + SOn = SpecialOrthogonal(n; parameter=parameter) A = RotationAction(Tn, SOn) return SemidirectProductGroup(Tn, SOn, A) end @@ -53,6 +53,9 @@ Base.show(io::IO, ::SpecialEuclidean{n}) where {n} = print(io, "SpecialEuclidean return merge_traits(IsGroupManifold(M.op), IsExplicitDecorator()) end +get_n(::SpecialEuclidean{N}) where {N} = N +get_n(M::SpecialEuclidean{Tuple{Int}}) = manifold_dimension(M.manifold.manifolds[1]) + Base.@propagate_inbounds function Base.getindex( p::AbstractMatrix, M::Union{SpecialEuclidean,SpecialEuclideanManifold}, @@ -72,24 +75,27 @@ Base.@propagate_inbounds function Base.setindex!( end Base.@propagate_inbounds function submanifold_component( - ::Union{SpecialEuclidean{n},SpecialEuclideanManifold{n}}, + G::Union{SpecialEuclidean,SpecialEuclideanManifold}, p::AbstractMatrix, ::Val{1}, -) where {n} +) + n = get_n(G) return view(p, 1:n, n + 1) end Base.@propagate_inbounds function submanifold_component( - ::Union{SpecialEuclidean{n},SpecialEuclideanManifold{n}}, + G::Union{SpecialEuclidean,SpecialEuclideanManifold}, p::AbstractMatrix, ::Val{2}, -) where {n} +) + n = get_n(G) return view(p, 1:n, 1:n) end function submanifold_components( - G::Union{SpecialEuclidean{n},SpecialEuclideanManifold{n}}, + G::Union{SpecialEuclidean,SpecialEuclideanManifold}, p::AbstractMatrix, -) where {n} +) + n = get_n(G) @assert size(p) == (n + 1, n + 1) @inbounds t = submanifold_component(G, p, Val(1)) @inbounds R = submanifold_component(G, p, Val(2)) @@ -97,9 +103,10 @@ function submanifold_components( end Base.@propagate_inbounds function _padpoint!( - ::Union{SpecialEuclidean{n},SpecialEuclideanManifold{n}}, + G::Union{SpecialEuclidean,SpecialEuclideanManifold}, q::AbstractMatrix, -) where {n} +) + n = get_n(G) for i in 1:n q[n + 1, i] = 0 end @@ -108,9 +115,10 @@ Base.@propagate_inbounds function _padpoint!( end Base.@propagate_inbounds function _padvector!( - ::Union{SpecialEuclidean{n},SpecialEuclideanManifold{n}}, + ::Union{SpecialEuclidean,SpecialEuclideanManifold}, X::AbstractMatrix, -) where {n} +) + n = get_n(G) for i in 1:(n + 1) X[n + 1, i] = 0 end @@ -159,21 +167,26 @@ See also [`screw_matrix`](@ref) for matrix representations of the Lie algebra. > Rico Martinez, J. M., “Representations of the Euclidean group and its applications > to the kinematics of spatial chains,” PhD Thesis, University of Florida, 1988. """ -function affine_matrix(G::SpecialEuclidean{n}, p) where {n} +function affine_matrix(G::SpecialEuclidean, p) pis = submanifold_components(G, p) pmat = allocate_result(G, affine_matrix, pis...) map(copyto!, submanifold_components(G, pmat), pis) @inbounds _padpoint!(G, pmat) return pmat end -affine_matrix(::SpecialEuclidean{n}, p::AbstractMatrix) where {n} = p +affine_matrix(::SpecialEuclidean, p::AbstractMatrix) = p 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) end +function affine_matrix(::SpecialEuclidean{Tuple{Int}}, ::SpecialEuclideanIdentity) + n = get_n(G) + return Diagonal{Float64}(I, n) +end -function check_point(G::SpecialEuclideanManifold{n}, p::AbstractMatrix; kwargs...) where {n} +function check_point(G::SpecialEuclideanManifold, p::AbstractMatrix; kwargs...) + n = get_n(G) errs = DomainError[] # homogeneous if !isapprox(p[end, :], [zeros(size(p, 2) - 1)..., 1]; kwargs...) @@ -197,24 +210,27 @@ function check_point(G::SpecialEuclideanManifold{n}, p::AbstractMatrix; kwargs.. return length(errs) == 0 ? nothing : first(errs) end -function check_size(G::SpecialEuclideanManifold{n}, p::AbstractMatrix; kwargs...) where {n} +function check_size(G::SpecialEuclideanManifold, p::AbstractMatrix; kwargs...) + n = get_n(G) return check_size(Euclidean(n + 1, n + 1), p) end function check_size( - G::SpecialEuclideanManifold{n}, + G::SpecialEuclideanManifold, p::AbstractMatrix, X::AbstractMatrix; kwargs..., -) where {n} +) + n = get_n(G) return check_size(Euclidean(n + 1, n + 1), X) end function check_vector( - G::SpecialEuclideanManifold{n}, + G::SpecialEuclideanManifold, p::AbstractMatrix, X::AbstractMatrix; kwargs..., -) where {n} +) + n = get_n(G) errs = DomainError[] # homogeneous if !isapprox(X[end, :], zeros(size(X, 2)); kwargs...) @@ -255,19 +271,21 @@ a homomorphic embedding (see [`SpecialEuclideanInGeneralLinear`](@ref) for a hom See also [`affine_matrix`](@ref) for matrix representations of the Lie group. """ -function screw_matrix(G::SpecialEuclidean{n}, X) where {n} +function screw_matrix(G::SpecialEuclidean, X) Xis = submanifold_components(G, X) Xmat = allocate_result(G, screw_matrix, Xis...) map(copyto!, submanifold_components(G, Xmat), Xis) @inbounds _padvector!(G, Xmat) return Xmat end -screw_matrix(::SpecialEuclidean{n}, X::AbstractMatrix) where {n} = X +screw_matrix(::SpecialEuclidean, X::AbstractMatrix) = X -function allocate_result(::SpecialEuclidean{n}, ::typeof(affine_matrix), p...) where {n} +function allocate_result(::SpecialEuclidean, ::typeof(affine_matrix), p...) + n = get_n(G) return allocate(p[1], Size(n + 1, n + 1)) end -function allocate_result(::SpecialEuclidean{n}, ::typeof(screw_matrix), X...) where {n} +function allocate_result(::SpecialEuclidean, ::typeof(screw_matrix), X...) + n = get_n(G) return allocate(X[1], Size(n + 1, n + 1)) end diff --git a/src/groups/special_orthogonal.jl b/src/groups/special_orthogonal.jl index d49bffbde7..6f461c6352 100644 --- a/src/groups/special_orthogonal.jl +++ b/src/groups/special_orthogonal.jl @@ -8,9 +8,17 @@ Special orthogonal group ``\mathrm{SO}(n)`` represented by rotation matrices, se """ const SpecialOrthogonal{n} = GeneralUnitaryMultiplicationGroup{n,ℝ,DeterminantOneMatrices} -SpecialOrthogonal(n) = SpecialOrthogonal{n}(Rotations(n)) +function SpecialOrthogonal(n; parameter::Symbol=:field) + return GeneralUnitaryMultiplicationGroup(Rotations(n; parameter=parameter)) +end Base.inv(::SpecialOrthogonal, p) = transpose(p) Base.inv(::SpecialOrthogonal, e::Identity{MultiplicationOperation}) = e -Base.show(io::IO, ::SpecialOrthogonal{n}) where {n} = print(io, "SpecialOrthogonal($(n))") +function Base.show(io::IO, ::SpecialOrthogonal{TypeParameter{Tuple{n}}}) where {n} + return print(io, "SpecialOrthogonal($(n); parameter=:type)") +end +function Base.show(io::IO, M::SpecialOrthogonal{Type{Int}}) + n = get_n(M) + return print(io, "SpecialOrthogonal($(n))") +end diff --git a/src/groups/special_unitary.jl b/src/groups/special_unitary.jl index 7e69e22de8..c9d07ebe9d 100644 --- a/src/groups/special_unitary.jl +++ b/src/groups/special_unitary.jl @@ -18,9 +18,13 @@ or in other words we represent the tangent spaces employing the Lie algebra ``\m Generate the Lie group of ``n×n`` unitary matrices with determinant +1. """ -const SpecialUnitary{n} = GeneralUnitaryMultiplicationGroup{n,ℂ,DeterminantOneMatrices} +const SpecialUnitary{T} = GeneralUnitaryMultiplicationGroup{T,ℂ,DeterminantOneMatrices} -SpecialUnitary(n) = SpecialUnitary{n}(GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrices}()) +function SpecialUnitary(n::Int; parameter::Symbol=:field) + return GeneralUnitaryMultiplicationGroup( + GeneralUnitaryMatrices(n, ℂ, DeterminantOneMatrices; parameter=parameter), + ) +end @doc raw""" project(G::SpecialUnitary, p) @@ -50,7 +54,8 @@ function project(G::SpecialUnitary, p, X) return Y end -function project!(::SpecialUnitary{n}, q, p) where {n} +function project!(G::SpecialUnitary, q, p) + n = get_n(G) F = svd(p) detUVt = det(F.U) * det(F.Vt) if !isreal(detUVt) || real(detUVt) < 0 @@ -63,7 +68,8 @@ function project!(::SpecialUnitary{n}, q, p) where {n} end return q end -function project!(G::SpecialUnitary{n}, Y, p, X) where {n} +function project!(G::SpecialUnitary, Y, p, X) + n = get_n(G) inverse_translate_diff!(G, Y, p, p, X, LeftForwardAction()) project!(SkewHermitianMatrices(n, ℂ), Y, Y) Y[diagind(n, n)] .-= tr(Y) / n @@ -71,4 +77,10 @@ function project!(G::SpecialUnitary{n}, Y, p, X) where {n} return Y end -Base.show(io::IO, ::SpecialUnitary{n}) where {n} = print(io, "SpecialUnitary($(n))") +function Base.show(io::IO, ::SpecialUnitary{TypeParameter{Tuple{n}}}) where {n} + return print(io, "SpecialUnitary($(n); parameter=:type)") +end +function Base.show(io::IO, G::SpecialUnitary{Tuple{Int}}) + n = get_n(G) + return print(io, "SpecialUnitary($(n))") +end diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index 28397606c5..d28d3ecf55 100644 --- a/src/groups/translation_group.jl +++ b/src/groups/translation_group.jl @@ -1,19 +1,20 @@ @doc raw""" - TranslationGroup{T<:Tuple,𝔽} <: GroupManifold{Euclidean{T,𝔽},AdditionOperation} + TranslationGroup{T,𝔽} <: GroupManifold{Euclidean{T,𝔽},AdditionOperation} Translation group $\mathrm{T}(n)$ represented by translation arrays. # Constructor - TranslationGroup(n₁,...,nᵢ; field = 𝔽) + TranslationGroup(n₁,...,nᵢ; field = 𝔽, parameter::Symbol=:field) Generate the translation group on $𝔽^{n₁,…,nᵢ}$ = `Euclidean(n₁,...,nᵢ; field = 𝔽)`, which is isomorphic to the group itself. """ -const TranslationGroup{T<:Tuple,𝔽} = GroupManifold{𝔽,Euclidean{T,𝔽},AdditionOperation} +const TranslationGroup{T,𝔽} = GroupManifold{𝔽,Euclidean{T,𝔽},AdditionOperation} -function TranslationGroup(n::Int...; field::AbstractNumbers=ℝ) - return TranslationGroup{Tuple{n...},field}( - Euclidean(n...; field=field), +function TranslationGroup(n::Int...; field::AbstractNumbers=ℝ, parameter::Symbol=:type) + size = wrap_type_parameter(parameter, n) + return TranslationGroup{typeof(size),field}( + Euclidean(n...; field=field, parameter=parameter), AdditionOperation(), ) end @@ -44,6 +45,14 @@ function log!(::TranslationGroup, X, p::Identity{AdditionOperation}, q) return X end -function Base.show(io::IO, ::TranslationGroup{N,𝔽}) where {N,𝔽} - return print(io, "TranslationGroup($(join(N.parameters, ", ")); field = $(𝔽))") +function Base.show(io::IO, M::TranslationGroup{N,𝔽}) where {N<:Tuple,𝔽} + size = get_parameter(M.manifold.size) + return print( + io, + "TranslationGroup($(join(size, ", ")); field = $(𝔽), parameter = :field)", + ) +end +function Base.show(io::IO, M::TranslationGroup{N,𝔽}) where {N<:TypeParameter,𝔽} + size = get_parameter(M.manifold.size) + return print(io, "TranslationGroup($(join(size, ", ")); field = $(𝔽))") end diff --git a/src/groups/unitary.jl b/src/groups/unitary.jl index 3534e0a0b0..cfee6e4a45 100644 --- a/src/groups/unitary.jl +++ b/src/groups/unitary.jl @@ -26,7 +26,9 @@ See also [`Orthogonal(n)`](@ref) for the real-valued case. """ const Unitary{n,𝔽} = GeneralUnitaryMultiplicationGroup{n,𝔽,AbsoluteDeterminantOneMatrices} -Unitary(n, 𝔽::AbstractNumbers=ℂ) = Unitary{n,𝔽}(UnitaryMatrices(n, 𝔽)) +function Unitary(n, 𝔽::AbstractNumbers=ℂ; parameter::Symbol=:field) + return GeneralUnitaryMultiplicationGroup(UnitaryMatrices(n, 𝔽; parameter=parameter)) +end @doc raw""" exp_lie(G::Unitary{2,ℂ}, X) @@ -39,18 +41,18 @@ Compute the group exponential map on the [`Unitary(2)`](@ref) group, which is where ``θ = \frac{1}{2} \sqrt{4\det(X) - \operatorname{tr}(X)^2}``. """ -exp_lie(::Unitary{2,ℂ}, X) +exp_lie(::Unitary{TypeParameter{Tuple{2}},ℂ}, X) -function exp_lie(::Unitary{1,ℍ}, X::Number) +function exp_lie(::Unitary{TypeParameter{Tuple{1}},ℍ}, X::Number) return exp(X) end -function exp_lie!(::Unitary{1}, q, X) +function exp_lie!(::Unitary{TypeParameter{Tuple{1}}}, q, X) q[] = exp(X[]) return q end -function exp_lie!(::Unitary{2,ℂ}, q, X) +function exp_lie!(::Unitary{TypeParameter{Tuple{2}},ℂ}, q, X) size(X) === (2, 2) && size(q) === (2, 2) || throw(DomainError()) @inbounds a, d = imag(X[1, 1]), imag(X[2, 2]) @inbounds b = (X[2, 1] - X[1, 2]') / 2 @@ -75,11 +77,11 @@ function exp_lie!(G::Unitary, q, X) return q end -function log_lie!(::Unitary{1}, X, p) +function log_lie!(::Unitary{TypeParameter{Tuple{1}}}, X, p) X[] = log(p[]) return X end -function log_lie!(::Unitary{1}, X::AbstractMatrix, p::AbstractMatrix) +function log_lie!(::Unitary{TypeParameter{Tuple{1}}}, X::AbstractMatrix, p::AbstractMatrix) X[] = log(p[]) return X end @@ -89,13 +91,25 @@ function log_lie!(G::Unitary, X, p) return X end -identity_element(::Unitary{1,ℍ}) = Quaternions.quat(1.0) +identity_element(::Unitary{TypeParameter{Tuple{1}},ℍ}) = Quaternions.quat(1.0) -function log_lie(::Unitary{1}, q::Number) +function log_lie(::Unitary{TypeParameter{Tuple{1}}}, q::Number) return log(q) end Base.inv(::Unitary, p) = adjoint(p) -show(io::IO, ::Unitary{n,ℂ}) where {n} = print(io, "Unitary($(n))") -show(io::IO, ::Unitary{n,ℍ}) where {n} = print(io, "Unitary($(n), ℍ)") +function Base.show(io::IO, ::Unitary{TypeParameter{Tuple{n}},ℂ}) where {n} + return print(io, "Unitary($(n); parameter=:type)") +end +function Base.show(io::IO, M::Unitary{Tuple{Int},ℂ}) + n = get_n(M) + return print(io, "Unitary($(n))") +end +function Base.show(io::IO, ::Unitary{TypeParameter{Tuple{n}},ℍ}) where {n} + return print(io, "Unitary($(n), ℍ; parameter=:type)") +end +function Base.show(io::IO, M::Unitary{Tuple{Int},ℍ}) + n = get_n(M) + return print(io, "Unitary($(n), ℍ)") +end diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index ce2eef2afe..0c75ba672e 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -1,5 +1,5 @@ @doc raw""" - Euclidean{T<:Tuple,𝔽} <: AbstractManifold{𝔽} + Euclidean{T,𝔽} <: AbstractManifold{𝔽} Euclidean vector space. @@ -9,7 +9,7 @@ Euclidean vector space. Generate the ``n``-dimensional vector space ``ℝ^n``. - Euclidean(n₁,n₂,...,nᵢ; field=ℝ) + Euclidean(n₁,n₂,...,nᵢ; field=ℝ, parameter::Symbol = :field) 𝔽^(n₁,n₂,...,nᵢ) = Euclidean(n₁,n₂,...,nᵢ; field=𝔽) Generate the vector space of ``k = n_1 \cdot n_2 \cdot … \cdot n_i`` values, i.e. the @@ -20,15 +20,25 @@ The default `field=ℝ` can also be set to `field=ℂ`. The dimension of this space is ``k \dim_ℝ 𝔽``, where ``\dim_ℝ 𝔽`` is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of the field ``𝔽``. +`parameter`: whether a type parameter should be used to store `n`. By default size +is stored in a field. Value can either be `:field` or `:type`. + Euclidean(; field=ℝ) Generate the 1D Euclidean manifold for an `ℝ`-, `ℂ`-valued real- or complex-valued immutable values (in contrast to 1-element arrays from the constructor above). """ -struct Euclidean{N,𝔽} <: AbstractDecoratorManifold{𝔽} where {N<:Tuple} end +struct Euclidean{T,𝔽} <: AbstractDecoratorManifold{𝔽} where {T} + size::T +end -function Euclidean(n::Vararg{Int,I}; field::AbstractNumbers=ℝ) where {I} - return Euclidean{Tuple{n...},field}() +function Euclidean( + n::Vararg{Int,I}; + field::AbstractNumbers=ℝ, + parameter::Symbol=:field, +) where {I} + size = wrap_type_parameter(parameter, n) + return Euclidean{typeof(size),field}(size) end function active_traits(f, ::Euclidean, args...) @@ -41,16 +51,31 @@ end function adjoint_Jacobi_field(::Euclidean{Tuple{}}, p, q, t, X, β::Tβ) where {Tβ} return X end +function adjoint_Jacobi_field( + ::Euclidean{TypeParameter{Tuple{}}}, + p, + q, + t, + X, + β::Tβ, +) where {Tβ} + return X +end Base.:^(𝔽::AbstractNumbers, n) = Euclidean(n...; field=𝔽) Base.:^(M::Euclidean, n::Int) = ^(M, (n,)) -function Base.:^(::Euclidean{T,𝔽}, n::NTuple{N,Int}) where {T,𝔽,N} - return Euclidean{Tuple{T.parameters...,n...},𝔽}() +function Base.:^(M::Euclidean{<:Tuple,𝔽}, n::NTuple{N,Int}) where {𝔽,N} + size = get_parameter(M.size) + return Euclidean(size..., n...; field=𝔽, parameter=:field) +end +function Base.:^(M::Euclidean{<:TypeParameter,𝔽}, n::NTuple{N,Int}) where {𝔽,N} + size = get_parameter(M.size) + return Euclidean(size..., n...; field=𝔽, parameter=:type) end function allocation_promotion_function( - ::Euclidean{<:Tuple,ℂ}, + ::Euclidean{<:Any,ℂ}, ::Union{typeof(get_vector),typeof(get_coordinates)}, ::Tuple, ) @@ -106,7 +131,7 @@ end Compute the Euclidean distance between two points on the [`Euclidean`](@ref) manifold `M`, i.e. for vectors it's just the norm of the difference, for matrices -and higher order arrays, the matrix and ternsor Frobenius norm, respectively. +and higher order arrays, the matrix and tensor Frobenius norm, respectively. """ Base.@propagate_inbounds function distance(M::Euclidean, p, q) # Inspired by euclidean distance calculation in Distances.jl @@ -124,7 +149,9 @@ Base.@propagate_inbounds function distance(M::Euclidean, p, q) end return sqrt(s) end -distance(::Euclidean{Tuple{1}}, p::Number, q::Number) = abs(p - q) +distance(::Euclidean{TypeParameter{Tuple{1}}}, p::Number, q::Number) = abs(p - q) +distance(::Euclidean{TypeParameter{Tuple{}}}, p::Number, q::Number) = abs(p - q) +distance(::Euclidean{Tuple{Int}}, p::Number, q::Number) = abs(p - q) # for 1-dimensional Euclidean distance(::Euclidean{Tuple{}}, p::Number, q::Number) = abs(p - q) """ @@ -214,13 +241,7 @@ function get_coordinates_induced_basis!( return c end -function get_coordinates_orthonormal!( - M::Euclidean{<:Tuple,ℂ}, - c, - ::Any, - X, - ::ComplexNumbers, -) +function get_coordinates_orthonormal!(M::Euclidean{<:Any,ℂ}, c, ::Any, X, ::ComplexNumbers) S = representation_size(M) PS = prod(S) c .= [reshape(real.(X), PS)..., reshape(imag(X), PS)...] @@ -228,7 +249,7 @@ function get_coordinates_orthonormal!( end function get_coordinates_diagonalizing!( - M::Euclidean{<:Tuple,ℂ}, + M::Euclidean{<:Any,ℂ}, c, ::Any, X, @@ -256,15 +277,29 @@ function get_vector_orthonormal(M::Euclidean, ::Any, c, ::RealNumbers) S = representation_size(M) return reshape(c, S) end -function get_vector_orthonormal(::Euclidean{Tuple{N},ℝ}, ::Any, c, ::RealNumbers) where {N} +function get_vector_orthonormal( + ::Euclidean{TypeParameter{Tuple{N}},ℝ}, + ::Any, + c, + ::RealNumbers, +) where {N} # this method is defined just to skip a reshape return c end -function get_vector_orthonormal(::Euclidean, ::SArray{S}, c, ::RealNumbers) where {S} +function get_vector_orthonormal(::Euclidean{Tuple{Int},ℝ}, ::Any, c, ::RealNumbers) + # this method is defined just to skip a reshape + return c +end +function get_vector_orthonormal( + ::Euclidean{<:TypeParameter}, + ::SArray{S}, + c, + ::RealNumbers, +) where {S} return SArray{S}(c) end function get_vector_orthonormal( - ::Euclidean{Tuple{N},ℝ}, + ::Euclidean{TypeParameter{Tuple{N}},ℝ}, ::SArray{S}, c, ::RealNumbers, @@ -272,11 +307,16 @@ function get_vector_orthonormal( # probably doesn't need rewrapping in SArray return c end -function get_vector_orthonormal(::Euclidean, ::SizedArray{S}, c, ::RealNumbers) where {S} +function get_vector_orthonormal( + ::Euclidean{TypeParameter{Tuple{N}}}, + ::SizedArray{S}, + c, + ::RealNumbers, +) where {N,S} return SizedArray{S}(c) end function get_vector_orthonormal( - ::Euclidean{Tuple{N},ℝ}, + ::Euclidean{TypeParameter{Tuple{N}},ℝ}, ::SizedArray{S}, c, ::RealNumbers, @@ -286,7 +326,7 @@ function get_vector_orthonormal( end function get_vector_orthonormal!( - ::Euclidean{Tuple{N},ℝ}, + ::Euclidean{TypeParameter{Tuple{N}},ℝ}, Y, ::Any, c, @@ -317,14 +357,14 @@ function get_vector_induced_basis!(M::Euclidean, Y, ::Any, c, B::InducedBasis) copyto!(Y, reshape(c, S)) return Y end -function get_vector_orthonormal!(M::Euclidean{<:Tuple,ℂ}, Y, ::Any, c, ::ComplexNumbers) +function get_vector_orthonormal!(M::Euclidean{<:Any,ℂ}, Y, ::Any, c, ::ComplexNumbers) S = representation_size(M) N = div(length(c), 2) copyto!(Y, reshape(c[1:N] + im * c[(N + 1):end], S)) return Y end function get_vector_diagonalizing!( - M::Euclidean{<:Tuple,ℂ}, + M::Euclidean{<:Any,ℂ}, Y, ::Any, c, @@ -398,6 +438,9 @@ Return true. [`Euclidean`](@ref) is a flat manifold. """ is_flat(M::Euclidean) = true +function jacobi_field(::Euclidean{TypeParameter{Tuple{}}}, p, q, t, X, β::Tβ) where {Tβ} + return X +end function jacobi_field(::Euclidean{Tuple{}}, p, q, t, X, β::Tβ) where {Tβ} return X end @@ -427,7 +470,7 @@ which in this case is just ```` """ Base.log(::Euclidean, ::Any...) -Base.log(::Euclidean{Tuple{}}, p::Number, q::Number) = q - p +Base.log(::Euclidean{TypeParameter{Tuple{}}}, p::Number, q::Number) = q - p Base.log(::Euclidean, p, q) = q .- p log!(::Euclidean, X, p, q) = (X .= q .- p) @@ -440,7 +483,7 @@ function log_local_metric_density( return zero(eltype(p)) end -@generated _product_of_dimensions(::Euclidean{N}) where {N} = prod(N.parameters) +_product_of_dimensions(M::Euclidean) = prod(get_parameter(M.size)) """ manifold_dimension(M::Euclidean) @@ -449,14 +492,20 @@ Return the manifold dimension of the [`Euclidean`](@ref) `M`, i.e. the product of all array dimensions and the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of the underlying number system. """ -function manifold_dimension(M::Euclidean{N,𝔽}) where {N,𝔽} +function manifold_dimension(M::Euclidean{<:Any,𝔽}) where {𝔽} return _product_of_dimensions(M) * real_dimension(𝔽) end -manifold_dimension(::Euclidean{Tuple{},𝔽}) where {𝔽} = real_dimension(𝔽) +manifold_dimension(::Euclidean{TypeParameter{Tuple{}},𝔽}) where {𝔽} = real_dimension(𝔽) -Statistics.mean(::Euclidean{Tuple{}}, x::AbstractVector{<:Number}; kwargs...) = mean(x) function Statistics.mean( - ::Euclidean{Tuple{}}, + ::Euclidean{TypeParameter{Tuple{}}}, + x::AbstractVector{<:Number}; + kwargs..., +) + return mean(x) +end +function Statistics.mean( + ::Euclidean{TypeParameter{Tuple{}}}, x::AbstractVector{<:Number}, w::AbstractWeights; kwargs..., @@ -466,7 +515,7 @@ end Statistics.mean(::Euclidean, x::AbstractVector; kwargs...) = mean(x) function StatsBase.mean_and_var( - ::Euclidean{Tuple{}}, + ::Euclidean{TypeParameter{Tuple{}}}, x::AbstractVector{<:Number}; kwargs..., ) @@ -474,7 +523,7 @@ function StatsBase.mean_and_var( return m, sum(v) end function StatsBase.mean_and_var( - ::Euclidean{Tuple{}}, + ::Euclidean{TypeParameter{Tuple{}}}, x::AbstractVector{<:Number}, w::AbstractWeights; corrected=false, @@ -484,9 +533,15 @@ function StatsBase.mean_and_var( return m, sum(v) end -Statistics.median(::Euclidean{Tuple{}}, x::AbstractVector{<:Number}; kwargs...) = median(x) function Statistics.median( - ::Euclidean{Tuple{}}, + ::Euclidean{TypeParameter{Tuple{}}}, + x::AbstractVector{<:Number}; + kwargs..., +) + return median(x) +end +function Statistics.median( + ::Euclidean{TypeParameter{Tuple{}}}, x::AbstractVector{<:Number}, w::AbstractWeights; kwargs..., @@ -495,7 +550,7 @@ function Statistics.median( end mid_point(::Euclidean, p1, p2) = (p1 .+ p2) ./ 2 -mid_point(::Euclidean{Tuple{}}, p1::Number, p2::Number) = (p1 + p2) / 2 +mid_point(::Euclidean{TypeParameter{Tuple{}}}, p1::Number, p2::Number) = (p1 + p2) / 2 function mid_point!(::Euclidean, q, p1, p2) q .= (p1 .+ p2) ./ 2 @@ -567,6 +622,7 @@ Project an arbitrary point `p` onto the [`Euclidean`](@ref) manifold `M`, which is of course just the identity map. """ project(::Euclidean, ::Any) +project(::Euclidean{TypeParameter{Tuple{}}}, p::Number) = p project(::Euclidean{Tuple{}}, p::Number) = p project!(::Euclidean, q, p) = copyto!(q, p) @@ -579,6 +635,7 @@ Project an arbitrary vector `X` into the tangent space of a point `p` on the space of `M` can be identified with all of `M`. """ project(::Euclidean, ::Any, ::Any) +project(::Euclidean{TypeParameter{Tuple{}}}, ::Number, X::Number) = X project(::Euclidean{Tuple{}}, ::Number, X::Number) = X project!(::Euclidean, Y, p, X) = copyto!(Y, X) @@ -600,13 +657,17 @@ end Return the array dimensions required to represent an element on the [`Euclidean`](@ref) `M`, i.e. the vector of all array dimensions. """ -@generated representation_size(::Euclidean{N}) where {N} = size_to_tuple(N) -@generated representation_size(::Euclidean{Tuple{}}) = () +representation_size(M::Euclidean) = get_parameter(M.size) -function retract(M::Euclidean{Tuple{}}, p::Number, q::Number) +function retract(M::Euclidean{TypeParameter{Tuple{}}}, p::Number, q::Number) return retract(M, p, q, ExponentialRetraction()) end -function retract(M::Euclidean{Tuple{}}, p::Number, q::Number, ::ExponentialRetraction) +function retract( + M::Euclidean{TypeParameter{Tuple{}}}, + p::Number, + q::Number, + ::ExponentialRetraction, +) return exp(M, p, q) end @@ -623,15 +684,20 @@ function riemann_tensor!(::Euclidean, Xresult, p, X, Y, Z) return fill!(Xresult, 0) end -function Base.show(io::IO, ::Euclidean{N,𝔽}) where {N,𝔽} - return print(io, "Euclidean($(join(N.parameters, ", ")); field = $(𝔽))") +function Base.show(io::IO, M::Euclidean{N,𝔽}) where {N<:Tuple,𝔽} + size = get_parameter(M.size) + return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽), parameter = :field)") +end +function Base.show(io::IO, M::Euclidean{N,𝔽}) where {N<:TypeParameter,𝔽} + size = get_parameter(M.size) + return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽))") end # # Vector Transport # # The following functions are defined on layer 1 already, since # a) its independent of the transport or retraction method -# b) no amibuities occur +# b) no ambiguities occur # c) Euclidean is so basic, that these are plain defaults # function vector_transport_along( @@ -716,6 +782,7 @@ Return the zero vector in the tangent space of `x` on the [`Euclidean`](@ref) `M`, which here is just a zero filled array the same size as `x`. """ zero_vector(::Euclidean, ::Any...) +zero_vector(::Euclidean{TypeParameter{Tuple{}}}, p::Number) = zero(p) zero_vector(::Euclidean{Tuple{}}, p::Number) = zero(p) zero_vector!(::Euclidean, v, ::Any) = fill!(v, 0) diff --git a/src/manifolds/Flag.jl b/src/manifolds/Flag.jl index ab6fb95f96..cb7ddf4710 100644 --- a/src/manifolds/Flag.jl +++ b/src/manifolds/Flag.jl @@ -45,7 +45,7 @@ function Base.getindex(t::ZeroTuple, i::Int) end @doc raw""" - Flag{N,d} <: AbstractDecoratorManifold{ℝ} + Flag{T,d} <: AbstractDecoratorManifold{ℝ} Flag manifold of ``d`` subspaces of ``ℝ^N``[^YeWongLim2022]. By default the manifold uses the Stiefel coordinates representation, embedding it in the [`Stiefel`](@ref) manifold. @@ -56,7 +56,7 @@ Tangent space is represented in the block-skew-symmetric form. # Constructor - Flag(N, n1, n2, ..., nd) + Flag(N, n1, n2, ..., nd; parameter::Symbol=:field) Generate the manifold ``\operatorname{Flag}(n_1, n_2, ..., n_d; N)`` of subspaces ```math @@ -65,17 +65,20 @@ Generate the manifold ``\operatorname{Flag}(n_1, n_2, ..., n_d; N)`` of subspace where ``𝕍_i`` for ``i ∈ 1, 2, …, d`` are subspaces of ``ℝ^N`` of dimension ``\operatorname{dim} 𝕍_i = n_i``. +`parameter`: whether a type parameter should be used to store `n`. By default size +is stored in a field. Value can either be `:field` or `:type`. [^YeWongLim2022]: > K. Ye, K. S.-W. Wong, and L.-H. Lim, “Optimization on flag manifolds,” Math. Program., > vol. 194, no. 1, pp. 621–660, Jul. 2022, > doi: [10.1007/s10107-021-01640-3](https://doi.org/10.1007/s10107-021-01640-3). """ -struct Flag{N,dp1} <: AbstractDecoratorManifold{ℝ} +struct Flag{T,dp1} <: AbstractDecoratorManifold{ℝ} subspace_dimensions::ZeroTuple{NTuple{dp1,Int}} + size::T end -function Flag(N, ns::Vararg{Int,I}) where {I} +function Flag(N, ns::Vararg{Int,I}; parameter::Symbol=:field) where {I} if ns[1] <= 0 error( "First dimension in the sequence ns must be strictly positive, but is $(ns[1]).", @@ -91,7 +94,8 @@ function Flag(N, ns::Vararg{Int,I}) where {I} "Last dimension in sequence (given: $(ns[end])) must be strictly lower than N (given: $N).", ) end - return Flag{N,I + 1}(ZeroTuple(tuple(ns..., N))) + size = wrap_type_parameter(parameter, (N,)) + return Flag{typeof(size),I + 1}(ZeroTuple(tuple(ns..., N)), size) end function active_traits(f, ::Flag, args...) @@ -103,7 +107,15 @@ end Get the embedding of the [`Flag`](@ref) manifold `M`, i.e. the [`Stiefel`](@ref) manifold. """ -get_embedding(M::Flag{N,dp1}) where {N,dp1} = Stiefel(N, M.subspace_dimensions[dp1 - 1]) +function get_embedding(M::Flag{Tuple{Int},dp1}) where {dp1} + return Stiefel(M.size[1], M.subspace_dimensions[dp1 - 1]) +end +function get_embedding(M::Flag{TypeParameter{N},dp1}) where {N,dp1} + return Stiefel(N, M.subspace_dimensions[dp1 - 1]; parameter=:type) +end + +get_n(::Flag{TypeParameter{N}}) where {N} = N +get_n(M::Flag{Tuple{Int}}) = get_parameter(M.size)[1] @doc raw""" injectivity_radius(M::Flag) @@ -130,7 +142,8 @@ end Return dimension of flag manifold ``\operatorname{Flag}(n_1, n_2, ..., n_d; N)``. The formula reads ``\sum_{i=1}^d (n_i-n_{i-1})(N-n_i)``. """ -function manifold_dimension(M::Flag{N,dp1}) where {N,dp1} +function manifold_dimension(M::Flag{<:Any,dp1}) where {dp1} + N = get_n(M) dim = 0 for i in 1:(dp1 - 1) dim += @@ -140,7 +153,15 @@ function manifold_dimension(M::Flag{N,dp1}) where {N,dp1} return dim end -function Base.show(io::IO, M::Flag{N}) where {N} +function Base.show(io::IO, M::Flag{TypeParameter{N}}) where {N} + print(io, "Flag($(N)") + for d_i in M.subspace_dimensions.x[1:(end - 1)] + print(io, ", $d_i") + end + return print(io, "; parameter=:type)") +end +function Base.show(io::IO, M::Flag{Tuple{Int}}) + N = get_n(M) print(io, "Flag($(N)") for d_i in M.subspace_dimensions.x[1:(end - 1)] print(io, ", $d_i") diff --git a/src/manifolds/FlagOrthogonal.jl b/src/manifolds/FlagOrthogonal.jl index bd5c948bc5..f1de3f89b3 100644 --- a/src/manifolds/FlagOrthogonal.jl +++ b/src/manifolds/FlagOrthogonal.jl @@ -16,11 +16,11 @@ X = \begin{bmatrix} where ``B_{i,j} ∈ ℝ^{(n_i - n_{i-1}) × (n_j - n_{j-1})}``, for ``1 ≤ i < j ≤ d+1``. """ function check_vector( - M::Flag{N,dp1}, + M::Flag{<:Any,dp1}, p::OrthogonalPoint, X::OrthogonalTVector; kwargs..., -) where {N,dp1} +) where {dp1} for i in 1:dp1 for j in i:dp1 if i == j @@ -59,7 +59,10 @@ end Get embedding of [`Flag`](@ref) manifold `M`, i.e. the manifold [`OrthogonalMatrices`](@ref). """ -get_embedding(::Flag{N}, p::OrthogonalPoint) where {N} = OrthogonalMatrices(N) +function get_embedding(::Flag{TypeParameter{N}}, p::OrthogonalPoint) where {N} + return OrthogonalMatrices(N; parameter=:type) +end +get_embedding(M::Flag{Tuple{Int}}, p::OrthogonalPoint) = OrthogonalMatrices(M.size[1]) function _extract_flag(M::Flag, p::AbstractMatrix, i::Int) range = (M.subspace_dimensions[i - 1] + 1):M.subspace_dimensions[i] @@ -77,11 +80,12 @@ function inner(::Flag, p::OrthogonalPoint, X::OrthogonalTVector, Y::OrthogonalTV end function project!( - M::Flag{N,dp1}, + M::Flag{<:Any,dp1}, Y::OrthogonalTVector, ::OrthogonalPoint, X::OrthogonalTVector, -) where {N,dp1} +) where {dp1} + N = get_n(M) project!(SkewHermitianMatrices(N), Y.value, X.value) for i in 1:dp1 Bi = _extract_flag(M, Y.value, i) @@ -107,7 +111,8 @@ X = \begin{bmatrix} ```` where ``B_{i,j} ∈ ℝ^{(n_i - n_{i-1}) × (n_j - n_{j-1})}``, for ``1 ≤ i < j ≤ d+1``. """ -function project(M::Flag{N,dp1}, ::OrthogonalPoint, X::OrthogonalTVector) where {N,dp1} +function project(M::Flag{<:Any,dp1}, ::OrthogonalPoint, X::OrthogonalTVector) where {dp1} + N = get_n(M) Y = project(SkewHermitianMatrices(N), X.value) for i in 1:dp1 Bi = _extract_flag(M, Y, i) @@ -118,11 +123,12 @@ end function Random.rand!( rng::AbstractRNG, - M::Flag{N,dp1}, + M::Flag{<:Any,dp1}, pX::Union{OrthogonalPoint,OrthogonalTVector}; vector_at=nothing, -) where {N,dp1} +) where {dp1} if vector_at === nothing + N = get_n(M) RN = Rotations(N) rand!(rng, RN, pX.value) else diff --git a/src/manifolds/FlagStiefel.jl b/src/manifolds/FlagStiefel.jl index 9f0b91e0ec..511381d0ae 100644 --- a/src/manifolds/FlagStiefel.jl +++ b/src/manifolds/FlagStiefel.jl @@ -24,11 +24,11 @@ as the default vector transport method for the [`Flag`](@ref) manifold. default_vector_transport_method(::Flag) = ProjectionTransport() function inner( - M::Flag{N,dp1}, + M::Flag{<:Any,dp1}, p::AbstractMatrix, X::AbstractMatrix, Y::AbstractMatrix, -) where {N,dp1} +) where {dp1} inner_prod = zero(eltype(X)) pX = p' * X pY = p' * Y @@ -84,11 +84,11 @@ X = \begin{bmatrix} where ``B_{i,j} ∈ ℝ^{(n_i - n_{i-1}) × (n_j - n_{j-1})}``, for ``1 ≤ i < j ≤ d+1``. """ function check_vector( - M::Flag{N,dp1}, + M::Flag{<:Any,dp1}, p::AbstractMatrix, X::AbstractMatrix; atol=sqrt(eps(eltype(X))), -) where {N,dp1} +) where {dp1} for i in 1:(dp1 - 1) p_i = _extract_flag_stiefel(M, p, i) X_i = _extract_flag_stiefel(M, X, i) @@ -132,11 +132,11 @@ matrices for consecutive subspaces of the flag. project(::Flag, p, X) function project!( - M::Flag{N,dp1}, + M::Flag{<:Any,dp1}, Y::AbstractMatrix, p::AbstractMatrix, X::AbstractMatrix, -) where {N,dp1} +) where {dp1} Xc = X .- p * (p' * X) ./ 2 for i in 1:(dp1 - 1) Y_i = _extract_flag_stiefel(M, Y, i) @@ -158,12 +158,7 @@ function project!(M::Flag, q::AbstractMatrix, p::AbstractMatrix) return project!(get_embedding(M), q, p) end -function Random.rand!( - rng::AbstractRNG, - M::Flag{N,dp1}, - pX::AbstractMatrix; - vector_at=nothing, -) where {N,dp1} +function Random.rand!(rng::AbstractRNG, M::Flag, pX::AbstractMatrix; vector_at=nothing) EM = get_embedding(M) if vector_at === nothing rand!(rng, EM, pX) diff --git a/src/manifolds/GeneralUnitaryMatrices.jl b/src/manifolds/GeneralUnitaryMatrices.jl index 494f3a485a..9addd5d433 100644 --- a/src/manifolds/GeneralUnitaryMatrices.jl +++ b/src/manifolds/GeneralUnitaryMatrices.jl @@ -22,12 +22,27 @@ i.e. that the absolute value of the determinant is 1. struct AbsoluteDeterminantOneMatrices <: AbstractMatrixType end @doc raw""" - GeneralUnitaryMatrices{n,𝔽,S<:AbstractMatrixType} <: AbstractDecoratorManifold + GeneralUnitaryMatrices{T,𝔽,S<:AbstractMatrixType} <: AbstractDecoratorManifold -A common parametric type for matrices with a unitary property of size ``n×n`` over the field ``\mathbb F`` +A common parametric type for matrices with a unitary property of size ``n×n`` over the field ``𝔽`` which additionally have the `AbstractMatrixType`, e.g. are `DeterminantOneMatrices`. """ -struct GeneralUnitaryMatrices{n,𝔽,S<:AbstractMatrixType} <: AbstractDecoratorManifold{𝔽} end +struct GeneralUnitaryMatrices{T,𝔽,S<:AbstractMatrixType} <: AbstractDecoratorManifold{𝔽} + size::T +end + +function GeneralUnitaryMatrices( + n::Int, + field, + matrix_type::Type{<:AbstractMatrixType}; + parameter::Symbol=:field, +) + size = wrap_type_parameter(parameter, (n,)) + return GeneralUnitaryMatrices{typeof(size),field,matrix_type}(size) +end + +get_n(::GeneralUnitaryMatrices{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::GeneralUnitaryMatrices{Tuple{Int}}) = get_parameter(M.size)[1] function active_traits(f, ::GeneralUnitaryMatrices, args...) return merge_traits(IsEmbeddedManifold(), IsDefaultMetric(EuclideanMetric())) @@ -36,7 +51,7 @@ end @doc raw""" check_point(M::UnitaryMatrices, p; kwargs...) check_point(M::OrthogonalMatrices, p; kwargs...) - check_point(M::GeneralUnitaryMatrices{n,𝔽}, p; kwargs...) + check_point(M::GeneralUnitaryMatrices, p; kwargs...) Check whether `p` is a valid point on the [`UnitaryMatrices`](@ref) or [`OrthogonalMatrices`] `M`, i.e. that ``p`` has an determinante of absolute value one @@ -44,10 +59,10 @@ i.e. that ``p`` has an determinante of absolute value one The tolerance for the last test can be set using the `kwargs...`. """ function check_point( - M::GeneralUnitaryMatrices{n,𝔽,AbsoluteDeterminantOneMatrices}, + M::GeneralUnitaryMatrices{<:Any,𝔽,AbsoluteDeterminantOneMatrices}, p; kwargs..., -) where {n,𝔽} +) where {𝔽} if !isapprox(abs(det(p)), 1; kwargs...) return DomainError( abs(det(p)), @@ -72,10 +87,10 @@ i.e. that ``p`` has an determinante of absolute value one, i.e. that ``p^{\mathr The tolerance for the last test can be set using the `kwargs...`. """ function check_point( - M::GeneralUnitaryMatrices{n,𝔽,DeterminantOneMatrices}, + M::GeneralUnitaryMatrices{<:Any,𝔽,DeterminantOneMatrices}, p; kwargs..., -) where {n,𝔽} +) where {𝔽} if !isapprox(det(p), 1; kwargs...) return DomainError(det(p), "The determinant of $p has to be +1 but it is $(det(p))") end @@ -88,7 +103,8 @@ function check_point( return nothing end -function check_size(::GeneralUnitaryMatrices{n}, p) where {n} +function check_size(M::GeneralUnitaryMatrices, p) + n = get_n(M) m = size(p) if length(m) != 2 return DomainError( @@ -104,7 +120,8 @@ function check_size(::GeneralUnitaryMatrices{n}, p) where {n} end return nothing end -function check_size(::GeneralUnitaryMatrices{n}, p, X) where {n} +function check_size(M::GeneralUnitaryMatrices, p, X) + n = get_n(M) m = size(X) if length(size(X)) != 2 return DomainError( @@ -122,10 +139,10 @@ function check_size(::GeneralUnitaryMatrices{n}, p, X) where {n} end @doc raw""" - check_vector(M::UnitaryMatrices{n}, p, X; kwargs... ) - check_vector(M::OrthogonalMatrices{n}, p, X; kwargs... ) - check_vector(M::Rotations{n}, p, X; kwargs... ) - check_vector(M::GeneralUnitaryMatrices{n,𝔽}, p, X; kwargs... ) + check_vector(M::UnitaryMatrices, p, X; kwargs... ) + check_vector(M::OrthogonalMatrices, p, X; kwargs... ) + check_vector(M::Rotations, p, X; kwargs... ) + check_vector(M::GeneralUnitaryMatrices, p, X; kwargs... ) Check whether `X` is a tangent vector to `p` on the [`UnitaryMatrices`](@ref) space `M`, i.e. after [`check_point`](@ref)`(M,p)`, `X` has to be skew symmetric (Hermitian) @@ -133,7 +150,8 @@ and orthogonal to `p`. The tolerance for the last test can be set using the `kwargs...`. """ -function check_vector(M::GeneralUnitaryMatrices{n,𝔽}, p, X; kwargs...) where {n,𝔽} +function check_vector(M::GeneralUnitaryMatrices{<:Any,𝔽}, p, X; kwargs...) where {𝔽} + n = get_n(M) return check_point(SkewHermitianMatrices(n, 𝔽), X; kwargs...) end @@ -163,14 +181,14 @@ function cos_angles_4d_rotation_matrix(R) return ((a + b) / 4, (a - b) / 4) end -function default_estimation_method(::GeneralUnitaryMatrices{n,ℝ}, ::typeof(mean)) where {n} +function default_estimation_method(::GeneralUnitaryMatrices{<:Any,ℝ}, ::typeof(mean)) return GeodesicInterpolationWithinRadius(π / 2 / √2) end embed(::GeneralUnitaryMatrices, p) = p @doc raw""" - embed(M::GeneralUnitaryMatrices{n,𝔽}, p, X) + embed(M::GeneralUnitaryMatrices, p, X) Embed the tangent vector `X` at point `p` in `M` from its Lie algebra representation (set of skew matrices) into the @@ -198,7 +216,7 @@ Compute the exponential map, that is, since ``X`` is represented in the Lie alge exp_p(X) = p\mathrm{e}^X ``` -For different sizes, like ``n=2,3,4`` there is specialised implementations +For different sizes, like ``n=2,3,4``, there are specialized implementations. The algorithm used is a more numerically stable form of those proposed in [^Gallier2002] and [^Andrica2013]. @@ -225,28 +243,35 @@ function exp!(M::GeneralUnitaryMatrices, q, p, X, t::Number) return copyto!(M, q, p * exp(t * X)) end -function exp(M::GeneralUnitaryMatrices{2,ℝ}, p::SMatrix, X::SMatrix) +function exp(M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, p::SMatrix, X::SMatrix) θ = get_coordinates(M, p, X, DefaultOrthogonalBasis())[1] sinθ, cosθ = sincos(θ) return p * SA[cosθ -sinθ; sinθ cosθ] end -function exp(M::GeneralUnitaryMatrices{2,ℝ}, p::SMatrix, X::SMatrix, t::Real) +function exp( + M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, + p::SMatrix, + X::SMatrix, + t::Real, +) return exp(M, p, t * X) end -function exp!(M::GeneralUnitaryMatrices{2,ℝ}, q, p, X) +function exp!(M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, q, p, X) @assert size(q) == (2, 2) θ = get_coordinates(M, p, X, DefaultOrthogonalBasis())[1] sinθ, cosθ = sincos(θ) return copyto!(q, p * SA[cosθ -sinθ; sinθ cosθ]) end -function exp!(M::GeneralUnitaryMatrices{2,ℝ}, q, p, X, t::Real) +function exp!(M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, q, p, X, t::Real) @assert size(q) == (2, 2) θ = get_coordinates(M, p, X, DefaultOrthogonalBasis())[1] sinθ, cosθ = sincos(t * θ) return copyto!(q, p * SA[cosθ -sinθ; sinθ cosθ]) end -exp!(M::GeneralUnitaryMatrices{3,ℝ}, q, p, X) = exp!(M, q, p, X, one(eltype(X))) -function exp!(M::GeneralUnitaryMatrices{3,ℝ}, q, p, X, t::Real) +function exp!(M::GeneralUnitaryMatrices{TypeParameter{Tuple{3}},ℝ}, q, p, X) + return exp!(M, q, p, X, one(eltype(X))) +end +function exp!(M::GeneralUnitaryMatrices{TypeParameter{Tuple{3}},ℝ}, q, p, X, t::Real) θ = abs(t) * norm(M, p, X) / sqrt(2) if θ ≈ 0 a = 1 - θ^2 / 6 @@ -258,8 +283,10 @@ function exp!(M::GeneralUnitaryMatrices{3,ℝ}, q, p, X, t::Real) pinvq = I + a .* t .* X .+ b .* t^2 .* (X^2) return copyto!(q, p * pinvq) end -exp!(M::GeneralUnitaryMatrices{4,ℝ}, q, p, X, t::Real) = exp!(M, q, p, t * X) -function exp!(::GeneralUnitaryMatrices{4,ℝ}, q, p, X) +function exp!(M::GeneralUnitaryMatrices{TypeParameter{Tuple{4}},ℝ}, q, p, X, t::Real) + return exp!(M, q, p, t * X) +end +function exp!(::GeneralUnitaryMatrices{TypeParameter{Tuple{4}},ℝ}, q, p, X) T = eltype(X) α, β = angles_4d_skew_sym_matrix(X) sinα, cosα = sincos(α) @@ -319,9 +346,9 @@ along the axis of rotation. For $\mathrm{SO}(n)$ where $n ≥ 4$, the additional elements of $X^i$ are $X^{j (j - 3)/2 + k + 1} = X_{jk}$, for $j ∈ [4,n], k ∈ [1,j)$. """ -get_coordinates(::GeneralUnitaryMatrices{n,ℝ}, ::Any...) where {n} +get_coordinates(::GeneralUnitaryMatrices{<:Any,ℝ}, ::Any...) function get_coordinates( - ::GeneralUnitaryMatrices{2,ℝ}, + ::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, p, X, ::DefaultOrthogonalBasis{ℝ,TangentSpaceType}, @@ -329,7 +356,7 @@ function get_coordinates( return [X[2]] end function get_coordinates( - ::GeneralUnitaryMatrices{2,ℝ}, + ::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, p::SMatrix, X::SMatrix, ::DefaultOrthogonalBasis{ℝ,TangentSpaceType}, @@ -337,20 +364,32 @@ function get_coordinates( return SA[X[2]] end -function get_coordinates_orthogonal(M::GeneralUnitaryMatrices{n,ℝ}, p, X, N) where {n} +function get_coordinates_orthogonal(M::GeneralUnitaryMatrices{<:Any,ℝ}, p, X, N) Y = allocate_result(M, get_coordinates, p, X, DefaultOrthogonalBasis(N)) return get_coordinates_orthogonal!(M, Y, p, X, N) end -function get_coordinates_orthogonal!(::GeneralUnitaryMatrices{1,ℝ}, Xⁱ, p, X, ::RealNumbers) +function get_coordinates_orthogonal!( + ::GeneralUnitaryMatrices{TypeParameter{Tuple{1}},ℝ}, + Xⁱ, + p, + X, + ::RealNumbers, +) return Xⁱ end -function get_coordinates_orthogonal!(::GeneralUnitaryMatrices{2,ℝ}, Xⁱ, p, X, ::RealNumbers) +function get_coordinates_orthogonal!( + ::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, + Xⁱ, + p, + X, + ::RealNumbers, +) Xⁱ[1] = X[2] return Xⁱ end function get_coordinates_orthogonal!( - M::GeneralUnitaryMatrices{n,ℝ}, + M::GeneralUnitaryMatrices{TypeParameter{Tuple{n}},ℝ}, Xⁱ, p, X, @@ -371,13 +410,40 @@ function get_coordinates_orthogonal!( end return Xⁱ end +function get_coordinates_orthogonal!( + M::GeneralUnitaryMatrices{Tuple{Int},ℝ}, + Xⁱ, + p, + X, + ::RealNumbers, +) + n = get_n(M) + @assert length(Xⁱ) == manifold_dimension(M) + @assert size(X) == (n, n) + if n == 2 + Xⁱ[1] = X[2] + elseif n > 2 + @inbounds begin + Xⁱ[1] = X[3, 2] + Xⁱ[2] = X[1, 3] + Xⁱ[3] = X[2, 1] + + k = 4 + for i in 4:n, j in 1:(i - 1) + Xⁱ[k] = X[i, j] + k += 1 + end + end + end + return Xⁱ +end function get_coordinates_orthonormal!( - M::GeneralUnitaryMatrices{n,ℝ}, + M::GeneralUnitaryMatrices{<:Any,ℝ}, Xⁱ, p, X, num::RealNumbers, -) where {n} +) T = Base.promote_eltype(p, X) get_coordinates_orthogonal!(M, Xⁱ, p, X, num) Xⁱ .*= sqrt(T(2)) @@ -392,7 +458,13 @@ end Return the embedding, i.e. The ``\mathbb F^{n×n}``, where ``\mathbb F = \mathbb R`` for the first two and ``\mathbb F = \mathbb C`` for the unitary matrices. """ -get_embedding(::GeneralUnitaryMatrices{n,𝔽}) where {n,𝔽} = Euclidean(n, n; field=𝔽) +function get_embedding(::GeneralUnitaryMatrices{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return Euclidean(n, n; field=𝔽, parameter=:type) +end +function get_embedding(M::GeneralUnitaryMatrices{Tuple{Int},𝔽}) where {𝔽} + n = get_n(M) + return Euclidean(n, n; field=𝔽, parameter=:field) +end @doc raw""" get_vector(M::OrthogonalMatrices, p, Xⁱ, B::DefaultOrthogonalBasis) @@ -401,32 +473,44 @@ get_embedding(::GeneralUnitaryMatrices{n,𝔽}) where {n,𝔽} = Euclidean(n, n; Convert the unique tangent vector components `Xⁱ` at point `p` on [`Rotations`](@ref) or [`OrthogonalMatrices`](@ref) to the matrix representation $X$ of the tangent vector. See -[`get_coordinates`](@ref get_coordinates(::GeneralUnitaryMatrices{n,ℝ} where {n}, ::Any...)) for the conventions used. +[`get_coordinates`](@ref get_coordinates(::GeneralUnitaryMatrices, ::Any...)) for the conventions used. """ -get_vector(::GeneralUnitaryMatrices{n,ℝ}, ::Any...) where {n} +get_vector(::GeneralUnitaryMatrices{<:Any,ℝ}, ::Any...) -function get_vector_orthogonal( - M::GeneralUnitaryMatrices{n,ℝ}, - p, - c, - N::RealNumbers, -) where {n} +function get_vector_orthogonal(M::GeneralUnitaryMatrices{<:Any,ℝ}, p, c, N::RealNumbers) Y = allocate_result(M, get_vector, p, c) return get_vector_orthogonal!(M, Y, p, c, N) end -function get_vector_orthogonal(::GeneralUnitaryMatrices{2,ℝ}, p::SMatrix, Xⁱ, ::RealNumbers) +function get_vector_orthogonal( + ::GeneralUnitaryMatrices{TypeParameter{2},ℝ}, + p::SMatrix, + Xⁱ, + ::RealNumbers, +) return @SMatrix [0 -Xⁱ[]; Xⁱ[] 0] end -function get_vector_orthogonal!(::GeneralUnitaryMatrices{1,ℝ}, X, p, Xⁱ, N::RealNumbers) +function get_vector_orthogonal!( + ::GeneralUnitaryMatrices{TypeParameter{1},ℝ}, + X, + p, + Xⁱ, + N::RealNumbers, +) return X .= 0 end -function get_vector_orthogonal!(M::GeneralUnitaryMatrices{2,ℝ}, X, p, Xⁱ, N::RealNumbers) +function get_vector_orthogonal!( + M::GeneralUnitaryMatrices{TypeParameter{2},ℝ}, + X, + p, + Xⁱ, + N::RealNumbers, +) return get_vector_orthogonal!(M, X, p, Xⁱ[1], N) end function get_vector_orthogonal!( - ::GeneralUnitaryMatrices{2,ℝ}, + ::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, X, p, Xⁱ::Real, @@ -442,7 +526,7 @@ function get_vector_orthogonal!( return X end function get_vector_orthogonal!( - M::GeneralUnitaryMatrices{n,ℝ}, + M::GeneralUnitaryMatrices{TypeParameter{Tuple{n}},ℝ}, X, p, Xⁱ, @@ -450,6 +534,7 @@ function get_vector_orthogonal!( ) where {n} @assert size(X) == (n, n) @assert length(Xⁱ) == manifold_dimension(M) + @assert n > 2 @inbounds begin X[1, 1] = 0 X[1, 2] = -Xⁱ[3] @@ -472,22 +557,60 @@ function get_vector_orthogonal!( end return X end -function get_vector_orthonormal( - M::GeneralUnitaryMatrices{n,ℝ}, +function get_vector_orthogonal!( + M::GeneralUnitaryMatrices{Tuple{Int},ℝ}, + X, p, Xⁱ, - N::RealNumbers, -) where {n} + ::RealNumbers, +) + n = get_n(M) + @assert size(X) == (n, n) + @assert length(Xⁱ) == manifold_dimension(M) + if n == 1 + X .= 0 + elseif n == 2 + @inbounds begin + X[1] = 0 + X[2] = Xⁱ[1] + X[3] = -Xⁱ[1] + X[4] = 0 + end + else + @inbounds begin + X[1, 1] = 0 + X[1, 2] = -Xⁱ[3] + X[1, 3] = Xⁱ[2] + X[2, 1] = Xⁱ[3] + X[2, 2] = 0 + X[2, 3] = -Xⁱ[1] + X[3, 1] = -Xⁱ[2] + X[3, 2] = Xⁱ[1] + X[3, 3] = 0 + k = 4 + for i in 4:n + for j in 1:(i - 1) + X[i, j] = Xⁱ[k] + X[j, i] = -Xⁱ[k] + k += 1 + end + X[i, i] = 0 + end + end + end + return X +end +function get_vector_orthonormal(M::GeneralUnitaryMatrices{<:Any,ℝ}, p, Xⁱ, N::RealNumbers) return get_vector_orthogonal(M, p, Xⁱ, N) ./ sqrt(eltype(Xⁱ)(2)) end function get_vector_orthonormal!( - M::GeneralUnitaryMatrices{n,ℝ}, + M::GeneralUnitaryMatrices{<:Any,ℝ}, X, p, Xⁱ, N::RealNumbers, -) where {n} +) T = Base.promote_eltype(p, X) get_vector_orthogonal!(M, X, p, Xⁱ, N) X ./= sqrt(T(2)) @@ -506,7 +629,7 @@ Return the injectivity radius for general unitary matrix manifolds, which is[^1] injectivity_radius(::GeneralUnitaryMatrices) = π @doc raw""" - injectivity_radius(G::GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrices}) + injectivity_radius(G::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices}) Return the injectivity radius for general complex unitary matrix manifolds, where the determinant is $+1$, which is[^1] @@ -515,9 +638,7 @@ which is[^1] \operatorname{inj}_{\mathrm{SU}(n)} = π \sqrt{2}. ``` """ -function injectivity_radius( - ::GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrices}, -) where {n,ℂ} +function injectivity_radius(::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices}) return π * sqrt(2.0) end @@ -533,24 +654,23 @@ Return the radius of injectivity on the [`Rotations`](@ref) manifold `M`, which [^1]: > For a derivation of the injectivity radius, see [sethaxen.com/blog/2023/02/the-injectivity-radii-of-the-unitary-groups/](https://sethaxen.com/blog/2023/02/the-injectivity-radii-of-the-unitary-groups/). """ -injectivity_radius(::GeneralUnitaryMatrices{n,ℝ}) where {n} = π * sqrt(2.0) -injectivity_radius(::GeneralUnitaryMatrices{1,ℝ}) = 0.0 - -# Resolve ambiguity on Rotations and Orthogonal -function _injectivity_radius( - ::GeneralUnitaryMatrices{n,ℝ}, - ::ExponentialRetraction, -) where {n} +function injectivity_radius(::GeneralUnitaryMatrices{TypeParameter{Tuple{n}},ℝ}) where {n} return π * sqrt(2.0) end -function _injectivity_radius(::GeneralUnitaryMatrices{n,ℝ}, ::PolarRetraction) where {n} - return π / sqrt(2.0) +function injectivity_radius(M::GeneralUnitaryMatrices{Tuple{Int},ℝ}) + n = get_n(M) + return n == 1 ? 0.0 : π * sqrt(2.0) end -function _injectivity_radius(::GeneralUnitaryMatrices{1,ℝ}, ::ExponentialRetraction) - return 0.0 +injectivity_radius(::GeneralUnitaryMatrices{TypeParameter{Tuple{1}},ℝ}) = 0.0 + +# Resolve ambiguity on Rotations and Orthogonal +function _injectivity_radius(M::GeneralUnitaryMatrices{<:Any,ℝ}, ::ExponentialRetraction) + n = get_n(M) + return n == 1 ? 0.0 : π * sqrt(2.0) end -function _injectivity_radius(::GeneralUnitaryMatrices{1,ℝ}, ::PolarRetraction) - return 0.0 +function _injectivity_radius(::GeneralUnitaryMatrices{<:Any,ℝ}, ::PolarRetraction) + n = get_n(M) + return n == 1 ? 0.0 : π / sqrt(2.0) end inner(::GeneralUnitaryMatrices, p, X, Y) = dot(X, Y) @@ -561,8 +681,14 @@ inner(::GeneralUnitaryMatrices, p, X, Y) = dot(X, Y) Return true if [`GeneralUnitaryMatrices`](@ref) `M` is SO(2) or U(1) and false otherwise. """ is_flat(M::GeneralUnitaryMatrices) = false -is_flat(M::GeneralUnitaryMatrices{2,ℝ}) = true -is_flat(M::GeneralUnitaryMatrices{1,ℂ}) = true +is_flat(M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}) = true +is_flat(M::GeneralUnitaryMatrices{TypeParameter{Tuple{1}},ℂ}) = true +function is_flat(M::GeneralUnitaryMatrices{Tuple{Int64},ℝ}) + return M.size[1] == 2 +end +function is_flat(M::GeneralUnitaryMatrices{Tuple{Int64},ℂ}) + return M.size[1] == 1 +end @doc raw""" log(M::Rotations, p, X) @@ -594,25 +720,26 @@ the result is projected onto the set of skew symmetric matrices. For antipodal rotations the function returns deterministically one of the tangent vectors that point at `q`. """ -log(::GeneralUnitaryMatrices{n,ℝ}, ::Any...) where {n} -function ManifoldsBase.log(M::GeneralUnitaryMatrices{2,ℝ}, p, q) +log(::GeneralUnitaryMatrices{<:Any,ℝ}, ::Any...) +function ManifoldsBase.log(M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, p, q) U = transpose(p) * q @assert size(U) == (2, 2) @inbounds θ = atan(U[2], U[1]) return get_vector(M, p, θ, DefaultOrthogonalBasis()) end -function log!(::GeneralUnitaryMatrices{n,ℝ}, X, p, q) where {n} +function log!(M::GeneralUnitaryMatrices{<:Any,ℝ}, X, p, q) U = transpose(p) * q X .= real(log_safe(U)) + n = get_n(M) return project!(SkewSymmetricMatrices(n), X, p, X) end -function log!(M::GeneralUnitaryMatrices{2,ℝ}, X, p, q) +function log!(M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, X, p, q) U = transpose(p) * q @assert size(U) == (2, 2) @inbounds θ = atan(U[2], U[1]) return get_vector!(M, X, p, θ, DefaultOrthogonalBasis()) end -function log!(M::GeneralUnitaryMatrices{3,ℝ}, X, p, q) +function log!(M::GeneralUnitaryMatrices{TypeParameter{Tuple{3}},ℝ}, X, p, q) U = transpose(p) * q cosθ = (tr(U) - 1) / 2 if cosθ ≈ -1 @@ -625,7 +752,7 @@ function log!(M::GeneralUnitaryMatrices{3,ℝ}, X, p, q) X .= U ./ usinc_from_cos(cosθ) return project!(SkewSymmetricMatrices(3), X, p, X) end -function log!(::GeneralUnitaryMatrices{4,ℝ}, X, p, q) +function log!(::GeneralUnitaryMatrices{TypeParameter{Tuple{4}},ℝ}, X, p, q) U = transpose(p) * q cosα, cosβ = Manifolds.cos_angles_4d_rotation_matrix(U) α = acos(clamp(cosα, -1, 1)) @@ -651,8 +778,9 @@ function log!(::GeneralUnitaryMatrices{4,ℝ}, X, p, q) return project!(SkewSymmetricMatrices(4), X, p, X) end -function log!(::GeneralUnitaryMatrices{n,𝔽}, X, p, q) where {n,𝔽} +function log!(M::GeneralUnitaryMatrices{<:Any,𝔽}, X, p, q) where {𝔽} log_safe!(X, adjoint(p) * q) + n = get_n(M) project!(SkewHermitianMatrices(n, 𝔽), X, X) return X end @@ -668,16 +796,22 @@ Return the dimension of the manifold orthogonal matrices and of the manifold of \dim_{\mathrm{O}(n)} = \dim_{\mathrm{SO}(n)} = \frac{n(n-1)}{2}. ``` """ -manifold_dimension(::GeneralUnitaryMatrices{n,ℝ}) where {n} = div(n * (n - 1), 2) +function manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℝ}) + n = get_n(M) + return div(n * (n - 1), 2) +end @doc raw""" - manifold_dimension(M::GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrices}) + manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices}) Return the dimension of the manifold of special unitary matrices. ```math \dim_{\mathrm{SU}(n)} = n^2-1. ``` """ -manifold_dimension(::GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrices}) where {n} = n^2 - 1 +function manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices}) + n = get_n(M) + return n^2 - 1 +end """ mean( @@ -691,11 +825,11 @@ manifold_dimension(::GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrices}) where Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` using [`GeodesicInterpolationWithinRadius`](@ref). """ -mean(::GeneralUnitaryMatrices{n,ℝ}, ::Any) where {n} +mean(::GeneralUnitaryMatrices{<:Any,ℝ}, ::Any) @doc raw""" - project(G::UnitaryMatrices{n}, p) - project(G::OrthogonalMatrices{n}, p) + project(G::UnitaryMatrices, p) + project(G::OrthogonalMatrices, p) Project the point ``p ∈ 𝔽^{n × n}`` to the nearest point in ``\mathrm{U}(n,𝔽)=``[`Unitary(n,𝔽)`](@ref) under the Frobenius norm. @@ -706,22 +840,22 @@ is \operatorname{proj}_{\mathrm{U}(n,𝔽)} \colon p ↦ U V^\mathrm{H}. ```` """ -project(::GeneralUnitaryMatrices{n,𝔽,AbsoluteDeterminantOneMatrices}, p) where {n,𝔽} +project(::GeneralUnitaryMatrices{<:Any,𝔽,AbsoluteDeterminantOneMatrices}, p) where {𝔽} function project!( - ::GeneralUnitaryMatrices{n,𝔽,AbsoluteDeterminantOneMatrices}, + ::GeneralUnitaryMatrices{<:Any,𝔽,AbsoluteDeterminantOneMatrices}, q, p, -) where {n,𝔽} +) where {𝔽} F = svd(p) mul!(q, F.U, F.Vt) return q end @doc raw""" - project(M::OrthogonalMatrices{n}, p, X) - project(M::Rotations{n}, p, X) - project(M::UnitaryMatrices{n}, p, X) + project(M::OrthogonalMatrices, p, X) + project(M::Rotations, p, X) + project(M::UnitaryMatrices, p, X) Orthogonally project the tangent vector ``X ∈ 𝔽^{n × n}``, ``\mathbb F ∈ \{\mathbb R, \mathbb C\}`` to the tangent space of `M` at `p`, @@ -733,7 +867,8 @@ and change the representer to use the corresponding Lie algebra, i.e. we compute """ project(::GeneralUnitaryMatrices, p, X) -function project!(::GeneralUnitaryMatrices{n,𝔽}, Y, p, X) where {n,𝔽} +function project!(M::GeneralUnitaryMatrices{<:Any,𝔽}, Y, p, X) where {𝔽} + n = get_n(M) project!(SkewHermitianMatrices(n, 𝔽), Y, p \ X) return Y end @@ -756,7 +891,7 @@ be the singular value decomposition, then the formula reads \operatorname{retr}_p X = UV^\mathrm{T}. ```` """ -retract(::GeneralUnitaryMatrices{n,𝔽}, ::Any, ::Any, ::PolarRetraction) where {n,𝔽} +retract(::GeneralUnitaryMatrices, ::Any, ::Any, ::PolarRetraction) @doc raw""" retract(M::Rotations, p, X, ::QRRetraction) @@ -767,22 +902,22 @@ Compute the QR-based retraction on the [`Rotations`](@ref) and [`OrthogonalMatri This is also the default retraction on these manifolds. """ -retract(::GeneralUnitaryMatrices{n,𝔽}, ::Any, ::Any, ::QRRetraction) where {n,𝔽} +retract(::GeneralUnitaryMatrices, ::Any, ::Any, ::QRRetraction) function retract_qr!( - ::GeneralUnitaryMatrices{n,𝔽}, + ::GeneralUnitaryMatrices, q::AbstractArray{T}, p, X, t::Number, -) where {n,𝔽,T} +) where {T} A = p + p * (t * X) qr_decomp = qr(A) d = diag(qr_decomp.R) D = Diagonal(sign.(d .+ convert(T, 0.5))) return copyto!(q, qr_decomp.Q * D) end -function retract_polar!(M::GeneralUnitaryMatrices{n,𝔽}, q, p, X, t::Number) where {n,𝔽} +function retract_polar!(M::GeneralUnitaryMatrices, q, p, X, t::Number) A = p + p * (t * X) return project!(M, q, A; check_det=false) end diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 0227ca1f30..b413a0a29e 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -1,5 +1,5 @@ @doc raw""" - Grassmann{n,k,𝔽} <: AbstractDecoratorManifold{𝔽} + Grassmann{T,𝔽} <: AbstractDecoratorManifold{𝔽} The Grassmann manifold $\operatorname{Gr}(n,k)$ consists of all subspaces spanned by $k$ linear independent vectors $𝔽^n$, where $𝔽 ∈ \{ℝ, ℂ\}$ is either the real- (or complex-) valued vectors. @@ -75,18 +75,23 @@ case `field = ℝ` is the default. > _A Grassmann Manifold Handbook: Basic Geometry and Computational Aspects_, > arXiv preprint [2011.13699](https://arxiv.org/abs/2011.13699), 2020. """ -struct Grassmann{n,k,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct Grassmann{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end # # Generic functions independent of the representation of points # -Grassmann(n::Int, k::Int, field::AbstractNumbers=ℝ) = Grassmann{n,k,field}() +function Grassmann(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n, k)) + return Grassmann{typeof(size),field}(size) +end function active_traits(f, ::Grassmann, args...) return merge_traits(IsIsometricEmbeddedManifold(), IsQuotientManifold()) end -function allocation_promotion_function(::Grassmann{n,k,ℂ}, f, args::Tuple) where {n,k} +function allocation_promotion_function(::Grassmann{<:Any,ℂ}, f, args::Tuple) return complex end @@ -116,6 +121,9 @@ function change_metric!(::Grassmann, Y, ::EuclideanMetric, p, X) return Y end +get_nk(::Grassmann{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) +get_nk(M::Grassmann{Tuple{Int,Int}}) = get_parameter(M.size) + @doc raw""" injectivity_radius(M::Grassmann) injectivity_radius(M::Grassmann, p) @@ -152,7 +160,10 @@ Return the dimension of the [`Grassmann(n,k,𝔽)`](@ref) manifold `M`, i.e. where $\dim_ℝ 𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ -manifold_dimension(::Grassmann{n,k,𝔽}) where {n,k,𝔽} = k * (n - k) * real_dimension(𝔽) +function manifold_dimension(M::Grassmann{<:Any,𝔽}) where {𝔽} + n, k = get_nk(M) + return k * (n - k) * real_dimension(𝔽) +end """ mean( @@ -166,23 +177,30 @@ manifold_dimension(::Grassmann{n,k,𝔽}) where {n,k,𝔽} = k * (n - k) * real_ Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` using [`GeodesicInterpolationWithinRadius`](@ref). """ -mean(::Grassmann{n,k} where {n,k}, ::Any...) +mean(::Grassmann, ::Any...) function default_estimation_method(::Grassmann, ::typeof(mean)) return GeodesicInterpolationWithinRadius(π / 4) end -function get_orbit_action(M::Grassmann{n,k,ℝ}) where {n,k} +function get_orbit_action(M::Grassmann{<:Any,ℝ}) + n, k = get_nk(M) return RowwiseMultiplicationAction(M, Orthogonal(k)) end @doc raw""" - get_total_space(::Grassmann{n,k}) + get_total_space(::Grassmann) Return the total space of the [`Grassmann`](@ref) manifold, which is the corresponding Stiefel manifold, independent of whether the points are represented already in the total space or as [`ProjectorPoint`](@ref)s. """ -get_total_space(::Grassmann{n,k,𝔽}) where {n,k,𝔽} = Stiefel(n, k, 𝔽) +function get_total_space(::Grassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return Stiefel(n, k, 𝔽; parameter=:type) +end +function get_total_space(M::Grassmann{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_nk(M) + return Stiefel(n, k, 𝔽) +end # # Reprenter specific implementations in their corresponding subfiles diff --git a/src/manifolds/GrassmannProjector.jl b/src/manifolds/GrassmannProjector.jl index ea7571a11c..f0596a9227 100644 --- a/src/manifolds/GrassmannProjector.jl +++ b/src/manifolds/GrassmannProjector.jl @@ -21,13 +21,14 @@ ManifoldsBase.@manifold_vector_forwards ProjectorTVector value ManifoldsBase.@manifold_element_forwards ProjectorPoint value @doc raw""" - check_point(::Grassmann{n,k}, p::ProjectorPoint; kwargs...) + check_point(::Grassmann, p::ProjectorPoint; kwargs...) Check whether an orthogonal projector is a point from the [`Grassmann`](@ref)`(n,k)` manifold, i.e. the [`ProjectorPoint`](@ref) ``p ∈ \mathbb F^{n×n}``, ``\mathbb F ∈ \{\mathbb R, \mathbb C\}`` has to fulfill ``p^{\mathrm{T}} = p``, ``p^2=p``, and ``\operatorname{rank} p = k`. """ -function check_point(M::Grassmann{n,k,𝔽}, p::ProjectorPoint; kwargs...) where {n,k,𝔽} +function check_point(M::Grassmann, p::ProjectorPoint; kwargs...) + n, k = get_nk(M) c = p.value * p.value if !isapprox(c, p.value; kwargs...) return DomainError( @@ -52,16 +53,16 @@ function check_point(M::Grassmann{n,k,𝔽}, p::ProjectorPoint; kwargs...) where end @doc raw""" - check_size(M::Grassmann{n,k,𝔽}, p::ProjectorPoint; kwargs...) where {n,k} + check_size(M::Grassmann, p::ProjectorPoint; kwargs...) Check that the [`ProjectorPoint`](@ref) is of correct size, i.e. from ``\mathbb F^{n×n}`` """ -function check_size(M::Grassmann{n,k,𝔽}, p::ProjectorPoint; kwargs...) where {n,k,𝔽} +function check_size(M::Grassmann, p::ProjectorPoint; kwargs...) return check_size(get_embedding(M, p), p.value; kwargs...) end @doc raw""" - check_vector(::Grassmann{n,k,𝔽}, p::ProjectorPoint, X::ProjectorTVector; kwargs...) where {n,k,𝔽} + check_vector(::Grassmann, p::ProjectorPoint, X::ProjectorTVector; kwargs...) Check whether the [`ProjectorTVector`](@ref) `X` is from the tangent space ``T_p\operatorname{Gr}(n,k) `` at the [`ProjectorPoint`](@ref) `p` on the [`Grassmann`](@ref) manifold ``\operatorname{Gr}(n,k)``. @@ -74,12 +75,7 @@ Xp + pX = X must hold, where the `kwargs` can be used to check both for symmetrix of ``X``` and this equality up to a certain tolerance. """ -function check_vector( - M::Grassmann{n,k,𝔽}, - p::ProjectorPoint, - X::ProjectorTVector; - kwargs..., -) where {n,k,𝔽} +function check_vector(M::Grassmann, p::ProjectorPoint, X::ProjectorTVector; kwargs...) if !isapprox(X.value, X.value'; kwargs...) return DomainError( norm(X.value - X.value'), @@ -101,23 +97,35 @@ embed(::Grassmann, p::ProjectorPoint) = p.value embed(::Grassmann, p, X::ProjectorTVector) = X.value @doc raw""" - get_embedding(M::Grassmann{n,k,𝔽}, p::ProjectorPoint) where {n,k,𝔽} + get_embedding(M::Grassmann, p::ProjectorPoint) Return the embedding of the [`ProjectorPoint`](@ref) representation of the [`Grassmann`](@ref) manifold, i.e. the Euclidean space ``\mathbb F^{n×n}``. """ -get_embedding(::Grassmann{n,k,𝔽}, ::ProjectorPoint) where {n,k,𝔽} = Euclidean(n, n; field=𝔽) +function get_embedding( + ::Grassmann{TypeParameter{Tuple{n,k}},𝔽}, + ::ProjectorPoint, +) where {n,k,𝔽} + return Euclidean(n, n; field=𝔽, parameter=:type) +end +function get_embedding(M::Grassmann{Tuple{Int,Int},𝔽}, ::ProjectorPoint) where {𝔽} + n, k = get_nk(M) + return Euclidean(n, n; field=𝔽, parameter=:type) +end @doc raw""" - representation_size(M::Grassmann{n,k}, p::ProjectorPoint) + representation_size(M::Grassmann, p::ProjectorPoint) Return the represenation size or matrix dimension of a point on the [`Grassmann`](@ref) `M` when using [`ProjectorPoint`](@ref)s, i.e. ``(n,n)``. """ -@generated representation_size(::Grassmann{n,k}, p::ProjectorPoint) where {n,k} = (n, n) +function representation_size(M::Grassmann, p::ProjectorPoint) + n, k = get_nk(M) + return (n, n) +end @doc raw""" - canonical_project!(M::Grassmann{n,k}, q::ProjectorPoint, p) + canonical_project!(M::Grassmann, q::ProjectorPoint, p) Compute the canonical projection ``π(p)`` from the [`Stiefel`](@ref) manifold onto the [`Grassmann`](@ref) manifold when represented as [`ProjectorPoint`](@ref), i.e. @@ -126,27 +134,20 @@ manifold when represented as [`ProjectorPoint`](@ref), i.e. π^{\mathrm{SG}}(p) = pp^{\mathrm{T}} ``` """ -function canonical_project!(::Grassmann{n,k}, q::ProjectorPoint, p) where {n,k} +function canonical_project!(::Grassmann, q::ProjectorPoint, p) q.value .= p * p' return q end -function canonical_project!( - M::Grassmann{n,k}, - q::ProjectorPoint, - p::StiefelPoint, -) where {n,k} +function canonical_project!(M::Grassmann, q::ProjectorPoint, p::StiefelPoint) return canonical_project!(M, q, p.value) end -function allocate_result( - ::Grassmann{n,k}, - ::typeof(canonical_project), - p::StiefelPoint, -) where {n,k} +function allocate_result(M::Grassmann, ::typeof(canonical_project), p::StiefelPoint) + n, k = get_nk(M) return ProjectorPoint(allocate(p.value, (n, n))) end @doc raw""" - canonical_project!(M::Grassmann{n,k}, q::ProjectorPoint, p) + canonical_project!(M::Grassmann, q::ProjectorPoint, p) Compute the canonical projection ``π(p)`` from the [`Stiefel`](@ref) manifold onto the [`Grassmann`](@ref) manifold when represented as [`ProjectorPoint`](@ref), i.e. @@ -155,39 +156,31 @@ manifold when represented as [`ProjectorPoint`](@ref), i.e. Dπ^{\mathrm{SG}}(p)[X] = Xp^{\mathrm{T}} + pX^{\mathrm{T}} ``` """ -function differential_canonical_project!( - ::Grassmann{n,k}, - Y::ProjectorTVector, - p, - X, -) where {n,k} +function differential_canonical_project!(::Grassmann, Y::ProjectorTVector, p, X) Xpt = X * p' Y.value .= Xpt .+ Xpt' return Y end function differential_canonical_project!( - M::Grassmann{n,k}, + M::Grassmann, Y::ProjectorTVector, p::StiefelPoint, X::StiefelTVector, -) where {n,k} +) differential_canonical_project!(M, Y, p.value, X.value) return Y end function allocate_result( - ::Grassmann{n,k}, + M::Grassmann, ::typeof(differential_canonical_project), p::StiefelPoint, X::StiefelTVector, -) where {n,k} +) + n, k = get_nk(M) return ProjectorTVector(allocate(p.value, (n, n))) end -function allocate_result( - ::Grassmann{n,k}, - ::typeof(differential_canonical_project), - p, - X, -) where {n,k} +function allocate_result(M::Grassmann, ::typeof(differential_canonical_project), p, X) + n, k = get_nk(M) return ProjectorTVector(allocate(p, (n, n))) end diff --git a/src/manifolds/GrassmannStiefel.jl b/src/manifolds/GrassmannStiefel.jl index 5019a602f8..c8b17b0bad 100644 --- a/src/manifolds/GrassmannStiefel.jl +++ b/src/manifolds/GrassmannStiefel.jl @@ -93,8 +93,12 @@ function exp!(M::Grassmann, q, p, X) return copyto!(q, Array(qr(z).Q)) end -function get_embedding(::Grassmann{N,K,𝔽}) where {N,K,𝔽} - return Stiefel(N, K, 𝔽) +function get_embedding(::Grassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return Stiefel(n, k, 𝔽; parameter=:type) +end +function get_embedding(M::Grassmann{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_nk(M) + return Stiefel(n, k, 𝔽) end @doc raw""" @@ -228,12 +232,13 @@ rand(M::Grassmann; σ::Real=1.0) function Random.rand!( rng::AbstractRNG, - M::Grassmann{n,k,𝔽}, + M::Grassmann{<:Any,𝔽}, pX; σ::Real=one(real(eltype(pX))), vector_at=nothing, -) where {n,k,𝔽} +) where {𝔽} if vector_at === nothing + n, k = get_nk(M) V = σ * randn(rng, 𝔽 === ℝ ? Float64 : ComplexF64, (n, k)) pX .= qr(V).Q[:, 1:k] else @@ -245,12 +250,12 @@ function Random.rand!( end @doc raw""" - representation_size(M::Grassmann{n,k}) + representation_size(M::Grassmann) -Return the represenation size or matrix dimension of a point on the [`Grassmann`](@ref) +Return the representation size or matrix dimension of a point on the [`Grassmann`](@ref) `M`, i.e. $(n,k)$ for both the real-valued and the complex value case. """ -@generated representation_size(::Grassmann{n,k}) where {n,k} = (n, k) +representation_size(M::Grassmann) = get_nk(M) @doc raw""" retract(M::Grassmann, p, X, ::PolarRetraction) @@ -286,7 +291,7 @@ D = \operatorname{diag}\left( \operatorname{sgn}\left(R_{ii}+\frac{1}{2}\right)_ """ retract(::Grassmann, ::Any, ::Any, ::QRRetraction) -function retract_qr!(::Grassmann{N,K}, q, p, X, t::Number) where {N,K} +function retract_qr!(::Grassmann, q, p, X, t::Number) q .= p .+ t .* X qrfac = qr(q) d = diag(qrfac.R) @@ -295,7 +300,7 @@ function retract_qr!(::Grassmann{N,K}, q, p, X, t::Number) where {N,K} end @doc raw""" - riemann_tensor(::Grassmann{n,k,ℝ}, p, X, Y, Z) where {n,k} + riemann_tensor(::Grassmann{<:Any,ℝ}, p, X, Y, Z) Compute the value of Riemann tensor on the real [`Grassmann`](@ref) manifold. The formula reads[^Rentmeesters2011] @@ -306,9 +311,9 @@ The formula reads[^Rentmeesters2011] > Riemannian manifolds,” in 2011 50th IEEE Conference on Decision and Control and > European Control Conference, Dec. 2011, pp. 7141–7146. doi: [10.1109/CDC.2011.6161280](https://doi.org/10.1109/CDC.2011.6161280). """ -riemann_tensor(::Grassmann{n,k,ℝ}, p, X, Y, Z) where {n,k} +riemann_tensor(::Grassmann{<:Any,ℝ}, p, X, Y, Z) -function riemann_tensor!(::Grassmann{n,k,ℝ}, Xresult, p, X, Y, Z) where {n,k} +function riemann_tensor!(::Grassmann{<:Any,ℝ}, Xresult, p, X, Y, Z) XYᵀ = X * Y' YXᵀ = XYᵀ' YᵀX = Y' * X @@ -317,14 +322,18 @@ function riemann_tensor!(::Grassmann{n,k,ℝ}, Xresult, p, X, Y, Z) where {n,k} return Xresult end -function Base.show(io::IO, ::Grassmann{n,k,𝔽}) where {n,k,𝔽} +function Base.show(io::IO, ::Grassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return print(io, "Grassmann($(n), $(k), $(𝔽); parameter=:type)") +end +function Base.show(io::IO, M::Grassmann{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_nk(M) return print(io, "Grassmann($(n), $(k), $(𝔽))") end Base.show(io::IO, p::StiefelPoint) = print(io, "StiefelPoint($(p.value))") Base.show(io::IO, X::StiefelTVector) = print(io, "StiefelTVector($(X.value))") """ - uniform_distribution(M::Grassmann{n,k,ℝ}, p) + uniform_distribution(M::Grassmann{<:Any,ℝ}, p) Uniform distribution on given (real-valued) [`Grassmann`](@ref) `M`. Specifically, this is the normalized Haar measure on `M`. @@ -337,7 +346,8 @@ see also Theorem 2.2.2(iii) in [^Chikuse2003]. > Y. Chikuse: "Statistics on Special Manifolds", Springer New York, 2003, > doi: [10.1007/978-0-387-21540-2](https://doi.org/10.1007/978-0-387-21540-2). """ -function uniform_distribution(M::Grassmann{n,k,ℝ}, p) where {n,k} +function uniform_distribution(M::Grassmann{<:Any,ℝ}, p) + n, k = get_nk(M) μ = Distributions.Zeros(n, k) σ = one(eltype(p)) Σ1 = Distributions.PDMats.ScalMat(n, σ) @@ -348,7 +358,7 @@ function uniform_distribution(M::Grassmann{n,k,ℝ}, p) where {n,k} end @doc raw""" - vector_transport_to(M::Grassmann,p,X,q,::ProjectionTransport) + vector_transport_to(M::Grassmann, p, X, q, ::ProjectionTransport) compute the projection based transport on the [`Grassmann`](@ref) `M` by interpreting `X` from the tangent space at `p` as a point in the embedding and diff --git a/src/manifolds/Lorentz.jl b/src/manifolds/Lorentz.jl index aab3701be3..831d4fee68 100644 --- a/src/manifolds/Lorentz.jl +++ b/src/manifolds/Lorentz.jl @@ -27,20 +27,22 @@ The Lorentz manifold (or Lorentzian) is a pseudo-Riemannian manifold. Generate the Lorentz manifold of dimension `n` with the [`LorentzMetric`](@ref) `m`, which is by default set to the [`MinkowskiMetric`](@ref). """ -const Lorentz = MetricManifold{ℝ,Euclidean{Tuple{N},ℝ},<:LorentzMetric} where {N} +const Lorentz = MetricManifold{ℝ,Euclidean{T,ℝ},<:LorentzMetric} where {T} -function Lorentz(n, m::MT=MinkowskiMetric()) where {MT<:LorentzMetric} - return Lorentz{n,typeof(m)}(Euclidean(n), m) +function Lorentz(n::Int, m::LorentzMetric=MinkowskiMetric(); parameter::Symbol=:field) + E = Euclidean(n; parameter=parameter) + return Lorentz(E, m) +end +function Lorentz(E::Euclidean{T}, m::LorentzMetric=MinkowskiMetric()) where {T} + return Lorentz{T,typeof(m)}(E, m) end -function local_metric( - ::MetricManifold{ℝ,Euclidean{Tuple{N},ℝ},MinkowskiMetric}, - p, -) where {N} - return Diagonal([ones(N - 1)..., -1]) +function local_metric(M::Lorentz{Tuple{Int},MinkowskiMetric}, p) + n = M.manifold.size[1] + return Diagonal([ones(n - 1)..., -1]) end -function inner(::MetricManifold{ℝ,Euclidean{Tuple{N},ℝ},MinkowskiMetric}, p, X, Y) where {N} +function inner(::Lorentz{<:Any,MinkowskiMetric}, p, X, Y) return minkowski_metric(X, Y) end @doc raw""" diff --git a/src/manifolds/Oblique.jl b/src/manifolds/Oblique.jl index f03a213fe1..3871d969fa 100644 --- a/src/manifolds/Oblique.jl +++ b/src/manifolds/Oblique.jl @@ -1,5 +1,5 @@ @doc raw""" - Oblique{N,M,𝔽} <: AbstractPowerManifold{𝔽} + Oblique{T,𝔽,S} <: AbstractPowerManifold{𝔽} The oblique manifold $\mathcal{OB}(n,m)$ is the set of 𝔽-valued matrices with unit norm column endowed with the metric from the embedding. This yields exactly the same metric as @@ -11,31 +11,40 @@ The [`Sphere`](@ref) is stored internally within `M.manifold`, such that all fun # Constructor - Oblique(n,m) + Oblique(n::Int, m::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) Generate the manifold of matrices $\mathbb R^{n × m}$ such that the $m$ columns are unit vectors, i.e. from the [`Sphere`](@ref)`(n-1)`. """ -struct Oblique{N,M,𝔽,S} <: - AbstractPowerManifold{𝔽,Sphere{S,𝔽},ArrayPowerRepresentation} where {N,M} +struct Oblique{T,𝔽,S} <: AbstractPowerManifold{𝔽,Sphere{S,𝔽},ArrayPowerRepresentation} + size::T manifold::Sphere{S,𝔽} end -function Oblique(n::Int, m::Int, field::AbstractNumbers=ℝ) - return Oblique{n,m,field,n - 1}(Sphere(n - 1, field)) +function Oblique(n::Int, m::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) + sphere = Sphere(n - 1, field; parameter=parameter) + size = wrap_type_parameter(parameter, (n, m)) + return Oblique{typeof(size),field,typeof(sphere).parameters[1]}(size, sphere) end -Base.:^(M::Sphere{N,𝔽}, m::Int) where {N,𝔽} = Oblique{manifold_dimension(M) + 1,m,𝔽,N}(M) +function Base.:^(::Sphere{TypeParameter{Tuple{N}},𝔽}, m::Int) where {N,𝔽} + return Oblique(N + 1, m, 𝔽; parameter=:type) +end +function Base.:^(M::Sphere{Tuple{Int},𝔽}, m::Int) where {𝔽} + N = M.size[1] + return Oblique(N + 1, m, 𝔽) +end @doc raw""" - check_point(M::Oblique{n,m},p) + check_point(M::Oblique, p) Checks whether `p` is a valid point on the [`Oblique`](@ref)`{m,n}` `M`, i.e. is a matrix of `m` unit columns from $\mathbb R^{n}$, i.e. each column is a point from [`Sphere`](@ref)`(n-1)`. """ check_point(::Oblique, ::Any) -function check_point(M::Oblique{n,m}, p; kwargs...) where {n,m} +function check_point(M::Oblique, p; kwargs...) + n, m = get_nm(M) return check_point(PowerManifold(M.manifold, m), p; kwargs...) end @doc raw""" @@ -45,18 +54,23 @@ Checks whether `X` is a valid tangent vector to `p` on the [`Oblique`](@ref) `M` This means, that `p` is valid, that `X` is of correct dimension and columnswise a tangent vector to the columns of `p` on the [`Sphere`](@ref). """ -function check_vector(M::Oblique{n,m}, p, X; kwargs...) where {n,m} +function check_vector(M::Oblique, p, X; kwargs...) + n, m = get_nm(M) return check_vector(PowerManifold(M.manifold, m), p, X; kwargs...) end -get_iterator(::Oblique{n,m}) where {n,m} = Base.OneTo(m) +get_iterator(M::Oblique) = Base.OneTo(get_nm(M)[2]) -@generated function manifold_dimension(::Oblique{n,m,𝔽}) where {n,m,𝔽} +get_nm(::Oblique{TypeParameter{Tuple{n,m}}}) where {n,m} = (n, m) +get_nm(M::Oblique{Tuple{Int,Int}}) = get_parameter(M.size) + +function manifold_dimension(M::Oblique{<:Any,𝔽}) where {𝔽} + n, m = get_nm(M) return (n * real_dimension(𝔽) - 1) * m end -power_dimensions(::Oblique{n,m}) where {n,m} = (m,) +power_dimensions(M::Oblique) = get_nm(M)[2] -@generated representation_size(::Oblique{n,m}) where {n,m} = (n, m) +representation_size(M::Oblique) = get_nm(M) @doc raw""" parallel_transport_to(M::Oblique, p, X, q) @@ -67,6 +81,10 @@ doing a column wise parallel transport on the [`Sphere`](@ref) """ parallel_transport_to(::Oblique, p, X, q) -function Base.show(io::IO, ::Oblique{n,m,𝔽}) where {n,m,𝔽} - return print(io, "Oblique($(n),$(m); field = $(𝔽))") +function Base.show(io::IO, ::Oblique{TypeParameter{Tuple{n,m}},𝔽}) where {n,m,𝔽} + return print(io, "Oblique($(n), $(m); field = $(𝔽), parameter=:type)") +end +function Base.show(io::IO, M::Oblique{Tuple{Int,Int},𝔽}) where {𝔽} + n, m = get_nm(M) + return print(io, "Oblique($(n), $(m); field = $(𝔽))") end diff --git a/src/manifolds/Orthogonal.jl b/src/manifolds/Orthogonal.jl index de3051a00e..ba4360ed91 100644 --- a/src/manifolds/Orthogonal.jl +++ b/src/manifolds/Orthogonal.jl @@ -7,7 +7,10 @@ The manifold of (real) orthogonal matrices ``\mathrm{O}(n)``. """ const OrthogonalMatrices{n} = GeneralUnitaryMatrices{n,ℝ,AbsoluteDeterminantOneMatrices} -OrthogonalMatrices(n) = OrthogonalMatrices{n}() +function OrthogonalMatrices(n::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return OrthogonalMatrices{typeof(size)}(size) +end function Random.rand!( rng::AbstractRNG, @@ -33,6 +36,10 @@ function Random.rand!( return pX end -function Base.show(io::IO, ::OrthogonalMatrices{n}) where {n} - return print(io, "OrthogonalMatrices($(n))") +function Base.show(io::IO, ::OrthogonalMatrices{TypeParameter{n}}) where {n} + return print(io, "OrthogonalMatrices($(n); parameter=:type)") +end +function Base.show(io::IO, M::OrthogonalMatrices{Tuple{Int}}) + n = get_n(M) + return print(io, "OrthogonalMatrices($n)") end diff --git a/src/manifolds/ProjectiveSpace.jl b/src/manifolds/ProjectiveSpace.jl index 71d7dbf62f..a88893bdc1 100644 --- a/src/manifolds/ProjectiveSpace.jl +++ b/src/manifolds/ProjectiveSpace.jl @@ -37,8 +37,13 @@ Generate the projective space $𝔽ℙ^{n} ⊂ 𝔽^{n+1}$, defaulting to the re $ℝℙ^n$, where `field` can also be used to generate the complex- and right-quaternionic projective spaces. """ -struct ProjectiveSpace{N,𝔽} <: AbstractProjectiveSpace{𝔽} end -ProjectiveSpace(n::Int, field::AbstractNumbers=ℝ) = ProjectiveSpace{n,field}() +struct ProjectiveSpace{T,𝔽} <: AbstractProjectiveSpace{𝔽} + size::T +end +function ProjectiveSpace(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return ProjectiveSpace{typeof(size),field}(size) +end function active_traits(f, ::AbstractProjectiveSpace, args...) return merge_traits(IsIsometricEmbeddedManifold()) @@ -80,9 +85,16 @@ Generate the projective space $𝔽ℙ^{n_1, n_2, …, n_i}$, defaulting to the space, where `field` can also be used to generate the complex- and right-quaternionic projective spaces. """ -struct ArrayProjectiveSpace{N,𝔽} <: AbstractProjectiveSpace{𝔽} where {N<:Tuple} end -function ArrayProjectiveSpace(n::Vararg{Int,I}; field::AbstractNumbers=ℝ) where {I} - return ArrayProjectiveSpace{Tuple{n...},field}() +struct ArrayProjectiveSpace{T,𝔽} <: AbstractProjectiveSpace{𝔽} + size::T +end +function ArrayProjectiveSpace( + n::Vararg{Int,I}; + field::AbstractNumbers=ℝ, + parameter=:field, +) where {I} + size = wrap_type_parameter(parameter, n) + return ArrayProjectiveSpace{typeof(size),field}(size) end function allocation_promotion_function(::AbstractProjectiveSpace{ℂ}, f, args::Tuple) @@ -164,8 +176,9 @@ function exp!(M::AbstractProjectiveSpace, q, p, X) return q end -function get_basis(::ProjectiveSpace{n,ℝ}, p, B::DiagonalizingOrthonormalBasis{ℝ}) where {n} - return get_basis(Sphere{n,ℝ}(), p, B) +function get_basis(M::ProjectiveSpace{<:Any,ℝ}, p, B::DiagonalizingOrthonormalBasis{ℝ}) + n = get_n(M) + return get_basis(Sphere(n), p, B) end @doc raw""" @@ -238,6 +251,9 @@ function get_vector_orthonormal!( return Y end +get_n(::ProjectiveSpace{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::ProjectiveSpace{Tuple{Int}}) = get_parameter(M.size)[1] + injectivity_radius(::AbstractProjectiveSpace) = π / 2 injectivity_radius(::AbstractProjectiveSpace, p) = π / 2 injectivity_radius(::AbstractProjectiveSpace, ::AbstractRetractionMethod) = π / 2 @@ -408,8 +424,13 @@ project!(::AbstractProjectiveSpace, Y, p, X) = (Y .= X .- p .* dot(p, X)) Return the size points on the [`AbstractProjectiveSpace`](@ref) `M` are represented as, i.e., the representation size of the embedding. """ -@generated representation_size(::ArrayProjectiveSpace{N}) where {N} = size_to_tuple(N) -@generated representation_size(::ProjectiveSpace{N}) where {N} = (N + 1,) +function representation_size(M::ArrayProjectiveSpace) + return get_parameter(M.size) +end +function representation_size(M::ProjectiveSpace) + n = get_n(M) + return (n + 1,) +end @doc raw""" retract(M::AbstractProjectiveSpace, p, X, method::ProjectionRetraction) @@ -447,20 +468,31 @@ function retract_qr!(M::AbstractProjectiveSpace, q, p, X, t::Number) return project!(M, q, q) end -function Base.show(io::IO, ::ProjectiveSpace{n,𝔽}) where {n,𝔽} +function Base.show(io::IO, ::ProjectiveSpace{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return print(io, "ProjectiveSpace($(n), $(𝔽); parameter=:type)") +end +function Base.show(io::IO, M::ProjectiveSpace{Tuple{Int},𝔽}) where {𝔽} + n = get_n(M) return print(io, "ProjectiveSpace($(n), $(𝔽))") end -function Base.show(io::IO, ::ArrayProjectiveSpace{N,𝔽}) where {N,𝔽} - return print(io, "ArrayProjectiveSpace($(join(N.parameters, ", ")); field = $(𝔽))") +function Base.show(io::IO, ::ArrayProjectiveSpace{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return print( + io, + "ArrayProjectiveSpace($(join(n.parameters, ", ")); field = $(𝔽), parameter=:type)", + ) +end +function Base.show(io::IO, M::ArrayProjectiveSpace{<:Tuple,𝔽}) where {𝔽} + n = M.size + return print(io, "ArrayProjectiveSpace($(join(n, ", ")); field = $(𝔽))") end """ - uniform_distribution(M::ProjectiveSpace{n,ℝ}, p) where {n} + uniform_distribution(M::ProjectiveSpace{<:Any,ℝ}, p) Uniform distribution on given [`ProjectiveSpace`](@ref) `M`. Generated points will be of similar type as `p`. """ -function uniform_distribution(M::ProjectiveSpace{n,ℝ}, p) where {n} +function uniform_distribution(M::ProjectiveSpace{<:Any,ℝ}, p) d = Distributions.MvNormal(zero(p), 1.0) return ProjectedPointDistribution(M, d, project!, p) end diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index ded758a85f..a477a628e4 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,7 +1,7 @@ @doc raw""" - Rotations{N} <: AbstractManifold{ℝ} + Rotations{n} <: AbstractManifold{ℝ} -The manifold of rotation matrices of sice ``n × n``, i.e. +The manifold of rotation matrices of size ``n × n``, i.e. real-valued orthogonal matrices with determinant ``+1``. # Constructor @@ -12,7 +12,10 @@ Generate the manifold of ``n × n`` rotation matrices. """ const Rotations{n} = GeneralUnitaryMatrices{n,ℝ,DeterminantOneMatrices} -Rotations(n::Int) = Rotations{n}() +function Rotations(n::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return Rotations{typeof(size)}(size) +end """ NormalRotationDistribution(M::Rotations, d::Distribution, x::TResult) @@ -121,11 +124,8 @@ function _ev_zero(tridiagonal_elements, unitary, evec, evals, fill_at; i) return (values=evals, vectors=evec) end -function get_basis_diagonalizing( - M::Rotations{N}, - p, - B::DiagonalizingOrthonormalBasis{ℝ}, -) where {N} +function get_basis_diagonalizing(M::Rotations, p, B::DiagonalizingOrthonormalBasis{ℝ}) + n = get_n(M) decomp = schur(B.frame_direction) decomp = ordschur(decomp, map(v -> norm(v) > eps(eltype(p)), decomp.values)) @@ -135,7 +135,7 @@ function get_basis_diagonalizing( evals = Vector{eltype(B.frame_direction)}(undef, manifold_dimension(M)) i = 1 fill_at = Ref(1) - while i <= N + while i <= n if trian_elem[i] == 0 evs = _ev_zero(trian_elem, unitary, evec, evals, fill_at; i=i) i += 1 @@ -161,7 +161,10 @@ Return the radius of injectivity for the [`PolarRetraction`](https://juliamanifo [`Rotations`](@ref) `M` which is $\frac{π}{\sqrt{2}}$. """ injectivity_radius(::Rotations, ::PolarRetraction) -_injectivity_radius(::Rotations, ::PolarRetraction) = π / sqrt(2.0) +function _injectivity_radius(M::Rotations, ::PolarRetraction) + n = get_n(M) + return n == 1 ? 0.0 : π / sqrt(2.0) +end @doc raw""" inverse_retract(M, p, q, ::PolarInverseRetraction) @@ -192,7 +195,8 @@ Compute a vector from the tangent space $T_p\mathrm{SO}(n)$ of the point `p` on """ inverse_retract(::Rotations, ::Any, ::Any, ::QRInverseRetraction) -function inverse_retract_polar!(M::Rotations{n}, X, p, q) where {n} +function inverse_retract_polar!(M::Rotations, X, p, q) + n = get_n(M) A = transpose(p) * q Amat = A isa StaticMatrix ? A : convert(Matrix, A) H = copyto!(allocate(Amat), -2I) @@ -208,7 +212,8 @@ function inverse_retract_polar!(M::Rotations{n}, X, p, q) where {n} end return project!(SkewSymmetricMatrices(n), X, p, X) end -function inverse_retract_qr!(::Rotations{n}, X, p, q) where {n} +function inverse_retract_qr!(M::Rotations, X, p, q) + n = get_n(M) A = transpose(p) * q R = zero(X) for i in 1:n @@ -243,8 +248,9 @@ and second columns are swapped. The argument `p` is used to determine the type of returned points. """ -function normal_rotation_distribution(M::Rotations{N}, p, σ::Real) where {N} - d = Distributions.MvNormal(zeros(N * N), σ) +function normal_rotation_distribution(M::Rotations, p, σ::Real) + n = get_n(M) + d = Distributions.MvNormal(zeros(n * n), σ) return NormalRotationDistribution(M, d, p) end @@ -267,13 +273,14 @@ check with `check_det = false`. """ project(::Rotations, ::Any) -function project!(::Rotations{N}, q, p; check_det=true) where {N} +function project!(M::Rotations, q, p; check_det::Bool=true) + n = get_n(M) F = svd(p) mul!(q, F.U, F.Vt) if check_det && det(q) < 0 d = similar(F.S) - @inbounds fill!(view(d, 1:(N - 1)), 1) - @inbounds d[N] = -1 + @inbounds fill!(view(d, 1:(n - 1)), 1) + @inbounds d[n] = -1 copyto!(q, F.U * Diagonal(d) * F.Vt) end return q @@ -281,12 +288,13 @@ end function Random.rand( rng::AbstractRNG, - d::NormalRotationDistribution{TResult,Rotations{N}}, -) where {TResult,N} - return if N == 1 + d::NormalRotationDistribution{TResult,<:Rotations}, +) where {TResult} + n = get_n(d.manifold) + return if n == 1 convert(TResult, ones(1, 1)) else - A = reshape(rand(rng, d.distr), (N, N)) + A = reshape(rand(rng, d.distr), (n, n)) convert(TResult, _fix_random_rotation(A)) end end @@ -320,9 +328,9 @@ end function Distributions._rand!( rng::AbstractRNG, - d::NormalRotationDistribution{TResult,Rotations{N}}, + d::NormalRotationDistribution, x::AbstractArray{<:Real}, -) where {TResult,N} +) return copyto!(x, rand(rng, d)) end @@ -361,7 +369,7 @@ function parallel_transport_direction!(M::Rotations, Y, p, X, d) q = exp(M, p, d) return copyto!(Y, transpose(q) * p * expdhalf * X * expdhalf) end -function parallel_transport_direction!(::Rotations{2}, Y, p, X, d) +function parallel_transport_direction!(::Rotations{TypeParameter{Tuple{2}}}, Y, p, X, d) return copyto!(Y, X) end function parallel_transport_direction(M::Rotations, p, X, d) @@ -369,14 +377,14 @@ function parallel_transport_direction(M::Rotations, p, X, d) q = exp(M, p, d) return transpose(q) * p * expdhalf * X * expdhalf end -parallel_transport_direction(::Rotations{2}, p, X, d) = X +parallel_transport_direction(::Rotations{TypeParameter{Tuple{2}}}, p, X, d) = X function parallel_transport_to!(M::Rotations, Y, p, X, q) d = log(M, p, q) expdhalf = exp(d / 2) return copyto!(Y, transpose(q) * p * expdhalf * X * expdhalf) end -function parallel_transport_to!(::Rotations{2}, Y, p, X, q) +function parallel_transport_to!(::Rotations{TypeParameter{Tuple{2}}}, Y, p, X, q) return copyto!(Y, X) end function parallel_transport_to(M::Rotations, p, X, q) @@ -384,10 +392,14 @@ function parallel_transport_to(M::Rotations, p, X, q) expdhalf = exp(d / 2) return transpose(q) * p * expdhalf * X * expdhalf end -parallel_transport_to(::Rotations{2}, p, X, q) = X +parallel_transport_to(::Rotations{TypeParameter{Tuple{2}}}, p, X, q) = X -function Base.show(io::IO, ::Rotations{n}) where {n} - return print(io, "Rotations($(n))") +function Base.show(io::IO, ::Rotations{TypeParameter{Tuple{n}}}) where {n} + return print(io, "Rotations($(n); parameter=:type)") +end +function Base.show(io::IO, M::Rotations{Tuple{Int}}) + n = get_n(M) + return print(io, "Rotations($n)") end Distributions.support(d::NormalRotationDistribution) = MPointSupport(d.manifold) diff --git a/src/manifolds/SkewHermitian.jl b/src/manifolds/SkewHermitian.jl index 56c6485d7d..e5be7cf02b 100644 --- a/src/manifolds/SkewHermitian.jl +++ b/src/manifolds/SkewHermitian.jl @@ -22,14 +22,17 @@ which is also reflected in the Generate the manifold of ``n × n`` skew-hermitian matrices. """ -struct SkewHermitianMatrices{n,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct SkewHermitianMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end -function SkewHermitianMatrices(n::Int, field::AbstractNumbers=ℝ) - return SkewHermitianMatrices{n,field}() +function SkewHermitianMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return SkewHermitianMatrices{typeof(size),field}(size) end @doc raw""" - SkewSymmetricMatrices{n} + SkewSymmetricMatrices{T} Generate the manifold of ``n × n`` real skew-symmetric matrices. This is equivalent to [`SkewHermitianMatrices(n, ℝ)`](@ref). @@ -38,10 +41,11 @@ This is equivalent to [`SkewHermitianMatrices(n, ℝ)`](@ref). SkewSymmetricMatrices(n::Int) """ -const SkewSymmetricMatrices{n} = SkewHermitianMatrices{n,ℝ} +const SkewSymmetricMatrices{T} = SkewHermitianMatrices{T,ℝ} -SkewSymmetricMatrices(n::Int) = SkewSymmetricMatrices{n}() -@deprecate SkewSymmetricMatrices(n::Int, 𝔽) SkewHermitianMatrices(n, 𝔽) +function SkewSymmetricMatrices(n::Int; parameter::Symbol=:field) + return SkewHermitianMatrices(n; parameter=parameter) +end function active_traits(f, ::SkewHermitianMatrices, args...) return merge_traits(IsEmbeddedSubmanifold()) @@ -56,7 +60,7 @@ function allocation_promotion_function( end @doc raw""" - check_point(M::SkewHermitianMatrices{n,𝔽}, p; kwargs...) + check_point(M::SkewHermitianMatrices, p; kwargs...) Check whether `p` is a valid manifold point on the [`SkewHermitianMatrices`](@ref) `M`, i.e. whether `p` is a skew-hermitian matrix of size `(n,n)` with values from the corresponding @@ -64,7 +68,7 @@ whether `p` is a skew-hermitian matrix of size `(n,n)` with values from the corr The tolerance for the skew-symmetry of `p` can be set using `kwargs...`. """ -function check_point(M::SkewHermitianMatrices{n,𝔽}, p; kwargs...) where {n,𝔽} +function check_point(M::SkewHermitianMatrices{<:Any,𝔽}, p; kwargs...) where {𝔽} if !isapprox(p, -p'; kwargs...) return DomainError( norm(p + p'), @@ -75,7 +79,7 @@ function check_point(M::SkewHermitianMatrices{n,𝔽}, p; kwargs...) where {n, end """ - check_vector(M::SkewHermitianMatrices{n}, p, X; kwargs... ) + check_vector(M::SkewHermitianMatrices, p, X; kwargs... ) Check whether `X` is a tangent vector to manifold point `p` on the [`SkewHermitianMatrices`](@ref) `M`, i.e. `X` must be a skew-hermitian matrix of size `(n,n)` @@ -92,13 +96,8 @@ function get_basis(M::SkewHermitianMatrices, p, B::DiagonalizingOrthonormalBasis return CachedBasis(B, κ, Ξ) end -function get_coordinates_orthonormal!( - M::SkewSymmetricMatrices{N}, - Y, - p, - X, - ::RealNumbers, -) where {N} +function get_coordinates_orthonormal!(M::SkewSymmetricMatrices, Y, p, X, ::RealNumbers) + N = get_n(M) dim = manifold_dimension(M) @assert size(Y) == (dim,) @assert size(X) == (N, N) @@ -111,12 +110,13 @@ function get_coordinates_orthonormal!( return Y end function get_coordinates_orthonormal!( - M::SkewHermitianMatrices{N,ℂ}, + M::SkewHermitianMatrices{<:Any,ℂ}, Y, p, X, ::ComplexNumbers, -) where {N} +) + N = get_n(M) dim = manifold_dimension(M) @assert size(Y) == (dim,) @assert size(X) == (N, N) @@ -135,15 +135,16 @@ function get_coordinates_orthonormal!( return Y end -get_embedding(::SkewHermitianMatrices{N,𝔽}) where {N,𝔽} = Euclidean(N, N; field=𝔽) +function get_embedding(::SkewHermitianMatrices{TypeParameter{Tuple{N}},𝔽}) where {N,𝔽} + return Euclidean(N, N; field=𝔽, parameter=:type) +end +function get_embedding(M::SkewHermitianMatrices{Tuple{Int},𝔽}) where {𝔽} + N = get_n(M) + return Euclidean(N, N; field=𝔽) +end -function get_vector_orthonormal!( - M::SkewSymmetricMatrices{N}, - Y, - p, - X, - ::RealNumbers, -) where {N} +function get_vector_orthonormal!(M::SkewSymmetricMatrices, Y, p, X, ::RealNumbers) + N = get_n(M) dim = manifold_dimension(M) @assert size(X) == (dim,) @assert size(Y) == (N, N) @@ -159,12 +160,13 @@ function get_vector_orthonormal!( return Y end function get_vector_orthonormal!( - M::SkewHermitianMatrices{N,ℂ}, + M::SkewHermitianMatrices{<:Any,ℂ}, Y, p, X, ::ComplexNumbers, -) where {N} +) + N = get_n(M) dim = manifold_dimension(M) @assert size(X) == (dim,) @assert size(Y) == (N, N) @@ -182,6 +184,9 @@ function get_vector_orthonormal!( return Y end +get_n(::SkewHermitianMatrices{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::SkewHermitianMatrices{Tuple{Int}}) = get_parameter(M.size)[1] + """ is_flat(::SkewHermitianMatrices) @@ -190,7 +195,7 @@ Return true. [`SkewHermitianMatrices`](@ref) is a flat manifold. is_flat(M::SkewHermitianMatrices) = true @doc raw""" - manifold_dimension(M::SkewHermitianMatrices{n,𝔽}) + manifold_dimension(M::SkewHermitianMatrices) Return the dimension of the [`SkewHermitianMatrices`](@ref) matrix `M` over the number system `𝔽`, i.e. @@ -203,11 +208,12 @@ where ``\dim_ℝ 𝔽`` is the [`real_dimension`](https://juliamanifolds.github. only the upper triangular elements of the matrix being unique, and the second term corresponds to the constraint that the real part of the diagonal be zero. """ -function manifold_dimension(::SkewHermitianMatrices{N,𝔽}) where {N,𝔽} +function manifold_dimension(M::SkewHermitianMatrices{<:Any,𝔽}) where {𝔽} + N = get_n(M) return div(N * (N + 1), 2) * real_dimension(𝔽) - N end -function number_of_coordinates(M::SkewHermitianMatrices{N,ℂ}, ::AbstractBasis{ℂ}) where {N} +function number_of_coordinates(M::SkewHermitianMatrices{<:Any,ℂ}, ::AbstractBasis{ℂ}) return manifold_dimension(M) end @@ -244,11 +250,22 @@ project(::SkewHermitianMatrices, ::Any, ::Any) project!(M::SkewHermitianMatrices, Y, p, X) = project!(M, Y, X) -representation_size(::SkewHermitianMatrices{N}) where {N} = (N, N) +function representation_size(M::SkewHermitianMatrices) + N = get_n(M) + return (N, N) +end -function Base.show(io::IO, ::SkewHermitianMatrices{n,F}) where {n,F} +function Base.show(io::IO, ::SkewHermitianMatrices{TypeParameter{Tuple{n}},F}) where {n,F} + return print(io, "SkewHermitianMatrices($(n), $(F); parameter=:type)") +end +function Base.show(io::IO, ::SkewSymmetricMatrices{TypeParameter{Tuple{n}}}) where {n} + return print(io, "SkewSymmetricMatrices($(n); parameter=:type)") +end +function Base.show(io::IO, M::SkewHermitianMatrices{Tuple{Int},F}) where {F} + n = get_n(M) return print(io, "SkewHermitianMatrices($(n), $(F))") end -function Base.show(io::IO, ::SkewSymmetricMatrices{n}) where {n} +function Base.show(io::IO, M::SkewSymmetricMatrices{Tuple{Int}}) + n = get_n(M) return print(io, "SkewSymmetricMatrices($(n))") end diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index a40e0e5503..4bea4c9041 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -10,7 +10,7 @@ function active_traits(f, ::AbstractSphere, args...) end @doc raw""" - Sphere{n,𝔽} <: AbstractSphere{𝔽} + Sphere{T,𝔽} <: AbstractSphere{𝔽} The (unit) sphere manifold $𝕊^{n}$ is the set of all unit norm vectors in $𝔽^{n+1}$. The sphere is represented in the embedding, i.e. @@ -49,8 +49,13 @@ and the [`zero_vector`](@ref zero_vector(::Euclidean, ::Any...)) are inherited f Generate the (real-valued) sphere $𝕊^{n} ⊂ ℝ^{n+1}$, where `field` can also be used to generate the complex- and quaternionic-valued sphere. """ -struct Sphere{N,𝔽} <: AbstractSphere{𝔽} end -Sphere(n::Int, field::AbstractNumbers=ℝ) = Sphere{n,field}() +struct Sphere{T,𝔽} <: AbstractSphere{𝔽} + size::T +end +function Sphere(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return Sphere{typeof(size),field}(size) +end @doc raw""" ArraySphere{T<:Tuple,𝔽} <: AbstractSphere{𝔽} @@ -88,9 +93,12 @@ several functions like the [`inner`](@ref inner(::Euclidean, ::Any...)) product Generate sphere in $𝔽^{n_1, n_2, …, n_i}$, where $𝔽$ defaults to the real-valued case $ℝ$. """ -struct ArraySphere{N,𝔽} <: AbstractSphere{𝔽} where {N<:Tuple} end -function ArraySphere(n::Vararg{Int,I}; field::AbstractNumbers=ℝ) where {I} - return ArraySphere{Tuple{n...},field}() +struct ArraySphere{T,𝔽} <: AbstractSphere{𝔽} + size::T +end +function ArraySphere(n::Vararg{Int,I}; field::AbstractNumbers=ℝ, parameter=:field) where {I} + size = wrap_type_parameter(parameter, n) + return ArraySphere{typeof(size),field}(size) end """ @@ -185,11 +193,8 @@ function exp!(M::AbstractSphere, q, p, X, t::Number) return q end -function get_basis_diagonalizing( - M::Sphere{n,ℝ}, - p, - B::DiagonalizingOrthonormalBasis{ℝ}, -) where {n} +function get_basis_diagonalizing(M::Sphere{<:Any,ℝ}, p, B::DiagonalizingOrthonormalBasis{ℝ}) + n = get_n(M) A = zeros(n + 1, n + 1) A[1, :] = transpose(p) A[2, :] = transpose(B.frame_direction) @@ -264,6 +269,9 @@ function get_vector_orthonormal!(M::AbstractSphere{ℝ}, Y, p, X, ::RealNumbers) return Y end +get_n(::Sphere{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::Sphere{Tuple{Int}}) = get_parameter(M.size)[1] + @doc raw""" injectivity_radius(M::AbstractSphere[, p]) @@ -317,7 +325,15 @@ return the local representation of the metric in a [`DefaultOrthonormalBasis`](h the diagonal matrix of size ``n×n`` with ones on the diagonal, since the metric is obtained from the embedding by restriction to the tangent space ``T_p\mathcal M`` at ``p``. """ -function local_metric(::Sphere{n,ℝ}, p, B::DefaultOrthonormalBasis) where {n} +function local_metric(M::Sphere{Tuple{Int},ℝ}, p, ::DefaultOrthonormalBasis) + n = get_n(M) + return Diagonal(ones(eltype(p), n)) +end +function local_metric( + ::Sphere{TypeParameter{Tuple{n}},ℝ}, + p, + B::DefaultOrthonormalBasis, +) where {n} return Diagonal(ones(SVector{n,eltype(p)})) end @@ -437,8 +453,13 @@ end Return the size points on the [`AbstractSphere`](@ref) `M` are represented as, i.e., the representation size of the embedding. """ -@generated representation_size(::ArraySphere{N}) where {N} = size_to_tuple(N) -@generated representation_size(::Sphere{N}) where {N} = (N + 1,) +function representation_size(M::ArraySphere) + return get_parameter(M.size) +end +function representation_size(M::Sphere) + n = get_n(M) + return (n + 1,) +end @doc raw""" retract(M::AbstractSphere, p, X, ::ProjectionRetraction) @@ -456,9 +477,22 @@ function retract_project!(M::AbstractSphere, q, p, X, t::Number) return project!(M, q, q) end -Base.show(io::IO, ::Sphere{n,𝔽}) where {n,𝔽} = print(io, "Sphere($(n), $(𝔽))") -function Base.show(io::IO, ::ArraySphere{N,𝔽}) where {N,𝔽} - return print(io, "ArraySphere($(join(N.parameters, ", ")); field = $(𝔽))") +function Base.show(io::IO, ::Sphere{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return print(io, "Sphere($(n), $(𝔽); parameter=:type)") +end +function Base.show(io::IO, M::Sphere{Tuple{Int},𝔽}) where {𝔽} + n = get_n(M) + return print(io, "Sphere($(n), $(𝔽))") +end +function Base.show(io::IO, ::ArraySphere{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return print( + io, + "ArraySphere($(join(n.parameters, ", ")); field = $(𝔽), parameter=:type)", + ) +end +function Base.show(io::IO, M::ArraySphere{<:Tuple,𝔽}) where {𝔽} + n = M.size + return print(io, "ArraySphere($(join(n, ", ")); field = $(𝔽))") end """ @@ -467,7 +501,8 @@ end Uniform distribution on given [`Sphere`](@ref) `M`. Generated points will be of similar type as `p`. """ -function uniform_distribution(M::Sphere{n,ℝ}, p) where {n} +function uniform_distribution(M::Sphere{<:Any,ℝ}, p) + n = get_n(M) d = Distributions.MvNormal(zero(p), 1.0) return ProjectedPointDistribution(M, d, project!, p) end @@ -527,7 +562,7 @@ point (1, 0, ..., 0) (called `:south`). """ struct StereographicAtlas <: AbstractAtlas{ℝ} end -function get_chart_index(::Sphere{n,ℝ}, ::StereographicAtlas, p) where {n} +function get_chart_index(::Sphere{<:Any,ℝ}, ::StereographicAtlas, p) if p[1] < 0 return :south else @@ -535,7 +570,7 @@ function get_chart_index(::Sphere{n,ℝ}, ::StereographicAtlas, p) where {n} end end -function get_parameters!(::Sphere{n,ℝ}, x, ::StereographicAtlas, i::Symbol, p) where {n} +function get_parameters!(::Sphere{<:Any,ℝ}, x, ::StereographicAtlas, i::Symbol, p) if i === :north return x .= p[2:end] ./ (1 + p[1]) else @@ -543,7 +578,7 @@ function get_parameters!(::Sphere{n,ℝ}, x, ::StereographicAtlas, i::Symbol, p) end end -function get_point!(::Sphere{n,ℝ}, p, ::StereographicAtlas, i::Symbol, x) where {n} +function get_point!(::Sphere{<:Any,ℝ}, p, ::StereographicAtlas, i::Symbol, x) xnorm2 = dot(x, x) if i === :north p[1] = (1 - xnorm2) / (xnorm2 + 1) @@ -555,12 +590,13 @@ function get_point!(::Sphere{n,ℝ}, p, ::StereographicAtlas, i::Symbol, x) wher end function get_coordinates_induced_basis!( - ::Sphere{n,ℝ}, + M::Sphere{<:Any,ℝ}, Y, p, X, B::InducedBasis{ℝ,TangentSpaceType,<:StereographicAtlas}, -) where {n} +) + n = get_n(M) if B.i === :north for i in 1:n Y[i] = X[i + 1] / (1 + p[1]) - X[1] * p[i + 1] / (1 + p[1])^2 @@ -574,12 +610,13 @@ function get_coordinates_induced_basis!( end function get_vector_induced_basis!( - M::Sphere{n,ℝ}, + M::Sphere{<:Any,ℝ}, Y, p, X, B::InducedBasis{ℝ,TangentSpaceType,<:StereographicAtlas}, -) where {n} +) + n = get_n(M) a = get_parameters(M, B.A, B.i, p) mult = inv(1 + dot(a, a))^2 @@ -604,10 +641,10 @@ function get_vector_induced_basis!( end function local_metric( - M::Sphere{n,ℝ}, + M::Sphere{<:Any,ℝ}, p, B::InducedBasis{ℝ,TangentSpaceType,StereographicAtlas,Symbol}, -) where {n} +) a = get_parameters(M, B.A, B.i, p) return (4 / (1 + dot(a, a))^2) * I end diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index c64d651c93..5710c82772 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -13,7 +13,7 @@ and the field $𝔽 ∈ \{ ℝ, ℂ\}$. Though it is slightly redundant, usually the matrices are stored as $n × n$ arrays. Note that in this representation, the complex valued case has to have a real-valued diagonal, -which is also reflected in the [`manifold_dimension`](@ref manifold_dimension(::SymmetricMatrices{N,𝔽}) where {N,𝔽}). +which is also reflected in the [`manifold_dimension`](@ref manifold_dimension(::SymmetricMatrices)). # Constructor @@ -21,10 +21,13 @@ which is also reflected in the [`manifold_dimension`](@ref manifold_dimension(:: Generate the manifold of $n × n$ symmetric matrices. """ -struct SymmetricMatrices{n,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct SymmetricMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end -function SymmetricMatrices(n::Int, field::AbstractNumbers=ℝ) - return SymmetricMatrices{n,field}() +function SymmetricMatrices(n::Int, field::AbstractNumbers=ℝ; parameter=:field) + size = wrap_type_parameter(parameter, (n,)) + return SymmetricMatrices{typeof(size),field}(size) end function active_traits(f, ::SymmetricMatrices, args...) @@ -48,7 +51,7 @@ whether `p` is a symmetric matrix of size `(n,n)` with values from the correspon The tolerance for the symmetry of `p` can be set using `kwargs...`. """ -function check_point(M::SymmetricMatrices{n,𝔽}, p; kwargs...) where {n,𝔽} +function check_point(M::SymmetricMatrices{<:Any,𝔽}, p; kwargs...) where {𝔽} if !isapprox(norm(p - p'), 0.0; kwargs...) return DomainError( norm(p - p'), @@ -67,7 +70,7 @@ and its values have to be from the correct [`AbstractNumbers`](https://juliamani The tolerance for the symmetry of `X` can be set using `kwargs...`. """ -function check_vector(M::SymmetricMatrices{n,𝔽}, p, X; kwargs...) where {n,𝔽} +function check_vector(M::SymmetricMatrices{<:Any,𝔽}, p, X; kwargs...) where {𝔽} if !isapprox(norm(X - X'), 0.0; kwargs...) return DomainError( norm(X - X'), @@ -86,13 +89,8 @@ function get_basis(M::SymmetricMatrices, p, B::DiagonalizingOrthonormalBasis) return CachedBasis(B, κ, Ξ) end -function get_coordinates_orthonormal!( - M::SymmetricMatrices{N,ℝ}, - Y, - p, - X, - ::RealNumbers, -) where {N} +function get_coordinates_orthonormal!(M::SymmetricMatrices{<:Any,ℝ}, Y, p, X, ::RealNumbers) + N = get_n(M) dim = manifold_dimension(M) @assert size(Y) == (dim,) @assert size(X) == (N, N) @@ -106,12 +104,13 @@ function get_coordinates_orthonormal!( return Y end function get_coordinates_orthonormal!( - M::SymmetricMatrices{N,ℂ}, + M::SymmetricMatrices{<:Any,ℂ}, Y, p, X, ::ComplexNumbers, -) where {N} +) + N = get_n(M) dim = manifold_dimension(M) @assert size(Y) == (dim,) @assert size(X) == (N, N) @@ -129,15 +128,16 @@ function get_coordinates_orthonormal!( return Y end -get_embedding(::SymmetricMatrices{N,𝔽}) where {N,𝔽} = Euclidean(N, N; field=𝔽) +function get_embedding(::SymmetricMatrices{TypeParameter{N},𝔽}) where {N,𝔽} + return Euclidean(N, N; field=𝔽, parameter=:type) +end +function get_embedding(M::SymmetricMatrices{Tuple{Int},𝔽}) where {𝔽} + N = get_n(M) + return Euclidean(N, N; field=𝔽) +end -function get_vector_orthonormal!( - M::SymmetricMatrices{N,ℝ}, - Y, - p, - X, - ::RealNumbers, -) where {N} +function get_vector_orthonormal!(M::SymmetricMatrices{<:Any,ℝ}, Y, p, X, ::RealNumbers) + N = get_n(M) dim = manifold_dimension(M) @assert size(X) == (dim,) @assert size(Y) == (N, N) @@ -150,13 +150,8 @@ function get_vector_orthonormal!( end return Y end -function get_vector_orthonormal!( - M::SymmetricMatrices{N,ℂ}, - Y, - p, - X, - ::ComplexNumbers, -) where {N} +function get_vector_orthonormal!(M::SymmetricMatrices{<:Any,ℂ}, Y, p, X, ::ComplexNumbers) + N = get_n(M) dim = manifold_dimension(M) @assert size(X) == (dim,) @assert size(Y) == (N, N) @@ -171,6 +166,9 @@ function get_vector_orthonormal!( end ## unify within bases later. +get_n(::SymmetricMatrices{TypeParameter{n}}) where {n} = n +get_n(M::SymmetricMatrices{Tuple{Int}}) = get_parameter(M.size)[1] + """ is_flat(::SymmetricMatrices) @@ -193,7 +191,8 @@ Return the dimension of the [`SymmetricMatrices`](@ref) matrix `M` over the numb where the last $-n$ is due to the zero imaginary part for Hermitian matrices """ -function manifold_dimension(::SymmetricMatrices{N,𝔽}) where {N,𝔽} +function manifold_dimension(M::SymmetricMatrices{<:Any,𝔽}) where {𝔽} + N = get_n(M) return div(N * (N + 1), 2) * real_dimension(𝔽) - (𝔽 === ℂ ? N : 0) end diff --git a/src/manifolds/Unitary.jl b/src/manifolds/Unitary.jl index 2dc220c724..81350ef73d 100644 --- a/src/manifolds/Unitary.jl +++ b/src/manifolds/Unitary.jl @@ -27,26 +27,29 @@ the representation above. see also [`OrthogonalMatrices`](@ref) for the real valued case. """ -const UnitaryMatrices{n,𝔽} = GeneralUnitaryMatrices{n,𝔽,AbsoluteDeterminantOneMatrices} +const UnitaryMatrices{T,𝔽} = GeneralUnitaryMatrices{T,𝔽,AbsoluteDeterminantOneMatrices} -UnitaryMatrices(n::Int, 𝔽::AbstractNumbers=ℂ) = UnitaryMatrices{n,𝔽}() +function UnitaryMatrices(n::Int, 𝔽::AbstractNumbers=ℂ; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return UnitaryMatrices{typeof(size),𝔽}(size) +end -check_size(::UnitaryMatrices{1,ℍ}, p::Number) = nothing -check_size(::UnitaryMatrices{1,ℍ}, p, X::Number) = nothing +check_size(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p::Number) = nothing +check_size(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p, X::Number) = nothing -embed(::UnitaryMatrices{1,ℍ}, p::Number) = SMatrix{1,1}(p) +embed(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p::Number) = SMatrix{1,1}(p) -embed(::UnitaryMatrices{1,ℍ}, p, X::Number) = SMatrix{1,1}(X) +embed(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p, X::Number) = SMatrix{1,1}(X) -function exp(::UnitaryMatrices{1,ℍ}, p, X::Number) +function exp(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p, X::Number) return p * exp(X) end -function exp(::UnitaryMatrices{1,ℍ}, p, X::Number, t::Number) +function exp(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p, X::Number, t::Number) return p * exp(t * X) end function get_coordinates_orthonormal( - ::UnitaryMatrices{1,ℍ}, + ::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p, X::Quaternions.Quaternion, ::QuaternionNumbers, @@ -55,7 +58,7 @@ function get_coordinates_orthonormal( end function get_vector_orthonormal( - ::UnitaryMatrices{1,ℍ}, + ::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p::Quaternions.Quaternion, c, ::QuaternionNumbers, @@ -64,12 +67,16 @@ function get_vector_orthonormal( return Quaternions.quat(0, c[i], c[i + 1], c[i + 2]) end -injectivity_radius(::UnitaryMatrices{1,ℍ}) = π +injectivity_radius(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}) = π -_isapprox(::UnitaryMatrices{1,ℍ}, x, y; kwargs...) = isapprox(x[], y[]; kwargs...) -_isapprox(::UnitaryMatrices{1,ℍ}, p, X, Y; kwargs...) = isapprox(X[], Y[]; kwargs...) +function _isapprox(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, x, y; kwargs...) + return isapprox(x[], y[]; kwargs...) +end +function _isapprox(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p, X, Y; kwargs...) + return isapprox(X[], Y[]; kwargs...) +end -function log(::UnitaryMatrices{1,ℍ}, p::Number, q::Number) +function log(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p::Number, q::Number) return log(conj(p) * q) end @@ -81,7 +88,10 @@ Return the dimension of the manifold unitary matrices. \dim_{\mathrm{U}(n)} = n^2. ``` """ -manifold_dimension(::UnitaryMatrices{n,ℂ}) where {n} = n^2 +function manifold_dimension(M::UnitaryMatrices{<:Any,ℂ}) + n = get_n(M) + return n^2 +end @doc raw""" manifold_dimension(M::UnitaryMatrices{n,ℍ}) @@ -90,22 +100,29 @@ Return the dimension of the manifold unitary matrices. \dim_{\mathrm{U}(n, ℍ)} = n(2n+1). ``` """ -manifold_dimension(::UnitaryMatrices{n,ℍ}) where {n} = n * (2n + 1) +function manifold_dimension(M::UnitaryMatrices{<:Any,ℍ}) + n = get_n(M) + return n * (2n + 1) +end -Manifolds.number_of_coordinates(::UnitaryMatrices{1,ℍ}, ::AbstractBasis{ℍ}) = 3 +number_of_coordinates(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, ::AbstractBasis{ℍ}) = 3 -project(::UnitaryMatrices{1,ℍ}, p) = sign(p) +project(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p) = sign(p) -project(::UnitaryMatrices{1,ℍ}, p, X) = (X - conj(X)) / 2 +project(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p, X) = (X - conj(X)) / 2 -function Random.rand(M::UnitaryMatrices{1,ℍ}; vector_at=nothing) +function Random.rand(M::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}; vector_at=nothing) if vector_at === nothing return sign(rand(Quaternions.QuaternionF64)) else project(M, vector_at, rand(Quaternions.QuaternionF64)) end end -function Random.rand(rng::AbstractRNG, M::UnitaryMatrices{1,ℍ}; vector_at=nothing) +function Random.rand( + rng::AbstractRNG, + M::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}; + vector_at=nothing, +) if vector_at === nothing return sign(rand(rng, Quaternions.QuaternionF64)) else @@ -113,7 +130,19 @@ function Random.rand(rng::AbstractRNG, M::UnitaryMatrices{1,ℍ}; vector_at=noth end end -show(io::IO, ::UnitaryMatrices{n,ℂ}) where {n} = print(io, "UnitaryMatrices($(n))") -show(io::IO, ::UnitaryMatrices{n,ℍ}) where {n} = print(io, "UnitaryMatrices($(n), ℍ)") +function Base.show(io::IO, ::UnitaryMatrices{TypeParameter{Tuple{n}},ℂ}) where {n} + return print(io, "UnitaryMatrices($(n); parameter=:type)") +end +function Base.show(io::IO, M::UnitaryMatrices{Tuple{Int},ℂ}) + n = get_n(M) + return print(io, "UnitaryMatrices($n)") +end +function Base.show(io::IO, ::UnitaryMatrices{TypeParameter{Tuple{n}},ℍ}) where {n} + return print(io, "UnitaryMatrices($(n), ℍ; parameter=:type)") +end +function Base.show(io::IO, M::UnitaryMatrices{Tuple{Int},ℍ}) + n = get_n(M) + return print(io, "UnitaryMatrices($n, ℍ)") +end -Manifolds.zero_vector(::UnitaryMatrices{1,ℍ}, p) = zero(p) +zero_vector(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p) = zero(p) diff --git a/test/groups/general_unitary_groups.jl b/test/groups/general_unitary_groups.jl index a301f2fb02..edca64b45f 100644 --- a/test/groups/general_unitary_groups.jl +++ b/test/groups/general_unitary_groups.jl @@ -3,7 +3,7 @@ include("group_utils.jl") @testset "General Unitary Groups" begin # SpecialUnitary -> injectivity us also π√2 - SU3 = Manifolds.GeneralUnitaryMatrices{3,ℂ,Manifolds.DeterminantOneMatrices}() + SU3 = SpecialUnitary(3) @test injectivity_radius(SU3) == π * √2 @testset "Orthogonal Group" begin O2 = Orthogonal(2) @@ -47,8 +47,8 @@ include("group_utils.jl") end @testset "Quaternionic Unitary Group" begin - QU1 = Unitary(1, ℍ) - @test repr(QU1) == "Unitary(1, ℍ)" + QU1 = Unitary(1, ℍ; parameter=:type) + @test repr(QU1) == "Unitary(1, ℍ; parameter=:type)" @test identity_element(QU1) === Quaternion(1.0) @@ -86,8 +86,8 @@ include("group_utils.jl") end @testset "Special Unitary Group" begin - SU2 = SpecialUnitary(2) - @test repr(SU2) == "SpecialUnitary(2)" + SU2 = SpecialUnitary(2; parameter=:type) + @test repr(SU2) == "SpecialUnitary(2; parameter=:type)" p = ones(2, 2) q = project(SU2, p) @@ -149,22 +149,22 @@ include("group_utils.jl") end E = diagm(ones(4)) R1 = diagm([-1.0, -1.0, 1.0, 1.0]) - X1a = log(Rotations(4), E, R1) - X1b = log_lie(SpecialOrthogonal(4), R1) + X1a = log(Rotations(4; parameter=:type), E, R1) + X1b = log_lie(SpecialOrthogonal(4; parameter=:type), R1) @test isapprox(X1a, X1b) @test is_vector(Rotations(4), E, X1b) @test X1a[1, 2] ≈ π R2 = diagm([-1.0, 1.0, -1.0, 1.0]) - X2a = log(Rotations(4), E, R2) - X2b = log_lie(SpecialOrthogonal(4), R2) + X2a = log(Rotations(4; parameter=:type), E, R2) + X2b = log_lie(SpecialOrthogonal(4; parameter=:type), R2) @test isapprox(X2a, X2b) @test is_vector(Rotations(4), E, X2b) @test X2a[1, 3] ≈ π R3 = diagm([1.0, -1.0, -1.0, 1.0]) - X3a = log(Rotations(4), E, R3) - X3b = log_lie(SpecialOrthogonal(4), R3) + X3a = log(Rotations(4; parameter=:type), E, R3) + X3b = log_lie(SpecialOrthogonal(4; parameter=:type), R3) @test isapprox(X3a, X3b) @test is_vector(Rotations(4), E, X3b) @test X3a[2, 3] ≈ π diff --git a/test/groups/special_euclidean.jl b/test/groups/special_euclidean.jl index d9aa56e845..6c3545f0ce 100644 --- a/test/groups/special_euclidean.jl +++ b/test/groups/special_euclidean.jl @@ -6,330 +6,335 @@ using ManifoldsBase: VeeOrthogonalBasis Random.seed!(10) @testset "Special Euclidean group" begin - @testset "SpecialEuclidean($n)" for n in (2, 3, 4) - G = SpecialEuclidean(n) - @test isa(G, SpecialEuclidean{n}) - @test repr(G) == "SpecialEuclidean($n)" - M = base_manifold(G) - @test M === TranslationGroup(n) × SpecialOrthogonal(n) - @test submanifold(G, 1) === TranslationGroup(n) - @test submanifold(G, 2) === SpecialOrthogonal(n) - Rn = Rotations(n) - p = Matrix(I, n, n) - - if n == 2 - t = Vector{Float64}.([1:2, 2:3, 3:4]) - ω = [[1.0], [2.0], [1.0]] - tuple_pts = [(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] - tuple_X = [([-1.0, 2.0], hat(Rn, p, [1.0])), ([1.0, -2.0], hat(Rn, p, [0.5]))] - elseif n == 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]] - tuple_pts = [(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] - tuple_X = [ - ([-1.0, 2.0, 1.0], hat(Rn, p, [1.0, 0.5, -0.5])), - ([-2.0, 1.0, 0.5], hat(Rn, p, [-1.0, -0.5, 1.1])), - ] - else # n == 4 - t = Vector{Float64}.([1:4, 2:5, 3:6]) - ω = [ - [1.0, 2.0, 3.0, 1.0, 2.0, 3.0], - [3.0, 2.0, 1.0, 1.0, 2.0, 3.0], - [1.0, 3.0, 2.0, 1.0, 2.0, 3.0], - ] - tuple_pts = [(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] - tuple_X = [ - ([-1.0, 2.0, 1.0, 3.0], hat(Rn, p, [1.0, 0.5, -0.5, 0.0, 2.0, 1.0])), - ([-2.0, 1.5, -1.0, 2.0], hat(Rn, p, [1.0, -0.5, 0.5, 1.0, 0.0, 1.0])), - ] - end + for se_parameter in [:field, :type] + @testset "SpecialEuclidean($n)" for n in (2, 3, 4) + G = SpecialEuclidean(n; parameter=se_parameter) + @test isa(G, SpecialEuclidean{n}) + @test repr(G) == "SpecialEuclidean($n)" + M = base_manifold(G) + @test M === TranslationGroup(n) × SpecialOrthogonal(n) + @test submanifold(G, 1) === TranslationGroup(n) + @test submanifold(G, 2) === SpecialOrthogonal(n) + Rn = Rotations(n) + p = Matrix(I, n, n) + + if n == 2 + t = Vector{Float64}.([1:2, 2:3, 3:4]) + ω = [[1.0], [2.0], [1.0]] + tuple_pts = [(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] + tuple_X = + [([-1.0, 2.0], hat(Rn, p, [1.0])), ([1.0, -2.0], hat(Rn, p, [0.5]))] + elseif n == 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]] + tuple_pts = [(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] + tuple_X = [ + ([-1.0, 2.0, 1.0], hat(Rn, p, [1.0, 0.5, -0.5])), + ([-2.0, 1.0, 0.5], hat(Rn, p, [-1.0, -0.5, 1.1])), + ] + else # n == 4 + t = Vector{Float64}.([1:4, 2:5, 3:6]) + ω = [ + [1.0, 2.0, 3.0, 1.0, 2.0, 3.0], + [3.0, 2.0, 1.0, 1.0, 2.0, 3.0], + [1.0, 3.0, 2.0, 1.0, 2.0, 3.0], + ] + tuple_pts = [(ti, exp(Rn, p, hat(Rn, p, ωi))) for (ti, ωi) in zip(t, ω)] + tuple_X = [ + ([-1.0, 2.0, 1.0, 3.0], hat(Rn, p, [1.0, 0.5, -0.5, 0.0, 2.0, 1.0])), + ([-2.0, 1.5, -1.0, 2.0], hat(Rn, p, [1.0, -0.5, 0.5, 1.0, 0.0, 1.0])), + ] + end - basis_types = (DefaultOrthonormalBasis(),) + basis_types = (DefaultOrthonormalBasis(),) - @testset "isapprox with identity" begin - @test isapprox(G, Identity(G), identity_element(G)) - @test isapprox(G, identity_element(G), Identity(G)) - end + @testset "isapprox with identity" begin + @test isapprox(G, Identity(G), identity_element(G)) + @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 + 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 - 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=[(), (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(),), - ], - ) + 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=[(), (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, + ) - test_manifold( - G_TR, - pts; - is_mutating=true, - exp_log_atol_multiplier=50, - test_inner=false, - test_norm=false, - ) + 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 - 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, - ) + 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 end end - end - @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 "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=[(), (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 + 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 = 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 "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)) - - @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 [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), - ) + 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) + 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 end end end end @testset "Adjoint action on 𝔰𝔢(3)" begin - G = SpecialEuclidean(3) + G = SpecialEuclidean(3; parameter=:type) 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) diff --git a/test/groups/translation_group.jl b/test/groups/translation_group.jl index 07fac5d3b2..f3ea6b0285 100644 --- a/test/groups/translation_group.jl +++ b/test/groups/translation_group.jl @@ -6,6 +6,8 @@ include("group_utils.jl") G = TranslationGroup(2, 3) @test repr(G) == "TranslationGroup(2, 3; field = ℝ)" @test repr(TranslationGroup(2, 3; field=ℂ)) == "TranslationGroup(2, 3; field = ℂ)" + @test repr(TranslationGroup(2, 3; field=ℂ, parameter=:field)) == + "TranslationGroup(2, 3; field = ℂ, parameter = :field)" @test has_invariant_metric(G, LeftForwardAction()) @test has_invariant_metric(G, RightBackwardAction()) diff --git a/test/manifolds/centered_matrices.jl b/test/manifolds/centered_matrices.jl index cf77276232..5b3b37d61e 100644 --- a/test/manifolds/centered_matrices.jl +++ b/test/manifolds/centered_matrices.jl @@ -11,7 +11,7 @@ include("../utils.jl") @testset "Real Centered Matrices Basics" begin @test repr(M) == "CenteredMatrices(3, 2, ℝ)" @test representation_size(M) == (3, 2) - @test typeof(get_embedding(M)) === Euclidean{Tuple{3,2},ℝ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} @test is_flat(M) @test check_point(M, A) === nothing @test_throws ManifoldDomainError is_point(M, B, true) diff --git a/test/manifolds/euclidean.jl b/test/manifolds/euclidean.jl index 98e8fe6049..2d70ec9fdd 100644 --- a/test/manifolds/euclidean.jl +++ b/test/manifolds/euclidean.jl @@ -1,154 +1,167 @@ include("../utils.jl") using Manifolds: induced_basis +using FiniteDifferences @testset "Euclidean" begin - E = Euclidean(3) - Ec = Euclidean(3; field=ℂ) - EM = Manifolds.MetricManifold(E, Manifolds.EuclideanMetric()) - @test repr(E) == "Euclidean(3; field = ℝ)" - @test repr(Ec) == "Euclidean(3; field = ℂ)" - @test repr(Euclidean(2, 3; field=ℍ)) == "Euclidean(2, 3; field = ℍ)" - @test Manifolds.allocation_promotion_function(Ec, get_vector, ()) === complex - @test is_flat(E) - @test is_flat(Ec) - p = zeros(3) - A = Manifolds.RetractionAtlas() - B = induced_basis(EM, A, p, TangentSpace) - @test det_local_metric(EM, p, B) == one(eltype(p)) - @test log_local_metric_density(EM, p, B) == zero(eltype(p)) - @test project!(E, p, p) == p - @test embed!(E, p, p) == p - @test manifold_dimension(Ec) == 2 * manifold_dimension(E) - X = zeros(3) - X[1] = 1.0 - Y = similar(X) - project!(E, Y, p, X) - @test Y == X - @test embed(E, p, X) == X + for param in [:field, :type] + E = Euclidean(3, parameter=param) + Ec = Euclidean(3; field=ℂ, parameter=param) + EM = Manifolds.MetricManifold(E, Manifolds.EuclideanMetric()) + EH = Euclidean(2, 3; field=ℍ, parameter=param) + if param === :type + @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 - # temp: explicit test for induced basis - B = induced_basis(E, RetractionAtlas(), 0, ManifoldsBase.TangentSpaceType()) - @test get_coordinates(E, p, X, B) == X - get_coordinates!(E, Y, p, X, B) - @test Y == X - @test get_vector(E, p, Y, B) == X - Y2 = similar(X) - get_vector!(E, Y2, p, Y, B) - @test Y2 == X + @test Manifolds.allocation_promotion_function(Ec, get_vector, ()) === complex + @test is_flat(E) + @test is_flat(Ec) + p = zeros(3) + A = Manifolds.RetractionAtlas() + B = induced_basis(EM, A, p, TangentSpace) + @test det_local_metric(EM, p, B) == one(eltype(p)) + @test log_local_metric_density(EM, p, B) == zero(eltype(p)) + @test project!(E, p, p) == p + @test embed!(E, p, p) == p + @test manifold_dimension(Ec) == 2 * manifold_dimension(E) + X = zeros(3) + X[1] = 1.0 + Y = similar(X) + project!(E, Y, p, X) + @test Y == X + @test embed(E, p, X) == X - Y = parallel_transport_along(E, p, X, [p]) - @test Y == X - parallel_transport_along!(E, Y, p, X, [p]) - @test Y == X + # temp: explicit test for induced basis + B = induced_basis(E, RetractionAtlas(), 0, ManifoldsBase.TangentSpaceType()) + @test get_coordinates(E, p, X, B) == X + get_coordinates!(E, Y, p, X, B) + @test Y == X + @test get_vector(E, p, Y, B) == X + Y2 = similar(X) + get_vector!(E, Y2, p, Y, B) + @test Y2 == X - Y = vector_transport_along(E, p, X, [p]) - @test Y == X - vector_transport_along!(E, Y, p, X, [p]) - @test Y == X + Y = parallel_transport_along(E, p, X, [p]) + @test Y == X + parallel_transport_along!(E, Y, p, X, [p]) + @test Y == X - # real manifold does not allow complex values - @test_throws DomainError is_point(Ec, [:a, :b, :b], true) - @test_throws DomainError is_point(E, [1.0, 1.0im, 0.0], true) - @test_throws DomainError is_point(E, [1], true) - @test_throws DomainError is_vector(Ec, [:a, :b, :b], [1.0, 1.0, 0.0], true) - @test_throws DomainError is_vector(E, [1.0, 1.0im, 0.0], [1.0, 1.0, 0.0], true) # real manifold does not allow complex values - @test_throws DomainError is_vector(E, [1], [1.0, 1.0, 0.0], true) - @test_throws DomainError is_vector(E, [0.0, 0.0, 0.0], [1.0], true) - @test_throws DomainError is_vector(E, [0.0, 0.0, 0.0], [1.0, 0.0, 1.0im], true) - @test_throws DomainError is_vector(Ec, [0.0, 0.0, 0.0], [:a, :b, :c], true) + Y = vector_transport_along(E, p, X, [p]) + @test Y == X + vector_transport_along!(E, Y, p, X, [p]) + @test Y == X - @test E^2 === Euclidean(3, 2) - @test ^(E, 2) === Euclidean(3, 2) - @test E^(2,) === Euclidean(3, 2) - @test Ec^(4, 5) === Euclidean(3, 4, 5; field=ℂ) + # real manifold does not allow complex values + @test_throws DomainError is_point(Ec, [:a, :b, :b], true) + @test_throws DomainError is_point(E, [1.0, 1.0im, 0.0], true) + @test_throws DomainError is_point(E, [1], true) + @test_throws DomainError is_vector(Ec, [:a, :b, :b], [1.0, 1.0, 0.0], true) + @test_throws DomainError is_vector(E, [1.0, 1.0im, 0.0], [1.0, 1.0, 0.0], true) # real manifold does not allow complex values + @test_throws DomainError is_vector(E, [1], [1.0, 1.0, 0.0], true) + @test_throws DomainError is_vector(E, [0.0, 0.0, 0.0], [1.0], true) + @test_throws DomainError is_vector(E, [0.0, 0.0, 0.0], [1.0, 0.0, 1.0im], true) + @test_throws DomainError is_vector(Ec, [0.0, 0.0, 0.0], [:a, :b, :c], true) - manifolds = [E, EM, Ec] - types = [Vector{Float64}] - TEST_FLOAT32 && push!(types, Vector{Float32}) - TEST_DOUBLE64 && push!(types, Vector{Double64}) - TEST_STATIC_SIZED && push!(types, MVector{3,Float64}) + @test E^2 === Euclidean(3, 2, parameter=param) + @test ^(E, 2) === Euclidean(3, 2, parameter=param) + @test E^(2,) === Euclidean(3, 2, parameter=param) + @test Ec^(4, 5) === Euclidean(3, 4, 5; field=ℂ, parameter=param) - types_complex = [Vector{ComplexF64}] - TEST_FLOAT32 && push!(types_complex, Vector{ComplexF32}) - TEST_DOUBLE64 && push!(types_complex, Vector{ComplexDF64}) - TEST_STATIC_SIZED && push!(types_complex, MVector{3,ComplexF64}) + manifolds = [E, EM, Ec] + types = [Vector{Float64}] + TEST_FLOAT32 && push!(types, Vector{Float32}) + TEST_DOUBLE64 && push!(types, Vector{Double64}) + TEST_STATIC_SIZED && push!(types, MVector{3,Float64}) - for M in manifolds - basis_types = if M == E - ( - DefaultOrthonormalBasis(), - ProjectedOrthonormalBasis(:svd), - DiagonalizingOrthonormalBasis([1.0, 2.0, 3.0]), - ) - elseif M == Ec - ( - DefaultOrthonormalBasis(), - DefaultOrthonormalBasis(ℂ), - DiagonalizingOrthonormalBasis([1.0, 2.0, 3.0]), - DiagonalizingOrthonormalBasis([1.0, 2.0, 3.0], ℂ), - ) - else - () + types_complex = [Vector{ComplexF64}] + TEST_FLOAT32 && push!(types_complex, Vector{ComplexF32}) + TEST_DOUBLE64 && push!(types_complex, Vector{ComplexDF64}) + TEST_STATIC_SIZED && push!(types_complex, MVector{3,ComplexF64}) + + for M in manifolds + basis_types = if M == E + ( + DefaultOrthonormalBasis(), + ProjectedOrthonormalBasis(:svd), + DiagonalizingOrthonormalBasis([1.0, 2.0, 3.0]), + ) + elseif M == Ec + ( + DefaultOrthonormalBasis(), + DefaultOrthonormalBasis(ℂ), + DiagonalizingOrthonormalBasis([1.0, 2.0, 3.0]), + DiagonalizingOrthonormalBasis([1.0, 2.0, 3.0], ℂ), + ) + else + () + end + for T in types + @testset "$M Type $T" begin + pts = [ + convert(T, [1.0, 0.0, 0.0]), + convert(T, [0.0, 1.0, 0.0]), + convert(T, [0.0, 0.0, 1.0]), + ] + test_manifold( + M, + pts, + test_project_point=true, + test_project_tangent=true, + test_musical_isomorphisms=true, + test_default_vector_transport=true, + vector_transport_methods=[ + ParallelTransport(), + SchildsLadderTransport(), + PoleLadderTransport(), + ], + test_mutating_rand=isa(T, Vector), + point_distributions=[ + Manifolds.projected_distribution( + M, + Distributions.MvNormal(zero(pts[1]), 1.0 * I), + ), + ], + tvector_distributions=[ + Manifolds.normal_tvector_distribution(M, pts[1], 1.0 * I), + ], + basis_types_vecs=basis_types, + basis_types_to_from=basis_types, + basis_has_specialized_diagonalizing_get=true, + test_vee_hat=isa(M, Euclidean), + test_inplace=true, + test_rand_point=M === E, + test_rand_tvector=M === E, + ) + end + end end - for T in types - @testset "$M Type $T" begin + for T in types_complex + @testset "Complex Euclidean, type $T" begin pts = [ - convert(T, [1.0, 0.0, 0.0]), - convert(T, [0.0, 1.0, 0.0]), + convert(T, [1.0im, -1.0im, 1.0]), + convert(T, [0.0, 1.0, 1.0im]), convert(T, [0.0, 0.0, 1.0]), ] test_manifold( - M, + Ec, pts, - test_project_point=true, test_project_tangent=true, test_musical_isomorphisms=true, test_default_vector_transport=true, - vector_transport_methods=[ - ParallelTransport(), - SchildsLadderTransport(), - PoleLadderTransport(), - ], - test_mutating_rand=isa(T, Vector), - point_distributions=[ - Manifolds.projected_distribution( - M, - Distributions.MvNormal(zero(pts[1]), 1.0 * I), - ), - ], - tvector_distributions=[ - Manifolds.normal_tvector_distribution(M, pts[1], 1.0 * I), - ], - basis_types_vecs=basis_types, - basis_types_to_from=basis_types, - basis_has_specialized_diagonalizing_get=true, - test_vee_hat=isa(M, Euclidean), - test_inplace=true, - test_rand_point=M === E, - test_rand_tvector=M === E, + test_vee_hat=false, + parallel_transport=true, ) end end end - for T in types_complex - @testset "Complex Euclidean, type $T" begin - pts = [ - convert(T, [1.0im, -1.0im, 1.0]), - convert(T, [0.0, 1.0, 1.0im]), - convert(T, [0.0, 0.0, 1.0]), - ] - test_manifold( - Ec, - pts, - test_project_tangent=true, - test_musical_isomorphisms=true, - test_default_vector_transport=true, - test_vee_hat=false, - parallel_transport=true, - ) - end - end + E = Euclidean(3) + Ec = Euclidean(3; field=ℂ) number_types = [Float64, ComplexF64] TEST_FLOAT32 && push!(number_types, Float32) @@ -306,7 +319,7 @@ using Manifolds: induced_basis end @testset "StaticArrays specializations" begin - M1 = Euclidean(3) + M1 = Euclidean(3; parameter=:type) @test get_vector( M1, SA[1.0, 2.0, 3.0], @@ -317,7 +330,7 @@ using Manifolds: induced_basis c_sv = SizedVector{3}([-1.0, -2.0, -3.0]) @test get_vector(M1, SA[1.0, 2.0, 3.0], c_sv, DefaultOrthonormalBasis()) === c_sv - M2 = Euclidean(2, 2) + M2 = Euclidean(2, 2; parameter=:type) @test get_vector( M2, SA[1.0 2.0; 3.0 4.0], diff --git a/test/manifolds/flag.jl b/test/manifolds/flag.jl index 53e4f6c09d..6df6278aa8 100644 --- a/test/manifolds/flag.jl +++ b/test/manifolds/flag.jl @@ -267,6 +267,6 @@ using Random @test isapprox(Y_tmp, X1_ortho.value) @test retract(M, p1_ortho, X1_ortho, QRRetraction()).value ≈ - retract(Orthogonal(5), p1_ortho.value, X1_ortho.value, QRRetraction()) + retract(OrthogonalMatrices(5), p1_ortho.value, X1_ortho.value, QRRetraction()) end end diff --git a/test/manifolds/hyperbolic.jl b/test/manifolds/hyperbolic.jl index 638daa683f..9787c72e30 100644 --- a/test/manifolds/hyperbolic.jl +++ b/test/manifolds/hyperbolic.jl @@ -7,7 +7,7 @@ include("../utils.jl") @test base_manifold(M) == M @test manifold_dimension(M) == 2 @test typeof(get_embedding(M)) == - MetricManifold{ℝ,Euclidean{Tuple{3},ℝ},MinkowskiMetric} + MetricManifold{ℝ,Euclidean{Tuple{Int},ℝ},MinkowskiMetric} @test representation_size(M) == (3,) @test !is_flat(M) @test isinf(injectivity_radius(M)) diff --git a/test/manifolds/oblique.jl b/test/manifolds/oblique.jl index cfd46da61b..2c5e065fe9 100644 --- a/test/manifolds/oblique.jl +++ b/test/manifolds/oblique.jl @@ -6,8 +6,10 @@ include("../utils.jl") @test Sphere(2)^3 === Oblique(3, 3) @test Sphere(2)^(3,) === PowerManifold(Sphere(2), 3) @test ^(Sphere(2), 2) === Oblique(3, 2) - @test typeof(^(Sphere(2), 2)) == Oblique{3,2,ℝ,2} - @test repr(M) == "Oblique(3,2; field = ℝ)" + @test typeof(^(Sphere(2), 2)) == Oblique{Tuple{Int64,Int64},ℝ,Tuple{Int64}} + @test typeof(^(Sphere(2; parameter=:type), 2)) == + Oblique{TypeParameter{Tuple{3,2}},ℝ,TypeParameter{Tuple{2}}} + @test repr(M) == "Oblique(3, 2; field = ℝ)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 4 @test !is_flat(M) diff --git a/test/manifolds/projective_space.jl b/test/manifolds/projective_space.jl index ad381a7839..62bb3a4d20 100644 --- a/test/manifolds/projective_space.jl +++ b/test/manifolds/projective_space.jl @@ -290,7 +290,7 @@ include("../utils.jl") M = ArrayProjectiveSpace(2, 2; field=ℝ) @test manifold_dimension(M) == 3 @test repr(M) == "ArrayProjectiveSpace(2, 2; field = ℝ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{2,2},ℝ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} @test representation_size(M) == (2, 2) p = ones(2, 2) q = project(M, p) @@ -302,7 +302,7 @@ include("../utils.jl") M = ArrayProjectiveSpace(2, 2; field=ℂ) @test manifold_dimension(M) == 6 @test repr(M) == "ArrayProjectiveSpace(2, 2; field = ℂ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{2,2},ℂ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℂ} @test representation_size(M) == (2, 2) end diff --git a/test/manifolds/rotations.jl b/test/manifolds/rotations.jl index 25c0f62264..9bb7d54cc4 100644 --- a/test/manifolds/rotations.jl +++ b/test/manifolds/rotations.jl @@ -1,7 +1,7 @@ include("../utils.jl") @testset "Rotations" begin - M = Manifolds.Rotations(2) + M = Rotations(2) @test repr(M) == "Rotations(2)" @test representation_size(M) == (2, 2) @test is_flat(M) @@ -24,18 +24,18 @@ include("../utils.jl") basis_types = (DefaultOrthonormalBasis(), ProjectedOrthonormalBasis(:svd)) @testset "vee/hat" begin - M = Manifolds.Rotations(2) + M = Rotations(2) Xf = [1.23] p = Matrix{Float64}(I, 2, 2) - X = Manifolds.hat(M, p, Xf) + X = hat(M, p, Xf) @test isa(X, AbstractMatrix) @test norm(M, p, X) / sqrt(2) ≈ norm(Xf) - @test Manifolds.vee(M, p, X) == Xf + @test vee(M, p, X) == Xf X = project(M, p, randn(2, 2)) - Xf = Manifolds.vee(M, p, X) + Xf = vee(M, p, X) @test isa(Xf, AbstractVector) - @test Manifolds.hat(M, p, Xf) == X + @test hat(M, p, Xf) == X end for T in types @@ -107,7 +107,7 @@ include("../utils.jl") Random.seed!(42) for n in (3, 4, 5) @testset "Rotations: SO($n)" begin - SOn = Manifolds.Rotations(n) + SOn = Rotations(n) @test !is_flat(SOn) ptd = Manifolds.normal_rotation_distribution(SOn, Matrix(1.0I, n, n), 1.0) tvd = Manifolds.normal_tvector_distribution(SOn, Matrix(1.0I, n, n), 1.0) @@ -164,7 +164,7 @@ include("../utils.jl") ) p = exp(SOn, pts[1], X) X2 = log(SOn, pts[1], p) - @test p ≈ exp(SOn, pts[1], X2) + @test distance(SOn, p, exp(SOn, pts[1], X2)) < 20 * eps() end end @testset "Test AbstractManifold Point and Tangent Vector checks" begin diff --git a/test/manifolds/skewhermitian.jl b/test/manifolds/skewhermitian.jl index 76369454ad..8c26531ed7 100644 --- a/test/manifolds/skewhermitian.jl +++ b/test/manifolds/skewhermitian.jl @@ -2,7 +2,6 @@ include("../utils.jl") @testset "SkewSymmetricMatrices" begin @test SkewSymmetricMatrices(3) === SkewHermitianMatrices(3) - @test SkewSymmetricMatrices(3, ℂ) === SkewHermitianMatrices(3, ℂ) end @testset "SkewHermitianMatrices" begin @@ -23,7 +22,7 @@ end @test representation_size(M) == (3, 3) @test base_manifold(M) === M @test is_flat(M) - @test typeof(get_embedding(M)) === Euclidean{Tuple{3,3},ℝ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} @test check_point(M, B_skewsym) === nothing @test_throws DomainError is_point(M, A, true) @test_throws ManifoldDomainError is_point(M, C, true) diff --git a/test/manifolds/sphere.jl b/test/manifolds/sphere.jl index 41fac3e55d..4ccefd0962 100644 --- a/test/manifolds/sphere.jl +++ b/test/manifolds/sphere.jl @@ -7,7 +7,7 @@ using ManifoldsBase: TFVector M = Sphere(2) @testset "Sphere Basics" begin @test repr(M) == "Sphere(2, ℝ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{3},ℝ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int},ℝ} @test representation_size(M) == (3,) @test !is_flat(M) @test is_flat(Sphere(1)) @@ -139,7 +139,7 @@ using ManifoldsBase: TFVector @testset "Complex Sphere" begin M = Sphere(2, ℂ) @test repr(M) == "Sphere(2, ℂ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{3},ℂ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int},ℂ} @test representation_size(M) == (3,) p = [1.0, 1.0im, 1.0] q = project(M, p) @@ -156,7 +156,7 @@ using ManifoldsBase: TFVector @testset "Quaternion Sphere" begin M = Sphere(2, ℍ) @test repr(M) == "Sphere(2, ℍ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{3},ℍ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int},ℍ} @test representation_size(M) == (3,) p = [Quaternion(1.0), Quaternion(0, 1.0, 0, 0), Quaternion(0.0, 0.0, -1.0, 0.0)] q = project(M, p) @@ -169,7 +169,7 @@ using ManifoldsBase: TFVector @testset "Array Sphere" begin M = ArraySphere(2, 2; field=ℝ) @test repr(M) == "ArraySphere(2, 2; field = ℝ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{2,2},ℝ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} @test representation_size(M) == (2, 2) p = ones(2, 2) q = project(M, p) @@ -180,7 +180,7 @@ using ManifoldsBase: TFVector M = ArraySphere(2, 2; field=ℂ) @test repr(M) == "ArraySphere(2, 2; field = ℂ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{2,2},ℂ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℂ} @test representation_size(M) == (2, 2) end diff --git a/test/manifolds/symmetric.jl b/test/manifolds/symmetric.jl index f0921080b1..c943000ca6 100644 --- a/test/manifolds/symmetric.jl +++ b/test/manifolds/symmetric.jl @@ -17,7 +17,7 @@ include("../utils.jl") @test representation_size(M) == (3, 3) @test base_manifold(M) === M @test is_flat(M) - @test typeof(get_embedding(M)) === Euclidean{Tuple{3,3},ℝ} + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} @test check_point(M, B_sym) === nothing @test_throws DomainError is_point(M, A, true) @test_throws ManifoldDomainError is_point(M, C, true) diff --git a/test/utils.jl b/test/utils.jl index fb1efc5d2c..6e53f06d56 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -5,7 +5,7 @@ TEST_GROUP = get(ENV, "MANIFOLDS_TEST_GROUP", "all") using Manifolds using ManifoldsBase -using ManifoldsBase: number_of_coordinates +using ManifoldsBase: number_of_coordinates, TypeParameter import ManifoldsBase: active_traits, merge_traits using ManifoldDiff From 54799cb01fb744caaaaca2a317b02c921041a2b1 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Fri, 18 Aug 2023 19:36:09 +0200 Subject: [PATCH 10/81] optionally static sizes: part 2 --- src/groups/rotation_action.jl | 83 ++++++--------------- src/groups/special_euclidean.jl | 14 +++- src/groups/special_orthogonal.jl | 2 +- src/groups/translation_group.jl | 12 +-- src/manifolds/Euclidean.jl | 24 +++--- src/manifolds/GeneralUnitaryMatrices.jl | 2 +- src/manifolds/Symmetric.jl | 6 +- test/groups/special_euclidean.jl | 7 +- test/manifolds/sphere_symmetric_matrices.jl | 2 +- test/manifolds/unitary_matrices.jl | 10 +-- 10 files changed, 70 insertions(+), 92 deletions(-) diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index 8b13a72bc5..1bd866c6d7 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -26,11 +26,11 @@ function Base.show(io::IO, A::RotationAction) return print(io, "RotationAction($(A.manifold), $(A.SOn), $(direction(A)))") end -const RotationActionOnVector{N,F,TAD} = RotationAction{ - <:Union{Euclidean{Tuple{N},F},TranslationGroup{Tuple{N},F}}, - SpecialOrthogonal{N}, +const RotationActionOnVector{TAD,𝔽,TE,TSO} = RotationAction{ + Euclidean{TE,𝔽}, + SpecialOrthogonal{TSO}, TAD, -} where {TAD<:ActionDirection} +} where {TAD<:ActionDirection,𝔽,TE,TSO} base_group(A::RotationAction) = A.SOn @@ -43,92 +43,55 @@ function switch_direction( return RotationAction(A.manifold, A.SOn, switch_direction(TAD(), LeftRightSwitch())) end -apply(::RotationActionOnVector{N,F,LeftForwardAction}, a, p) where {N,F} = a * p -function apply(A::RotationActionOnVector{N,F,RightForwardAction}, a, p) where {N,F} +apply(::RotationActionOnVector{LeftForwardAction}, a, p) = a * p +function apply(A::RotationActionOnVector{RightForwardAction}, a, p) return inv(base_group(A), a) * p end -apply!(::RotationActionOnVector{N,F,LeftForwardAction}, q, a, p) where {N,F} = mul!(q, a, p) +apply!(::RotationActionOnVector{LeftForwardAction}, q, a, p) = mul!(q, a, p) -function inverse_apply(A::RotationActionOnVector{N,F,LeftForwardAction}, a, p) where {N,F} +function inverse_apply(A::RotationActionOnVector{LeftForwardAction}, a, p) return inv(base_group(A), a) * p end -inverse_apply(::RotationActionOnVector{N,F,RightForwardAction}, a, p) where {N,F} = a * p +inverse_apply(::RotationActionOnVector{RightForwardAction}, a, p) = a * p -apply_diff(::RotationActionOnVector{N,F,LeftForwardAction}, a, p, X) where {N,F} = a * X +apply_diff(::RotationActionOnVector{LeftForwardAction}, a, p, X) = a * X function apply_diff( - ::RotationActionOnVector{N,F,LeftForwardAction}, + ::RotationActionOnVector{LeftForwardAction}, ::Identity{MultiplicationOperation}, p, X, -) where {N,F} +) return X end -function apply_diff(A::RotationActionOnVector{N,F,RightForwardAction}, a, p, X) where {N,F} +function apply_diff(A::RotationActionOnVector{RightForwardAction}, a, p, X) return inv(base_group(A), a) * X end -function apply_diff!( - ::RotationActionOnVector{N,F,LeftForwardAction}, - Y, - a, - p, - X, -) where {N,F} +function apply_diff!(::RotationActionOnVector{LeftForwardAction}, Y, a, p, X) return mul!(Y, a, X) end -function apply_diff!( - A::RotationActionOnVector{N,F,RightForwardAction}, - Y, - a, - p, - X, -) where {N,F} +function apply_diff!(A::RotationActionOnVector{RightForwardAction}, Y, a, p, X) return mul!(Y, inv(base_group(A), a), X) end -function apply_diff_group( - ::RotationActionOnVector{N,F,LeftForwardAction}, - ::Identity, - X, - p, -) where {N,F} +function apply_diff_group(::RotationActionOnVector{LeftForwardAction}, ::Identity, X, p) return X * p end -function apply_diff_group!( - ::RotationActionOnVector{N,F,LeftForwardAction}, - Y, - ::Identity, - X, - p, -) where {N,F} +function apply_diff_group!(::RotationActionOnVector{LeftForwardAction}, Y, ::Identity, X, p) Y .= X * p return Y end -function inverse_apply_diff( - A::RotationActionOnVector{N,F,LeftForwardAction}, - a, - p, - X, -) where {N,F} +function inverse_apply_diff(A::RotationActionOnVector{LeftForwardAction}, a, p, X) return inv(base_group(A), a) * X end -function inverse_apply_diff( - A::RotationActionOnVector{N,F,RightForwardAction}, - a, - p, - X, -) where {N,F} +function inverse_apply_diff(A::RotationActionOnVector{RightForwardAction}, a, p, X) return a * X end -function optimal_alignment( - ::RotationActionOnVector{N,T,LeftForwardAction}, - p, - q, -) where {N,T} +function optimal_alignment(::RotationActionOnVector{LeftForwardAction}, p, q) Xmul = p * transpose(q) F = svd(Xmul) L = size(Xmul)[2] @@ -136,11 +99,7 @@ function optimal_alignment( Ostar = det(UVt) ≥ 0 ? UVt : F.U * Diagonal([i < L ? 1 : -1 for i in 1:L]) * F.Vt return convert(typeof(Xmul), Ostar) end -function optimal_alignment( - A::RotationActionOnVector{N,T,RightForwardAction}, - p, - q, -) where {N,T} +function optimal_alignment(A::RotationActionOnVector{RightForwardAction}, p, q) return optimal_alignment(switch_direction(A), q, p) end diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index ffc2ce0f4a..70576327fa 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -47,13 +47,19 @@ const SpecialEuclideanOperation{N} = SemidirectProductOperation{ } const SpecialEuclideanIdentity{N} = Identity{SpecialEuclideanOperation{N}} -Base.show(io::IO, ::SpecialEuclidean{n}) where {n} = print(io, "SpecialEuclidean($(n))") +function Base.show(io::IO, ::SpecialEuclidean{TypeParameter{Tuple{n}}}) where {n} + return print(io, "SpecialEuclidean($(n); field=:type)") +end +function Base.show(io::IO, G::SpecialEuclidean{Tuple{Int}}) + n = get_n(G) + return print(io, "SpecialEuclidean($(n))") +end @inline function active_traits(f, M::SpecialEuclidean, args...) return merge_traits(IsGroupManifold(M.op), IsExplicitDecorator()) end -get_n(::SpecialEuclidean{N}) where {N} = N +get_n(::SpecialEuclidean{TypeParameter{Tuple{N}}}) where {N} = N get_n(M::SpecialEuclidean{Tuple{Int}}) = manifold_dimension(M.manifold.manifolds[1]) Base.@propagate_inbounds function Base.getindex( @@ -280,11 +286,11 @@ function screw_matrix(G::SpecialEuclidean, X) end screw_matrix(::SpecialEuclidean, X::AbstractMatrix) = X -function allocate_result(::SpecialEuclidean, ::typeof(affine_matrix), p...) +function allocate_result(G::SpecialEuclidean, ::typeof(affine_matrix), p...) n = get_n(G) return allocate(p[1], Size(n + 1, n + 1)) end -function allocate_result(::SpecialEuclidean, ::typeof(screw_matrix), X...) +function allocate_result(G::SpecialEuclidean, ::typeof(screw_matrix), X...) n = get_n(G) return allocate(X[1], Size(n + 1, n + 1)) end diff --git a/src/groups/special_orthogonal.jl b/src/groups/special_orthogonal.jl index 6f461c6352..ee68f1f826 100644 --- a/src/groups/special_orthogonal.jl +++ b/src/groups/special_orthogonal.jl @@ -18,7 +18,7 @@ Base.inv(::SpecialOrthogonal, e::Identity{MultiplicationOperation}) = e function Base.show(io::IO, ::SpecialOrthogonal{TypeParameter{Tuple{n}}}) where {n} return print(io, "SpecialOrthogonal($(n); parameter=:type)") end -function Base.show(io::IO, M::SpecialOrthogonal{Type{Int}}) +function Base.show(io::IO, M::SpecialOrthogonal{Tuple{Int}}) n = get_n(M) return print(io, "SpecialOrthogonal($(n))") end diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index d28d3ecf55..401ab27fde 100644 --- a/src/groups/translation_group.jl +++ b/src/groups/translation_group.jl @@ -11,7 +11,7 @@ $𝔽^{n₁,…,nᵢ}$ = `Euclidean(n₁,...,nᵢ; field = 𝔽)`, which is isom """ const TranslationGroup{T,𝔽} = GroupManifold{𝔽,Euclidean{T,𝔽},AdditionOperation} -function TranslationGroup(n::Int...; field::AbstractNumbers=ℝ, parameter::Symbol=:type) +function TranslationGroup(n::Int...; field::AbstractNumbers=ℝ, parameter::Symbol=:field) size = wrap_type_parameter(parameter, n) return TranslationGroup{typeof(size),field}( Euclidean(n...; field=field, parameter=parameter), @@ -47,12 +47,12 @@ end function Base.show(io::IO, M::TranslationGroup{N,𝔽}) where {N<:Tuple,𝔽} size = get_parameter(M.manifold.size) - return print( - io, - "TranslationGroup($(join(size, ", ")); field = $(𝔽), parameter = :field)", - ) + return print(io, "TranslationGroup($(join(size, ", ")); field = $(𝔽))") end function Base.show(io::IO, M::TranslationGroup{N,𝔽}) where {N<:TypeParameter,𝔽} size = get_parameter(M.manifold.size) - return print(io, "TranslationGroup($(join(size, ", ")); field = $(𝔽))") + return print( + io, + "TranslationGroup($(join(size, ", ")); field = $(𝔽), parameter = :type)", + ) end diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 0c75ba672e..7e4254171d 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -498,14 +498,14 @@ end manifold_dimension(::Euclidean{TypeParameter{Tuple{}},𝔽}) where {𝔽} = real_dimension(𝔽) function Statistics.mean( - ::Euclidean{TypeParameter{Tuple{}}}, + ::Union{Euclidean{TypeParameter{Tuple{}}},Euclidean{Tuple{}}}, x::AbstractVector{<:Number}; kwargs..., ) return mean(x) end function Statistics.mean( - ::Euclidean{TypeParameter{Tuple{}}}, + ::Union{Euclidean{TypeParameter{Tuple{}}},Euclidean{Tuple{}}}, x::AbstractVector{<:Number}, w::AbstractWeights; kwargs..., @@ -515,7 +515,7 @@ end Statistics.mean(::Euclidean, x::AbstractVector; kwargs...) = mean(x) function StatsBase.mean_and_var( - ::Euclidean{TypeParameter{Tuple{}}}, + ::Union{Euclidean{TypeParameter{Tuple{}}},Euclidean{Tuple{}}}, x::AbstractVector{<:Number}; kwargs..., ) @@ -523,7 +523,7 @@ function StatsBase.mean_and_var( return m, sum(v) end function StatsBase.mean_and_var( - ::Euclidean{TypeParameter{Tuple{}}}, + ::Union{Euclidean{TypeParameter{Tuple{}}},Euclidean{Tuple{}}}, x::AbstractVector{<:Number}, w::AbstractWeights; corrected=false, @@ -534,14 +534,14 @@ function StatsBase.mean_and_var( end function Statistics.median( - ::Euclidean{TypeParameter{Tuple{}}}, + ::Union{Euclidean{TypeParameter{Tuple{}}},Euclidean{Tuple{}}}, x::AbstractVector{<:Number}; kwargs..., ) return median(x) end function Statistics.median( - ::Euclidean{TypeParameter{Tuple{}}}, + ::Union{Euclidean{TypeParameter{Tuple{}}},Euclidean{Tuple{}}}, x::AbstractVector{<:Number}, w::AbstractWeights; kwargs..., @@ -550,7 +550,13 @@ function Statistics.median( end mid_point(::Euclidean, p1, p2) = (p1 .+ p2) ./ 2 -mid_point(::Euclidean{TypeParameter{Tuple{}}}, p1::Number, p2::Number) = (p1 + p2) / 2 +function mid_point( + ::Union{Euclidean{TypeParameter{Tuple{}}},Euclidean{Tuple{}}}, + p1::Number, + p2::Number, +) + return (p1 + p2) / 2 +end function mid_point!(::Euclidean, q, p1, p2) q .= (p1 .+ p2) ./ 2 @@ -686,11 +692,11 @@ end function Base.show(io::IO, M::Euclidean{N,𝔽}) where {N<:Tuple,𝔽} size = get_parameter(M.size) - return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽), parameter = :field)") + return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽))") end function Base.show(io::IO, M::Euclidean{N,𝔽}) where {N<:TypeParameter,𝔽} size = get_parameter(M.size) - return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽))") + return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽), parameter = :type)") end # # Vector Transport diff --git a/src/manifolds/GeneralUnitaryMatrices.jl b/src/manifolds/GeneralUnitaryMatrices.jl index 9addd5d433..d56b2f040b 100644 --- a/src/manifolds/GeneralUnitaryMatrices.jl +++ b/src/manifolds/GeneralUnitaryMatrices.jl @@ -668,7 +668,7 @@ function _injectivity_radius(M::GeneralUnitaryMatrices{<:Any,ℝ}, ::Exponential n = get_n(M) return n == 1 ? 0.0 : π * sqrt(2.0) end -function _injectivity_radius(::GeneralUnitaryMatrices{<:Any,ℝ}, ::PolarRetraction) +function _injectivity_radius(M::GeneralUnitaryMatrices{<:Any,ℝ}, ::PolarRetraction) n = get_n(M) return n == 1 ? 0.0 : π / sqrt(2.0) end diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 5710c82772..cc4de5c773 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -229,6 +229,10 @@ project(::SymmetricMatrices, ::Any, ::Any) project!(M::SymmetricMatrices, Y, p, X) = (Y .= (X .+ transpose(X)) ./ 2) -function Base.show(io::IO, ::SymmetricMatrices{n,F}) where {n,F} +function Base.show(io::IO, ::SymmetricMatrices{TypeParameter{Tuple{n}},F}) where {n,F} + return print(io, "SymmetricMatrices($(n), $(F); parameter=:type)") +end +function Base.show(io::IO, M::SymmetricMatrices{Tuple{Int},F}) where {F} + n = get_n(M) return print(io, "SymmetricMatrices($(n), $(F))") end diff --git a/test/groups/special_euclidean.jl b/test/groups/special_euclidean.jl index 6c3545f0ce..db3c36e68e 100644 --- a/test/groups/special_euclidean.jl +++ b/test/groups/special_euclidean.jl @@ -9,7 +9,12 @@ Random.seed!(10) for se_parameter in [:field, :type] @testset "SpecialEuclidean($n)" for n in (2, 3, 4) G = SpecialEuclidean(n; parameter=se_parameter) - @test isa(G, SpecialEuclidean{n}) + if se_parameter === :field + @test isa(G, SpecialEuclidean{Tuple{Int}}) + else + @test isa(G, SpecialEuclidean{TypeParameter{Tuple{n}}}) + end + @test repr(G) == "SpecialEuclidean($n)" M = base_manifold(G) @test M === TranslationGroup(n) × SpecialOrthogonal(n) diff --git a/test/manifolds/sphere_symmetric_matrices.jl b/test/manifolds/sphere_symmetric_matrices.jl index 9ddada770b..9710acd478 100644 --- a/test/manifolds/sphere_symmetric_matrices.jl +++ b/test/manifolds/sphere_symmetric_matrices.jl @@ -15,7 +15,7 @@ include("../utils.jl") @test representation_size(M) == (3, 3) @test base_manifold(M) === M @test !is_flat(M) - @test typeof(get_embedding(M)) === ArraySphere{Tuple{3,3},ℝ} + @test typeof(get_embedding(M)) === ArraySphere{Tuple{Int,Int},ℝ} @test check_point(M, A) === nothing @test_throws ManifoldDomainError is_point(M, B, true) @test_throws ManifoldDomainError is_point(M, C, true) diff --git a/test/manifolds/unitary_matrices.jl b/test/manifolds/unitary_matrices.jl index 05888dfd26..a4245bcce6 100644 --- a/test/manifolds/unitary_matrices.jl +++ b/test/manifolds/unitary_matrices.jl @@ -57,14 +57,14 @@ end end @testset "Special unitary matrices" begin - M = Manifolds.GeneralUnitaryMatrices{2,ℂ,Manifolds.DeterminantOneMatrices}() + M = SpecialUnitary(2) @test manifold_dimension(M) == 3 @test injectivity_radius(M) ≈ π * sqrt(2.0) end @testset "Quaternionic Unitary Matrices" begin - M = UnitaryMatrices(1, ℍ) - @test repr(M) == "UnitaryMatrices(1, ℍ)" + M = UnitaryMatrices(1, ℍ; parameter=:type) + @test repr(M) == "UnitaryMatrices(1, ℍ; parameter=:type)" @test manifold_dimension(M) == 3 @test injectivity_radius(M) == π @test !is_flat(M) @@ -124,7 +124,5 @@ end end @testset "Flatness edge cases" begin - @test is_flat( - Manifolds.GeneralUnitaryMatrices{1,ℂ,Manifolds.AbsoluteDeterminantOneMatrices}(), - ) + @test is_flat(SpecialUnitary(1)) end From 0f8e431ff29d04aca2407d5d6a2a9976c36294fe Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 3 Sep 2023 17:02:30 +0200 Subject: [PATCH 11/81] a few fixes --- src/manifolds/EssentialManifold.jl | 11 +++++++---- src/manifolds/GeneralUnitaryMatrices.jl | 26 ++++++++++++------------- test/manifolds/essential_manifold.jl | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/manifolds/EssentialManifold.jl b/src/manifolds/EssentialManifold.jl index a68d8dea8e..8d46f2ffca 100644 --- a/src/manifolds/EssentialManifold.jl +++ b/src/manifolds/EssentialManifold.jl @@ -49,12 +49,15 @@ Generate the manifold of essential matrices, either the signed (`is_signed=true` unsigned (`is_signed=false`) variant. """ -struct EssentialManifold <: AbstractPowerManifold{ℝ,Rotations{3},NestedPowerRepresentation} +struct EssentialManifold <: + AbstractPowerManifold{ℝ,Rotations{TypeParameter{Tuple{3}}},NestedPowerRepresentation} is_signed::Bool - manifold::Rotations{3} + manifold::Rotations{TypeParameter{Tuple{3}}} end -EssentialManifold(is_signed::Bool=true) = EssentialManifold(is_signed, Rotations(3)) +function EssentialManifold(is_signed::Bool=true) + return EssentialManifold(is_signed, Rotations(3; parameter=:type)) +end @doc raw""" check_point(M::EssentialManifold, p; kwargs...) @@ -482,6 +485,6 @@ pose of camera $i$ $g_i = (R_i,T'_i) ∈ \text{SE}(3)$ and $R_0 ∈ \text{SO}(3) function vert_proj(M::EssentialManifold, p, X) return sum(vert_proj.(Ref(M.manifold), p, X)) end -function vert_proj(M::Rotations{3}, p, X) +function vert_proj(M::Rotations{TypeParameter{Tuple{3}}}, p, X) return (p[3, :]' * get_coordinates(M, p, X, DefaultOrthogonalBasis())) end diff --git a/src/manifolds/GeneralUnitaryMatrices.jl b/src/manifolds/GeneralUnitaryMatrices.jl index c42293f13b..72db9235d9 100644 --- a/src/manifolds/GeneralUnitaryMatrices.jl +++ b/src/manifolds/GeneralUnitaryMatrices.jl @@ -801,7 +801,7 @@ function manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMa end @doc raw""" - manifold_volume(::GeneralUnitaryMatrices{n,ℝ,AbsoluteDeterminantOneMatrices}) where {n} + manifold_volume(::GeneralUnitaryMatrices{<:Any,ℝ,AbsoluteDeterminantOneMatrices}) Volume of the manifold of real orthogonal matrices of absolute determinant one. The formula reads [BoyaSudarshanTilma:2003](@cite): @@ -813,13 +813,12 @@ formula reads [BoyaSudarshanTilma:2003](@cite): \end{cases} ``` """ -function manifold_volume( - ::GeneralUnitaryMatrices{n,ℝ,AbsoluteDeterminantOneMatrices}, -) where {n} - return 2 * manifold_volume(GeneralUnitaryMatrices{n,ℝ,DeterminantOneMatrices}()) +function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℝ,AbsoluteDeterminantOneMatrices}) + n = get_n(M) + return 2 * manifold_volume(GeneralUnitaryMatrices(n, ℝ, DeterminantOneMatrices)) end @doc raw""" - manifold_volume(::GeneralUnitaryMatrices{n,ℝ,DeterminantOneMatrices}) where {n} + manifold_volume(::GeneralUnitaryMatrices{<:Any,ℝ,DeterminantOneMatrices}) Volume of the manifold of real orthogonal matrices of determinant one. The formula reads [BoyaSudarshanTilma:2003](@cite): @@ -835,7 +834,8 @@ formula reads [BoyaSudarshanTilma:2003](@cite): It differs from the paper by a factor of `sqrt(2)` due to a different choice of normalization. """ -function manifold_volume(::GeneralUnitaryMatrices{n,ℝ,DeterminantOneMatrices}) where {n} +function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℝ,DeterminantOneMatrices}) + n = get_n(M) vol = 1.0 if n % 2 == 0 k = div(n, 2) @@ -856,7 +856,7 @@ function manifold_volume(::GeneralUnitaryMatrices{n,ℝ,DeterminantOneMatrices}) return vol end @doc raw""" - manifold_volume(::GeneralUnitaryMatrices{n,ℂ,AbsoluteDeterminantOneMatrices}) where {n} + manifold_volume(::GeneralUnitaryMatrices{<:Any,ℂ,AbsoluteDeterminantOneMatrices}) Volume of the manifold of complex general unitary matrices of absolute determinant one. The formula reads [BoyaSudarshanTilma:2003](@cite) @@ -865,9 +865,8 @@ formula reads [BoyaSudarshanTilma:2003](@cite) \sqrt{n 2^{n+1}} π^{n(n+1)/2} \prod_{k=1}^{n-1}\frac{1}{k!} ``` """ -function manifold_volume( - ::GeneralUnitaryMatrices{n,ℂ,AbsoluteDeterminantOneMatrices}, -) where {n} +function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℂ,AbsoluteDeterminantOneMatrices}) + n = get_n(M) vol = sqrt(n * 2^(n + 1)) * π^(((n + 1) * n) // 2) kf = 1 for k in 1:(n - 1) @@ -877,7 +876,7 @@ function manifold_volume( return vol end @doc raw""" - manifold_volume(::GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrices}) where {n} + manifold_volume(::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices}) Volume of the manifold of complex general unitary matrices of determinant one. The formula reads [BoyaSudarshanTilma:2003](@cite) @@ -886,7 +885,8 @@ reads [BoyaSudarshanTilma:2003](@cite) \sqrt{n 2^{n-1}} π^{(n-1)(n+2)/2} \prod_{k=1}^{n-1}\frac{1}{k!} ``` """ -function manifold_volume(::GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrices}) where {n} +function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices}) + n = get_n(M) vol = sqrt(n * 2^(n - 1)) * π^(((n - 1) * (n + 2)) // 2) kf = 1 for k in 1:(n - 1) diff --git a/test/manifolds/essential_manifold.jl b/test/manifolds/essential_manifold.jl index a466998f82..da9265085f 100644 --- a/test/manifolds/essential_manifold.jl +++ b/test/manifolds/essential_manifold.jl @@ -13,7 +13,7 @@ include("../utils.jl") p2 = [r1, r3] p3 = [r2, r2] @testset "Essential manifold Basics" begin - @test M.manifold == Rotations(3) + @test M.manifold == Rotations(3; parameter=:type) @test repr(M) == "EssentialManifold(true)" @test manifold_dimension(M) == 5 @test !is_flat(M) From f8961e7d626473e9ec153ff6ee07ca2de1c4dc2e Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 5 Sep 2023 21:38:01 +0200 Subject: [PATCH 12/81] bugfixing --- NEWS.md | 37 +- src/groups/group_action.jl | 30 +- src/groups/rotation_action.jl | 2 +- src/groups/special_euclidean.jl | 30 +- src/manifolds/GeneralUnitaryMatrices.jl | 2 +- test/groups/groups_general.jl | 24 +- test/groups/special_euclidean.jl | 538 ++++++++++++------------ test/manifolds/euclidean.jl | 13 +- 8 files changed, 353 insertions(+), 323 deletions(-) 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 From b3995393379399ac7fc502098ec3d8d7e0f5978a Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 6 Sep 2023 14:02:16 +0200 Subject: [PATCH 13/81] Stiefel, SPD, minor fixes --- NEWS.md | 31 ++++- src/manifolds/CholeskySpace.jl | 34 +++-- src/manifolds/Grassmann.jl | 2 +- src/manifolds/GrassmannStiefel.jl | 2 +- src/manifolds/Rotations.jl | 3 +- src/manifolds/Stiefel.jl | 119 +++++++++++++----- src/manifolds/StiefelCanonicalMetric.jl | 61 +++++---- src/manifolds/StiefelEuclideanMetric.jl | 30 ++--- src/manifolds/StiefelSubmersionMetric.jl | 79 ++++++------ src/manifolds/SymmetricPositiveDefinite.jl | 45 ++++--- ...ymmetricPositiveDefiniteAffineInvariant.jl | 54 ++++---- ...mmetricPositiveDefiniteBuresWasserstein.jl | 8 +- ...tiveDefiniteGeneralizedBuresWasserstein.jl | 24 ++-- .../SymmetricPositiveDefiniteLogCholesky.jl | 58 ++++----- .../SymmetricPositiveDefiniteLogEuclidean.jl | 8 +- test/groups/special_orthogonal.jl | 6 +- test/groups/translation_group.jl | 4 +- test/manifolds/stiefel.jl | 8 +- 18 files changed, 325 insertions(+), 251 deletions(-) diff --git a/NEWS.md b/NEWS.md index a2829afd23..e7ba48b346 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,7 +18,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - 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: - - 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 + - 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: + - `CholeskySpace{N}`, + - `Euclidean`, + - `GeneralUnitaryMultiplicationGroup{n}`, + - `Grassmann{n,k}`, + - `Orthogonal{n}`, + - `SpecialOrthogonal{n}`, + - `SpecialUnitary{n}`, + - `SpecialEuclideanManifold{n}`, + - `Stiefel{n,k}`, + - `SymmetricPositiveDefinite{n}`, + - `TranslationGroup`. + + For example ```{julia} function Base.show(io::IO, ::SpecialEuclidean{n}) where {n} @@ -43,10 +56,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 end ``` - for groups with size stored in field. + for groups with size stored in field. Alternatively, you can use a single generic method like this: + + ```{julia} + function Base.show(io::IO, G::SpecialEuclidean{T}) where {T} + n = get_n(G) + if T <: TypeParameter + return print(io, "SpecialEuclidean($(n); parameter=:type)") + else + return print(io, "SpecialEuclidean($(n))") + end + end + ``` + - 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 +- Default methods throwing "not implemented" `ErrorException` for some group-related operations. diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 40f8cbd56f..1c713aceaf 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -1,5 +1,5 @@ @doc raw""" - CholeskySpace{N} <: AbstractManifold{ℝ} + CholeskySpace{T} <: AbstractManifold{ℝ} The manifold of lower triangular matrices with positive diagonal and a metric based on the cholesky decomposition. The formulae for this manifold @@ -7,13 +7,18 @@ are for example summarized in Table 1 of [Lin:2019](@cite). # Constructor - CholeskySpace(n) + CholeskySpace(n; parameter::Symbol=:field) Generate the manifold of $n× n$ lower triangular matrices with positive diagonal. """ -struct CholeskySpace{N} <: AbstractManifold{ℝ} end +struct CholeskySpace{T} <: AbstractManifold{ℝ} + size::T +end -CholeskySpace(n::Int) = CholeskySpace{n}() +function CholeskySpace(n::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return CholeskySpace{typeof(size)}(size) +end @doc raw""" check_point(M::CholeskySpace, p; kwargs...) @@ -105,6 +110,9 @@ function exp!(::CholeskySpace, q, p, X) return q end +get_n(::CholeskySpace{TypeParameter{N}}) where {N} = N +get_n(M::CholeskySpace{Tuple{Int}}) = get_parameter(M.size)[1] + @doc raw""" inner(M::CholeskySpace, p, X, Y) @@ -164,16 +172,28 @@ Return the manifold dimension for the [`CholeskySpace`](@ref) `M`, i.e. \dim(\mathcal M) = \frac{N(N+1)}{2}. ```` """ -@generated manifold_dimension(::CholeskySpace{N}) where {N} = div(N * (N + 1), 2) +function manifold_dimension(M::CholeskySpace) + N = get_n(M) + return div(N * (N + 1), 2) +end @doc raw""" representation_size(M::CholeskySpace) Return the representation size for the [`CholeskySpace`](@ref)`{N}` `M`, i.e. `(N,N)`. """ -@generated representation_size(::CholeskySpace{N}) where {N} = (N, N) +function representation_size(M::CholeskySpace) + N = get_n(M) + return (N, N) +end -Base.show(io::IO, ::CholeskySpace{N}) where {N} = print(io, "CholeskySpace($(N))") +function Base.show(io::IO, ::CholeskySpace{TypeParameter{Tuple{n}}}) where {n} + return print(io, "CholeskySpace($(n); parameter=:type)") +end +function Base.show(io::IO, M::CholeskySpace{Tuple{Int}}) + n = get_n(M) + return print(io, "CholeskySpace($(n))") +end # two small helpers for strictly lower and upper triangulars strictlyLowerTriangular(p) = LowerTriangular(p) - Diagonal(diag(p)) diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index ed315f3e30..f989907758 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -65,7 +65,7 @@ A good overview can be found in[BendokatZimmermannAbsil:2020](@cite). # Constructor - Grassmann(n,k,field=ℝ) + Grassmann(n, k, field=ℝ, parameter::Symbol=:field) Generate the Grassmann manifold $\operatorname{Gr}(n,k)$, where the real-valued case `field = ℝ` is the default. diff --git a/src/manifolds/GrassmannStiefel.jl b/src/manifolds/GrassmannStiefel.jl index 6877de3f86..035ad99cd3 100644 --- a/src/manifolds/GrassmannStiefel.jl +++ b/src/manifolds/GrassmannStiefel.jl @@ -28,7 +28,7 @@ end ManifoldsBase.@manifold_element_forwards StiefelPoint value ManifoldsBase.@manifold_vector_forwards StiefelTVector value ManifoldsBase.@default_manifold_fallbacks Stiefel StiefelPoint StiefelTVector value value -ManifoldsBase.@default_manifold_fallbacks (Stiefel{n,k,ℝ} where {n,k}) StiefelPoint StiefelTVector value value +ManifoldsBase.@default_manifold_fallbacks (Stiefel{<:Any,ℝ}) StiefelPoint StiefelTVector value value ManifoldsBase.@default_manifold_fallbacks Grassmann StiefelPoint StiefelTVector value value function default_vector_transport_method(::Grassmann, ::Type{<:AbstractArray}) diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 4006f8080a..4dee6fb28e 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -408,7 +408,8 @@ and that means the inverse has to be appliead to the (Euclidean) Hessian to map it into the Lie algebra. """ riemannian_Hessian(M::Rotations, p, G, H, X) -function riemannian_Hessian!(::Rotations{N}, Y, p, G, H, X) where {N} +function riemannian_Hessian!(M::Rotations, Y, p, G, H, X) + N = get_n(M) symmetrize!(Y, G' * p) project!(SkewSymmetricMatrices(N), Y, p' * H - X * Y) return Y diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 2cbee11ac0..81db0a3ce0 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -1,5 +1,5 @@ @doc raw""" - Stiefel{n,k,𝔽} <: AbstractDecoratorManifold{𝔽} + Stiefel{T,𝔽} <: AbstractDecoratorManifold{𝔽} The Stiefel manifold consists of all $n × k$, $n ≥ k$ unitary matrices, i.e. @@ -27,19 +27,24 @@ The manifold is named after [Eduard L. Stiefel](https://en.wikipedia.org/wiki/Eduard_Stiefel) (1909–1978). # Constructor - Stiefel(n, k, field = ℝ) + Stiefel(n, k, field = ℝ; parameter::Symbol=:field) Generate the (real-valued) Stiefel manifold of $n × k$ dimensional orthonormal matrices. """ -struct Stiefel{n,k,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct Stiefel{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end -Stiefel(n::Int, k::Int, field::AbstractNumbers=ℝ) = Stiefel{n,k,field}() +function Stiefel(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n, k)) + return Stiefel{typeof(size),field}(size) +end function active_traits(f, ::Stiefel, args...) return merge_traits(IsIsometricEmbeddedManifold(), IsDefaultMetric(EuclideanMetric())) end -function allocation_promotion_function(::Stiefel{n,k,ℂ}, ::Any, ::Tuple) where {n,k} +function allocation_promotion_function(::Stiefel{<:Any,ℂ}, ::Any, ::Tuple) return complex end @@ -76,7 +81,7 @@ Check whether `p` is a valid point on the [`Stiefel`](@ref) `M`=$\operatorname{S [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system) type and $p^{\mathrm{H}}p$ is (approximately) the identity, where $\cdot^{\mathrm{H}}$ is the complex conjugate transpose. The settings for approximately can be set with `kwargs...`. """ -function check_point(M::Stiefel{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} +function check_point(M::Stiefel, p; kwargs...) cks = check_size(M, p) (cks === nothing) || return cks c = p' * p @@ -98,7 +103,8 @@ it (approximately) holds that $p^{\mathrm{H}}X + \overline{X^{\mathrm{H}}p} = 0$ where $\cdot^{\mathrm{H}}$ denotes the Hermitian and $\overline{\cdot}$ the (elementwise) complex conjugate. The settings for approximately can be set with `kwargs...`. """ -function check_vector(M::Stiefel{n,k,𝔽}, p, X; kwargs...) where {n,k,𝔽} +function check_vector(M::Stiefel, p, X; kwargs...) + n, k = get_nk(M) cks = check_size(M, p, X) cks === nothing || return cks if !isapprox(p' * X, -conj(X' * p); kwargs...) @@ -138,9 +144,16 @@ end embed(::Stiefel, p) = p embed(::Stiefel, p, X) = X -function get_embedding(::Stiefel{N,K,𝔽}) where {N,K,𝔽} - return Euclidean(N, K; field=𝔽) +function get_embedding(::Stiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return Euclidean(n, k; field=𝔽, parameter=:type) end +function get_embedding(M::Stiefel{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_nk(M) + return Euclidean(n, k; field=𝔽) +end + +get_nk(::Stiefel{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) +get_nk(M::Stiefel{Tuple{Int,Int}}) = get_parameter(M.size) @doc raw""" inverse_retract(M::Stiefel, p, q, ::PolarInverseRetraction) @@ -176,7 +189,8 @@ in [KanekoFioriTanaka:2013](@cite). """ inverse_retract(::Stiefel, ::Any, ::Any, ::QRInverseRetraction) -function _stiefel_inv_retr_qr_mul_by_r_generic!(::Stiefel{n,k}, X, q, R, A) where {n,k} +function _stiefel_inv_retr_qr_mul_by_r_generic!(M::Stiefel, X, q, R, A) + n, k = get_nk(M) @inbounds for i in 1:k b = zeros(eltype(R), i) b[i] = 1 @@ -188,12 +202,18 @@ function _stiefel_inv_retr_qr_mul_by_r_generic!(::Stiefel{n,k}, X, q, R, A) wher return mul!(X, q, R) end -function _stiefel_inv_retr_qr_mul_by_r!(::Stiefel{n,1}, X, q, A, ::Type) where {n} +function _stiefel_inv_retr_qr_mul_by_r!( + ::Stiefel{TypeParameter{Tuple{n,1}}}, + X, + q, + A, + ::Type, +) where {n} @inbounds R = SMatrix{1,1}(inv(A[1, 1])) return mul!(X, q, R) end function _stiefel_inv_retr_qr_mul_by_r!( - M::Stiefel{n,1}, + M::Stiefel{TypeParameter{Tuple{n,1}}}, X, q, A::StaticArray, @@ -201,7 +221,13 @@ function _stiefel_inv_retr_qr_mul_by_r!( ) where {n,ElT} return invoke( _stiefel_inv_retr_qr_mul_by_r!, - Tuple{Stiefel{n,1},typeof(X),typeof(q),AbstractArray,typeof(ElT)}, + Tuple{ + Stiefel{TypeParameter{Tuple{n,1}}}, + typeof(X), + typeof(q), + AbstractArray, + typeof(ElT), + }, M, X, q, @@ -209,7 +235,13 @@ function _stiefel_inv_retr_qr_mul_by_r!( ElT, ) end -function _stiefel_inv_retr_qr_mul_by_r!(::Stiefel{n,2}, X, q, A, ::Type{ElT}) where {n,ElT} +function _stiefel_inv_retr_qr_mul_by_r!( + ::Stiefel{TypeParameter{Tuple{n,2}}}, + X, + q, + A, + ::Type{ElT}, +) where {n,ElT} R11 = inv(A[1, 1]) @inbounds R = hcat(SA[R11, zero(ElT)], A[SOneTo(2), SOneTo(2)] \ SA[-R11 * A[2, 1], one(ElT)]) @@ -219,7 +251,7 @@ function _stiefel_inv_retr_qr_mul_by_r!(::Stiefel{n,2}, X, q, A, ::Type{ElT}) wh return mul!(X, q, R) end function _stiefel_inv_retr_qr_mul_by_r!( - M::Stiefel{n,2}, + M::Stiefel{TypeParameter{Tuple{n,2}}}, X, q, A::StaticArray, @@ -227,7 +259,13 @@ function _stiefel_inv_retr_qr_mul_by_r!( ) where {n,ElT} return invoke( _stiefel_inv_retr_qr_mul_by_r!, - Tuple{Stiefel{n,2},typeof(X),typeof(q),AbstractArray,typeof(ElT)}, + Tuple{ + Stiefel{TypeParameter{Tuple{n,2}}}, + typeof(X), + typeof(q), + AbstractArray, + typeof(ElT), + }, M, X, q, @@ -236,7 +274,7 @@ function _stiefel_inv_retr_qr_mul_by_r!( ) end function _stiefel_inv_retr_qr_mul_by_r!( - M::Stiefel{n,k}, + M::Stiefel{TypeParameter{Tuple{n,k}}}, X, q, A::StaticArray, @@ -245,13 +283,8 @@ function _stiefel_inv_retr_qr_mul_by_r!( R = zeros(MMatrix{k,k,ElT}) return _stiefel_inv_retr_qr_mul_by_r_generic!(M, X, q, R, A) end -function _stiefel_inv_retr_qr_mul_by_r!( - M::Stiefel{n,k}, - X, - q, - A, - ::Type{ElT}, -) where {n,k,ElT} +function _stiefel_inv_retr_qr_mul_by_r!(M::Stiefel, X, q, A, ::Type{ElT}) where {ElT} + n, k = get_nk(M) R = zeros(ElT, k, k) return _stiefel_inv_retr_qr_mul_by_r_generic!(M, X, q, R, A) end @@ -264,7 +297,8 @@ function inverse_retract_polar!(::Stiefel, X, p, q) X .-= p return X end -function inverse_retract_qr!(M::Stiefel{n,k}, X, p, q) where {n,k} +function inverse_retract_qr!(M::Stiefel, X, p, q) + n, k = get_nk(M) A = p' * q @boundscheck size(A) === (k, k) ElT = typeof(one(eltype(p)) * one(eltype(q))) @@ -298,9 +332,18 @@ The dimension is given by \end{aligned} ```` """ -manifold_dimension(::Stiefel{n,k,ℝ}) where {n,k} = n * k - div(k * (k + 1), 2) -manifold_dimension(::Stiefel{n,k,ℂ}) where {n,k} = 2 * n * k - k * k -manifold_dimension(::Stiefel{n,k,ℍ}) where {n,k} = 4 * n * k - k * (2k - 1) +function manifold_dimension(M::Stiefel{<:Any,ℝ}) + n, k = get_nk(M) + return n * k - div(k * (k + 1), 2) +end +function manifold_dimension(M::Stiefel{<:Any,ℂ}) + n, k = get_nk(M) + return 2 * n * k - k * k +end +function manifold_dimension(M::Stiefel{<:Any,ℍ}) + n, k = get_nk(M) + return 4 * n * k - k * (2k - 1) +end @doc raw""" rand(::Stiefel; vector_at=nothing, σ::Real=1.0) @@ -318,11 +361,12 @@ rand(::Stiefel; σ::Real=1.0) function Random.rand!( rng::AbstractRNG, - M::Stiefel{n,k,𝔽}, + M::Stiefel{<:Any,𝔽}, pX; vector_at=nothing, σ::Real=one(real(eltype(pX))), -) where {n,k,𝔽} +) where {𝔽} + n, k = get_nk(M) if vector_at === nothing A = σ * randn(rng, 𝔽 === ℝ ? Float64 : ComplexF64, n, k) pX .= Matrix(qr(A).Q) @@ -471,12 +515,18 @@ end Returns the representation size of the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, i.e. `(n,k)`, which is the matrix dimensions. """ -@generated representation_size(::Stiefel{n,k}) where {n,k} = (n, k) +representation_size(M::Stiefel) = get_nk(M) -Base.show(io::IO, ::Stiefel{n,k,F}) where {n,k,F} = print(io, "Stiefel($(n), $(k), $(F))") +function Base.show(io::IO, ::Stiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return print(io, "Stiefel($(n), $(k), $(𝔽); parameter=:type)") +end +function Base.show(io::IO, M::Stiefel{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_nk(M) + return print(io, "Stiefel($(n), $(k), $(𝔽))") +end """ - uniform_distribution(M::Stiefel{n,k,ℝ}, p) + uniform_distribution(M::Stiefel{<:Any,ℝ}, p) Uniform distribution on given (real-valued) [`Stiefel`](@ref) `M`. Specifically, this is the normalized Haar and Hausdorff measure on `M`. @@ -485,7 +535,8 @@ Generated points will be of similar type as `p`. The implementation is based on Section 2.5.1 in [Chikuse:2003](@cite); see also Theorem 2.2.1(iii) in [Chikuse:2003](@cite). """ -function uniform_distribution(M::Stiefel{n,k,ℝ}, p) where {n,k} +function uniform_distribution(M::Stiefel{<:Any,ℝ}, p) + n, k = get_nk(M) μ = Distributions.Zeros(n, k) σ = one(eltype(p)) Σ1 = Distributions.PDMats.ScalMat(n, σ) diff --git a/src/manifolds/StiefelCanonicalMetric.jl b/src/manifolds/StiefelCanonicalMetric.jl index 68d57a6f28..c0e05069db 100644 --- a/src/manifolds/StiefelCanonicalMetric.jl +++ b/src/manifolds/StiefelCanonicalMetric.jl @@ -11,7 +11,7 @@ struct CanonicalMetric <: RiemannianMetric end ApproximateLogarithmicMap <: ApproximateInverseRetraction An approximate implementation of the logarithmic map, which is an [`inverse_retract`](@ref)ion. -See [`inverse_retract(::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, ::Any, ::Any, ::ApproximateLogarithmicMap) where {n,k}`](@ref) for a use case. +See [`inverse_retract(::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, ::Any, ::Any, ::ApproximateLogarithmicMap)`](@ref) for a use case. # Fields @@ -24,15 +24,15 @@ struct ApproximateLogarithmicMap{T} <: ApproximateInverseRetraction tolerance::T end -function distance(M::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, q, p) where {n,k} +function distance(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, q, p) return norm(M, p, log(M, p, q)) end @doc raw""" - q = exp(M::MetricManifold{ℝ, Stiefel{n,k,ℝ}, CanonicalMetric}, p, X) - exp!(M::MetricManifold{ℝ, Stiefel{n,k,ℝ}, q, CanonicalMetric}, p, X) + q = exp(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, p, X) + exp!(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, q, p, X) -Compute the exponential map on the [`Stiefel`](@ref)`(n,k)` manifold with respect to the [`CanonicalMetric`](@ref). +Compute the exponential map on the [`Stiefel`](@ref)`(n, k)` manifold with respect to the [`CanonicalMetric`](@ref). First, decompose The tangent vector ``X`` into its horizontal and vertical component with respect to ``p``, i.e. @@ -64,9 +64,10 @@ q = \exp_p X = pC + QB. ``` For more details, see [EdelmanAriasSmith:1998](@cite)[Zimmermann:2017](@cite). """ -exp(::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, ::Any...) where {n,k} +exp(::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, ::Any...) -function exp!(::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, q, p, X) where {n,k} +function exp!(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, q, p, X) + n, k = get_nk(M.manifold) A = p' * X n == k && return mul!(q, p, exp(A)) QR = qr(X - p * A) @@ -79,7 +80,7 @@ function exp!(::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, q, p, X) w end @doc raw""" - inner(M::MetricManifold{ℝ, Stiefel{n,k,ℝ}, X, CanonicalMetric}, p, X, Y) + inner(M::MetricManifold{ℝ, Stiefel{<:Any,ℝ}, X, CanonicalMetric}, p, X, Y) Compute the inner product on the [`Stiefel`](@ref) manifold with respect to the [`CanonicalMetric`](@ref). The formula reads @@ -88,7 +89,8 @@ Compute the inner product on the [`Stiefel`](@ref) manifold with respect to the g_p(X,Y) = \operatorname{tr}\bigl( X^{\mathrm{T}}(I_n - \frac{1}{2}pp^{\mathrm{T}})Y \bigr). ``` """ -function inner(::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, p, X, Y) where {n,k} +function inner(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, p, X, Y) + n, k = get_nk(M.manifold) T = Base.promote_eltype(p, X, Y) if n == k return T(dot(X, Y)) / 2 @@ -98,54 +100,55 @@ function inner(::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, p, X, Y) end @doc raw""" - X = inverse_retract(M::MetricManifold{ℝ, Stiefel{n,k,ℝ}, CanonicalMetric}, p, q, a::ApproximateLogarithmicMap) - inverse_retract!(M::MetricManifold{ℝ, Stiefel{n,k,ℝ}, X, CanonicalMetric}, p, q, a::ApproximateLogarithmicMap) + X = inverse_retract(M::MetricManifold{ℝ, Stiefel{<:Any,ℝ}, CanonicalMetric}, p, q, a::ApproximateLogarithmicMap) + inverse_retract!(M::MetricManifold{ℝ, Stiefel{<:Any,ℝ}, X, CanonicalMetric}, p, q, a::ApproximateLogarithmicMap) -Compute an approximation to the logarithmic map on the [`Stiefel`](@ref)`(n,k)` manifold with respect to the [`CanonicalMetric`](@ref) +Compute an approximation to the logarithmic map on the [`Stiefel`](@ref)`(n, k)` manifold with respect to the [`CanonicalMetric`](@ref) using a matrix-algebraic based approach to an iterative inversion of the formula of the -[`exp`](@ref exp(::MetricManifold{ℝ, Stiefel{n,k,ℝ}, CanonicalMetric}, ::Any...) where {n,k}). +[`exp`](@ref exp(::MetricManifold{ℝ, Stiefel{<:Any,ℝ}, CanonicalMetric}, ::Any...)). The algorithm is derived in [Zimmermann:2017](@cite) and it uses the `max_iterations` and the `tolerance` field from the [`ApproximateLogarithmicMap`](@ref). """ inverse_retract( - ::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, + ::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, ::Any, ::Any, ::ApproximateLogarithmicMap, -) where {n,k} +) function log( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, p, q; maxiter::Int=10000, tolerance=1e-9, -) where {n,k} +) X = allocate_result(M, log, p, q) inverse_retract!(M, X, p, q, ApproximateLogarithmicMap(maxiter, tolerance)) return X end function log!( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, X, p, q; maxiter::Int=10000, tolerance=1e-9, -) where {n,k} +) inverse_retract!(M, X, p, q, ApproximateLogarithmicMap(maxiter, tolerance)) return X end function inverse_retract!( - ::MetricManifold{ℝ,Stiefel{n,k,ℝ},CanonicalMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, X, p, q, a::ApproximateLogarithmicMap, -) where {n,k} +) + n, k = get_nk(M.manifold) qfact = stiefel_factorization(p, q) V = allocate(qfact.Z, Size(2k, 2k)) LV = allocate(V) @@ -176,8 +179,8 @@ function inverse_retract!( end @doc raw""" - Y = riemannian_Hessian(M::MetricManifold{ℝ, Stiefel{n,k}, CanonicalMetric}, p, G, H, X) - riemannian_Hessian!(M::MetricManifold{ℝ, Stiefel{n,k}, CanonicalMetric}, Y, p, G, H, X) + Y = riemannian_Hessian(M::MetricManifold{ℝ, Stiefel, CanonicalMetric}, p, G, H, X) + riemannian_Hessian!(M::MetricManifold{ℝ, Stiefel, CanonicalMetric}, Y, p, G, H, X) Compute the Riemannian Hessian ``\operatorname{Hess} f(p)[X]`` given the Euclidean gradient ``∇ f(\tilde p)`` in `G` and the Euclidean Hessian ``∇^2 f(\tilde p)[\tilde X]`` in `H`, @@ -196,22 +199,16 @@ Here, we adopt Eq. (5.6) [Nguyen:2023](@cite), for the [`CanonicalMetric`](@ref) ``` where ``P = I-pp^{\mathrm{H}}``. """ -riemannian_Hessian( - M::MetricManifold{𝔽,Stiefel{n,k,𝔽},CanonicalMetric}, - p, - G, - H, - X, -) where {n,k,𝔽} +riemannian_Hessian(M::MetricManifold{𝔽,Stiefel,CanonicalMetric}, p, G, H, X) where {𝔽} function riemannian_Hessian!( - M::MetricManifold{𝔽,Stiefel{n,k,𝔽},CanonicalMetric}, + M::MetricManifold{𝔽,<:Stiefel{<:Any,𝔽},CanonicalMetric}, Y, p, G, H, X, -) where {n,k,𝔽} +) where {𝔽} Gp = symmetrize(G' * p) Z = symmetrize((I - p * p') * G * p') project!(M, Y, p, H - X * Gp - Z * X) diff --git a/src/manifolds/StiefelEuclideanMetric.jl b/src/manifolds/StiefelEuclideanMetric.jl index f897f4053b..50a9c451a4 100644 --- a/src/manifolds/StiefelEuclideanMetric.jl +++ b/src/manifolds/StiefelEuclideanMetric.jl @@ -23,7 +23,8 @@ $0_k$ are the identity matrix and the zero matrix of dimension $k × k$, respect """ exp(::Stiefel, ::Any...) -function exp!(::Stiefel{n,k}, q, p, X) where {n,k} +function exp!(M::Stiefel, q, p, X) + n, k = get_nk(M) A = p' * X B = exp([A -X'*X; I A]) @views begin @@ -35,7 +36,7 @@ function exp!(::Stiefel{n,k}, q, p, X) where {n,k} end @doc raw""" - get_basis(M::Stiefel{n,k,ℝ}, p, B::DefaultOrthonormalBasis) where {n,k} + get_basis(M::Stiefel{<:Any,ℝ}, p, B::DefaultOrthonormalBasis) Create the default basis using the parametrization for any $X ∈ T_p\mathcal M$. Set $p_\bot \in ℝ^{n\times(n-k)}$ the matrix such that the $n\times n$ matrix of the common @@ -58,30 +59,24 @@ trangular entries of $a$ is set to $1$ its symmetric entry to $-1$ and we normal the factor $\frac{1}{\sqrt{2}}$ and for $b$ one can just use unit vectors reshaped to a matrix to obtain orthonormal set of parameters. """ -get_basis(M::Stiefel{n,k,ℝ}, p, B::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) where {n,k} +get_basis(M::Stiefel{<:Any,ℝ}, p, B::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) function _get_basis( - M::Stiefel{n,k,ℝ}, + M::Stiefel{<:Any,ℝ}, p, B::DefaultOrthonormalBasis{ℝ,TangentSpaceType}; kwargs..., -) where {n,k} +) return CachedBasis(B, get_vectors(M, p, B)) end -function get_coordinates_orthonormal!( - M::Stiefel{n,k,ℝ}, - c, - p, - X, - N::RealNumbers, -) where {n,k} +function get_coordinates_orthonormal!(M::Stiefel{<:Any,ℝ}, c, p, X, N::RealNumbers) V = get_vectors(M, p, DefaultOrthonormalBasis(N)) c .= inner.(Ref(M), Ref(p), V, Ref(X)) return c end -function get_vector_orthonormal!(M::Stiefel{n,k,ℝ}, X, p, c, N::RealNumbers) where {n,k} +function get_vector_orthonormal!(M::Stiefel{<:Any,ℝ}, X, p, c, N::RealNumbers) V = get_vectors(M, p, DefaultOrthonormalBasis(N)) zero_vector!(M, X, p) length(c) < length(V) && error( @@ -93,11 +88,8 @@ function get_vector_orthonormal!(M::Stiefel{n,k,ℝ}, X, p, c, N::RealNumbers) w return X end -function get_vectors( - ::Stiefel{n,k,ℝ}, - p, - ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, -) where {n,k} +function get_vectors(M::Stiefel{<:Any,ℝ}, p, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) + n, k = get_nk(M) p⊥ = nullspace([p zeros(n, n - k)]) an = div(k * (k - 1), 2) bn = (n - k) * k @@ -126,7 +118,7 @@ function inverse_retract_project!(M::Stiefel, X, p, q) return X end -function log!(M::Stiefel{n,k,ℝ}, X, p, q) where {n,k} +function log!(M::Stiefel{<:Any,ℝ}, X, p, q) MM = MetricManifold(M, StiefelSubmersionMetric(-1 // 2)) log!(MM, X, p, q) return X diff --git a/src/manifolds/StiefelSubmersionMetric.jl b/src/manifolds/StiefelSubmersionMetric.jl index 4c0fbbe00e..982c7f385f 100644 --- a/src/manifolds/StiefelSubmersionMetric.jl +++ b/src/manifolds/StiefelSubmersionMetric.jl @@ -22,8 +22,8 @@ struct StiefelSubmersionMetric{T<:Real} <: RiemannianMetric end @doc raw""" - q = exp(M::MetricManifold{ℝ, Stiefel{n,k,ℝ}, <:StiefelSubmersionMetric}, p, X) - exp!(M::MetricManifold{ℝ, Stiefel{n,k,ℝ}, q, <:StiefelSubmersionMetric}, p, X) + q = exp(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p, X) + exp!(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, q, p, X) Compute the exponential map on the [`Stiefel(n,k)`](@ref) manifold with respect to the [`StiefelSubmersionMetric`](@ref). @@ -40,14 +40,10 @@ This implementation is based on [ZimmermannHueper:2022](@cite). For ``k < \frac{n}{2}`` the exponential is computed more efficiently using [`StiefelFactorization`](@ref). """ -exp(::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, ::Any...) where {n,k} +exp(::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, ::Any...) -function exp!( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, - q, - p, - X, -) where {n,k} +function exp!(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, q, p, X) + n, k = get_nk(M.manifold) α = metric(M).α T = Base.promote_eltype(q, p, X) if k ≤ div(n, 2) @@ -82,7 +78,7 @@ function exp!( end @doc raw""" - inner(M::MetricManifold{ℝ, Stiefel{n,k,ℝ}, X, <:StiefelSubmersionMetric}, p, X, Y) + inner(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p, X, Y) Compute the inner product on the [`Stiefel`](@ref) manifold with respect to the [`StiefelSubmersionMetric`](@ref). The formula reads @@ -91,12 +87,8 @@ g_p(X,Y) = \operatorname{tr}\bigl( X^{\mathrm{T}}(I_n - \frac{2α+1}{2(α+1)}pp^ ``` where ``α`` is the parameter of the metric. """ -function inner( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, - p, - X, - Y, -) where {n,k} +function inner(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p, X, Y) + n, k = get_nk(M.manifold) α = metric(M).α T = typeof(one(Base.promote_eltype(p, X, Y, α))) if n == k @@ -119,7 +111,7 @@ end @doc doc""" inverse_retract( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p, q, method::ShootingInverseRetraction, @@ -130,7 +122,7 @@ Compute the inverse retraction using [`ShootingInverseRetraction`](https://julia In general the retraction is computed using the generic shooting method. inverse_retract( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p, q, method::ShootingInverseRetraction{ @@ -153,7 +145,7 @@ inverse_retract( ) function inverse_retract_shooting!( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, X::AbstractMatrix, p::AbstractMatrix, q::AbstractMatrix, @@ -162,13 +154,14 @@ function inverse_retract_shooting!( ProjectionInverseRetraction, <:Union{ProjectionTransport,ScaledVectorTransport{ProjectionTransport}}, }, -) where {n,k} +) + n, k = get_nk(M.manifold) if k > div(n, 2) # fall back to default method invoke( inverse_retract_shooting!, Tuple{ - MetricManifold{ℝ,Stiefel{n,k,ℝ}}, + MetricManifold{ℝ,typeof(M.manifold)}, typeof(X), typeof(p), typeof(q), @@ -192,7 +185,7 @@ function inverse_retract_shooting!( end @doc raw""" - log(M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, p, q; kwargs...) + log(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p, q; kwargs...) Compute the logarithmic map on the [`Stiefel(n,k)`](@ref) manifold with respect to the [`StiefelSubmersionMetric`](@ref). @@ -205,13 +198,13 @@ that documentation for details. Their defaults are: - `max_iterations=1_000` """ function log( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p, q; tolerance=sqrt(eps(float(real(Base.promote_eltype(p, q))))), max_iterations=1_000, num_transport_points=4, -) where {n,k} +) X = allocate_result(M, log, p, q) log!( M, @@ -225,14 +218,14 @@ function log( return X end function log!( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, X, p, q; tolerance=sqrt(eps(float(real(eltype(X))))), max_iterations=1_000, num_transport_points=4, -) where {n,k} +) retraction = ExponentialRetraction() initial_inverse_retraction = ProjectionInverseRetraction() vector_transport = ScaledVectorTransport(ProjectionTransport()) @@ -248,8 +241,8 @@ function log!( end @doc raw""" - Y = riemannian_Hessian(M::MetricManifold{ℝ,Stiefel{n,k,ℝ}, StiefelSubmersionMetric},, p, G, H, X) - riemannian_Hessian!(MetricManifold{ℝ,Stiefel{n,k,ℝ}, StiefelSubmersionMetric},, Y, p, G, H, X) + Y = riemannian_Hessian(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},StiefelSubmersionMetric}, p, G, H, X) + riemannian_Hessian!(MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},StiefelSubmersionMetric}, Y, p, G, H, X) Compute the Riemannian Hessian ``\operatorname{Hess} f(p)[X]`` given the Euclidean gradient ``∇ f(\tilde p)`` in `G` and the Euclidean Hessian ``∇^2 f(\tilde p)[\tilde X]`` in `H`, @@ -271,21 +264,21 @@ where ``P = I-pp^{\mathrm{H}}``. Compared to Eq. (5.6) we have that their ``α_0 = 1``and ``\alpha_1 = \frac{2α+1}{2(α+1)} + 1``. """ riemannian_Hessian( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p, G, H, X, -) where {n,k} +) function riemannian_Hessian!( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, Y, p, G, H, X, -) where {n,k} +) α = metric(M).α Gp = symmetrize(G' * p) Z = symmetrize((I - p * p') * G * p') @@ -416,38 +409,38 @@ function Base.copyto!( broadcast!(bc.f, dest.Z, Zargs...) return dest end -function project!( - ::Stiefel{n,k,ℝ}, - q::StiefelFactorization, - p::StiefelFactorization, -) where {n,k} +function project!(M::Stiefel{<:Any,ℝ}, q::StiefelFactorization, p::StiefelFactorization) + n, k = get_nk(M) project!(Stiefel(2k, k), q.Z, p.Z) return q end function project!( - ::Stiefel{n,k,ℝ}, + M::Stiefel{<:Any,ℝ}, Y::StiefelFactorization, p::StiefelFactorization, X::StiefelFactorization, -) where {n,k} +) + n, k = get_nk(M) project!(Stiefel(2k, k), Y.Z, p.Z, X.Z) return Y end function inner( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p::StiefelFactorization, X::StiefelFactorization, Y::StiefelFactorization, -) where {n,k} +) + n, k = get_nk(M.manifold) Msub = MetricManifold(Stiefel(2k, k), metric(M)) return inner(Msub, p.Z, X.Z, Y.Z) end function exp!( - M::MetricManifold{ℝ,Stiefel{n,k,ℝ},<:StiefelSubmersionMetric}, + M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, q::StiefelFactorization, p::StiefelFactorization, X::StiefelFactorization, -) where {n,k} +) + n, k = get_nk(M.manifold) α = metric(M).α @views begin ZM = X.Z[1:k, 1:k] diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index 44e974741d..d70b1d9282 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -1,5 +1,5 @@ @doc raw""" - SymmetricPositiveDefinite{N} <: AbstractDecoratorManifold{ℝ} + SymmetricPositiveDefinite{T} <: AbstractDecoratorManifold{ℝ} The manifold of symmetric positive definite matrices, i.e. @@ -22,13 +22,18 @@ i.e. the set of symmetric matrices, # Constructor - SymmetricPositiveDefinite(n) + SymmetricPositiveDefinite(n; parameter::Symbol=:field) generates the manifold $\mathcal P(n) \subset ℝ^{n × n}$ """ -struct SymmetricPositiveDefinite{N} <: AbstractDecoratorManifold{ℝ} end +struct SymmetricPositiveDefinite{T} <: AbstractDecoratorManifold{ℝ} + size::T +end -SymmetricPositiveDefinite(n::Int) = SymmetricPositiveDefinite{n}() +function SymmetricPositiveDefinite(n::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return SymmetricPositiveDefinite{typeof(size)}(size) +end @doc raw""" SPDPoint <: AbstractManifoldsPoint @@ -131,7 +136,7 @@ checks, whether `p` is a valid point on the [`SymmetricPositiveDefinite`](@ref) of size `(N,N)`, symmetric and positive definite. The tolerance for the second to last test can be set using the `kwargs...`. """ -function check_point(M::SymmetricPositiveDefinite{N}, p; kwargs...) where {N} +function check_point(M::SymmetricPositiveDefinite, p; kwargs...) if !isapprox(norm(p - transpose(p)), 0.0; kwargs...) return DomainError( norm(p - transpose(p)), @@ -159,7 +164,7 @@ and a symmetric matrix, i.e. this stores tangent vetors as elements of the corre Lie group. The tolerance for the last test can be set using the `kwargs...`. """ -function check_vector(M::SymmetricPositiveDefinite{N}, p, X; kwargs...) where {N} +function check_vector(M::SymmetricPositiveDefinite, p, X; kwargs...) if !isapprox(norm(X - transpose(X)), 0.0; kwargs...) return DomainError( X, @@ -227,6 +232,9 @@ function get_embedding(M::SymmetricPositiveDefinite) return Euclidean(representation_size(M)...; field=ℝ) end +get_n(::SymmetricPositiveDefinite{TypeParameter{N}}) where {N} = N +get_n(M::SymmetricPositiveDefinite{Tuple{Int}}) = get_parameter(M.size)[1] + @doc raw""" injectivity_radius(M::SymmetricPositiveDefinite[, p]) injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,AffineInvariantMetric}[, p]) @@ -261,8 +269,9 @@ returns the dimension of \dim \mathcal P(n) = \frac{n(n+1)}{2}. ```` """ -@generated function manifold_dimension(::SymmetricPositiveDefinite{N}) where {N} - return div(N * (N + 1), 2) +function manifold_dimension(M::SymmetricPositiveDefinite) + n = get_n(M) + return div(n * (n + 1), 2) end """ @@ -405,13 +414,14 @@ rand(M::SymmetricPositiveDefinite; σ::Real=1) function Random.rand!( rng::AbstractRNG, - M::SymmetricPositiveDefinite{N}, + M::SymmetricPositiveDefinite, pX; vector_at=nothing, σ::Real=one(eltype(pX)) / (vector_at === nothing ? 1 : norm(convert(AbstractMatrix, vector_at))), tangent_distr=:Gaussian, -) where {N} +) + N = get_n(M) if vector_at === nothing D = Diagonal(1 .+ rand(rng, N)) # random diagonal matrix s = qr(σ * randn(rng, N, N)) # random q @@ -454,10 +464,17 @@ Return the size of an array representing an element on the [`SymmetricPositiveDefinite`](@ref) manifold `M`, i.e. $n × n$, the size of such a symmetric positive definite matrix on $\mathcal M = \mathcal P(n)$. """ -@generated representation_size(::SymmetricPositiveDefinite{N}) where {N} = (N, N) +function representation_size(M::SymmetricPositiveDefinite) + N = get_n(M) + return (N, N) +end -function Base.show(io::IO, ::SymmetricPositiveDefinite{N}) where {N} - return print(io, "SymmetricPositiveDefinite($(N))") +function Base.show(io::IO, ::SymmetricPositiveDefinite{TypeParameter{Tuple{n}}}) where {n} + return print(io, "SymmetricPositiveDefinite($(n); parameter=:type)") +end +function Base.show(io::IO, M::SymmetricPositiveDefinite{Tuple{Int}}) + n = get_n(M) + return print(io, "SymmetricPositiveDefinite($(n))") end function Base.show(io::IO, ::MIME"text/plain", p::SPDPoint) @@ -489,4 +506,4 @@ function zero_vector(M::SymmetricPositiveDefinite, p::SPDPoint) return zero_vector(M, convert(AbstractMatrix, p)) end -zero_vector!(::SymmetricPositiveDefinite{N}, X, ::Any) where {N} = fill!(X, 0) +zero_vector!(::SymmetricPositiveDefinite, X, ::Any) = fill!(X, 0) diff --git a/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl b/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl index dde2356aef..c41acc107c 100644 --- a/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl +++ b/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl @@ -31,7 +31,7 @@ function change_representer!(::SymmetricPositiveDefinite, Y, ::EuclideanMetric, end @doc raw""" - change_metric(M::SymmetricPositiveDefinite{n}, E::EuclideanMetric, p, X) + change_metric(M::SymmetricPositiveDefinite, E::EuclideanMetric, p, X) Given a tangent vector ``X ∈ T_p\mathcal P(n)`` with respect to the [`EuclideanMetric`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.EuclideanMetric) `g_E`, this function changes into the [`AffineInvariantMetric`](@ref) (default) metric on the @@ -67,7 +67,7 @@ d_{\mathcal P(n)}(p,q) where $\operatorname{Log}$ denotes the matrix logarithm and $\lVert\cdot\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. """ -function distance(::SymmetricPositiveDefinite{N}, p, q) where {N} +function distance(::SymmetricPositiveDefinite, p, q) # avoid numerical instabilities in cholesky norm(p - q) < eps(eltype(p + q)) && return zero(eltype(p + q)) cq = cholesky(Symmetric(q)) # to avoid numerical inaccuracies @@ -80,7 +80,7 @@ end @doc raw""" exp(M::SymmetricPositiveDefinite, p, X) - exp(M::MetricManifold{SymmetricPositiveDefinite{N},AffineInvariantMetric}, p, X) + exp(M::MetricManifold{<:SymmetricPositiveDefinite,AffineInvariantMetric}, p, X) Compute the exponential map from `p` with tangent vector `X` on the [`SymmetricPositiveDefinite`](@ref) `M` with its default [`MetricManifold`](@ref) having the @@ -95,7 +95,7 @@ where $\operatorname{Exp}$ denotes to the matrix exponential. exp(::SymmetricPositiveDefinite, ::Any...) exp(M::SymmetricPositiveDefinite, p::SPDPoint, X, t::Number) = exp(M, p, t * X) -function exp(::SymmetricPositiveDefinite{N}, p::SPDPoint, X) where {N} +function exp(::SymmetricPositiveDefinite, p::SPDPoint, X) (p_sqrt, p_sqrt_inv) = spd_sqrt_and_sqrt_inv(p) T = Symmetric(p_sqrt_inv * X * p_sqrt_inv) eig1 = eigen(T) # numerical stabilization @@ -114,7 +114,7 @@ end function exp!(M::SymmetricPositiveDefinite, q, p, X, t::Number) return exp!(M, q, p, t * X) end -function exp!(::SymmetricPositiveDefinite{N}, q, p, X) where {N} +function exp!(::SymmetricPositiveDefinite, q, p, X) (p_sqrt, p_sqrt_inv) = spd_sqrt_and_sqrt_inv(p) T = Symmetric(p_sqrt_inv * X * p_sqrt_inv) eig1 = eigen(T) # numerical stabilization @@ -123,7 +123,7 @@ function exp!(::SymmetricPositiveDefinite{N}, q, p, X) where {N} pUe = p_sqrt * Ue return copyto!(q, pUe * Se * transpose(pUe)) end -function exp!(::SymmetricPositiveDefinite{N}, q::SPDPoint, p, X) where {N} +function exp!(::SymmetricPositiveDefinite, q::SPDPoint, p, X) (p_sqrt, p_sqrt_inv) = spd_sqrt_and_sqrt_inv(p) T = Symmetric(p_sqrt_inv * X * p_sqrt_inv) eig1 = eigen(T) # numerical stabilization @@ -146,7 +146,7 @@ end @doc raw""" [Ξ,κ] = get_basis_diagonalizing(M::SymmetricPositiveDefinite, p, B::DiagonalizingOrthonormalBasis) - [Ξ,κ] = get_basis_diagonalizing(M::MetricManifold{SymmetricPositiveDefinite{N},AffineInvariantMetric}, p, B::DiagonalizingOrthonormalBasis) + [Ξ,κ] = get_basis_diagonalizing(M::MetricManifold{<:SymmetricPositiveDefinite,AffineInvariantMetric}, p, B::DiagonalizingOrthonormalBasis) Return a orthonormal basis `Ξ` as a vector of tangent vectors (of length [`manifold_dimension`](@ref) of `M`) in the tangent space of `p` on the @@ -158,10 +158,11 @@ The construction is based on an ONB for the symmetric matrices similar to [`get_ just that the ONB here is build from the eigen vectors of ``p^{\frac{1}{2}}Vp^{\frac{1}{2}}``. """ function get_basis_diagonalizing( - ::SymmetricPositiveDefinite{N}, + M::SymmetricPositiveDefinite, p, B::DiagonalizingOrthonormalBasis, -) where {N} +) + N = get_n(M) (p_sqrt, p_sqrt_inv) = spd_sqrt_and_sqrt_inv(p) eigv = eigen(Symmetric(p_sqrt_inv * B.frame_direction * p_sqrt_inv)) V = eigv.vectors @@ -178,7 +179,7 @@ end @doc raw""" [Ξ,κ] = get_basis(M::SymmetricPositiveDefinite, p, B::DefaultOrthonormalBasis) - [Ξ,κ] = get_basis(M::MetricManifold{SymmetricPositiveDefinite{N},AffineInvariantMetric}, p, B::DefaultOrthonormalBasis) + [Ξ,κ] = get_basis(M::MetricManifold{<:SymmetricPositiveDefinite,AffineInvariantMetric}, p, B::DefaultOrthonormalBasis) Return a default ONB for the tangent space ``T_p\mathcal P(n)`` of the [`SymmetricPositiveDefinite`](@ref) with respect to the [`AffineInvariantMetric`](@ref). @@ -208,11 +209,8 @@ We then form the ONB by """ get_basis(::SymmetricPositiveDefinite, p, B::DefaultOrthonormalBasis) -function get_basis_orthonormal( - M::SymmetricPositiveDefinite{N}, - p, - Ns::RealNumbers, -) where {N} +function get_basis_orthonormal(M::SymmetricPositiveDefinite, p, Ns::RealNumbers) + N = get_n(M) p_sqrt = spd_sqrt(p) Ξ = [similar(convert(AbstractMatrix, p)) for _ in 1:manifold_dimension(M)] k = 1 @@ -240,13 +238,8 @@ where $k$ is trhe linearized index of the $i=1,\ldots,n, j=i,\ldots,n$. """ get_coordinates(::SymmetricPositiveDefinite, c, p, X, ::DefaultOrthonormalBasis) -function get_coordinates_orthonormal!( - M::SymmetricPositiveDefinite{N}, - c, - p, - X, - ::RealNumbers, -) where {N} +function get_coordinates_orthonormal!(M::SymmetricPositiveDefinite, c, p, X, ::RealNumbers) + N = get_n(M) dim = manifold_dimension(M) @assert size(c) == (dim,) @assert size(X) == (N, N) @@ -283,13 +276,8 @@ where $k$ is the linearized index of the $i=1,\ldots,n, j=i,\ldots,n$. """ get_vector(::SymmetricPositiveDefinite, X, p, c, ::DefaultOrthonormalBasis) -function get_vector_orthonormal!( - ::SymmetricPositiveDefinite{N}, - X, - p, - c, - ::RealNumbers, -) where {N} +function get_vector_orthonormal!(M::SymmetricPositiveDefinite, X, p, c, ::RealNumbers) + N = get_n(M) @assert size(c) == (div(N * (N + 1), 2),) @assert size(X) == (N, N) p_sqrt = spd_sqrt(p) @@ -359,7 +347,7 @@ function allocate_result( return allocate_result(M, log, convert(AbstractMatrix, q), convert(AbstractMatrix, p)) end -function log!(::SymmetricPositiveDefinite{N}, X, p, q) where {N} +function log!(::SymmetricPositiveDefinite, X, p, q) (p_sqrt, p_sqrt_inv) = spd_sqrt_and_sqrt_inv(p) T = Symmetric(p_sqrt_inv * convert(AbstractMatrix, q) * p_sqrt_inv) e2 = eigen(T) @@ -402,7 +390,7 @@ and `log` the logarithmic map on [`SymmetricPositiveDefinite`](@ref) """ parallel_transport_to(::SymmetricPositiveDefinite, ::Any, ::Any, ::Any) -function parallel_transport_to!(M::SymmetricPositiveDefinite{N}, Y, p, X, q) where {N} +function parallel_transport_to!(M::SymmetricPositiveDefinite, Y, p, X, q) distance(M, p, q) < 2 * eps(eltype(p)) && copyto!(Y, X) (p_sqrt, p_sqrt_inv) = spd_sqrt_and_sqrt_inv(p) tv = Symmetric(p_sqrt_inv * X * p_sqrt_inv) # p^(-1/2)Xp^{-1/2} @@ -467,13 +455,13 @@ function riemann_tensor!(::SymmetricPositiveDefinite, Xresult, p, X, Y, Z) end """ - volume_density(::SymmetricPositiveDefinite{n}, p, X) where {n} + volume_density(::SymmetricPositiveDefinite, p, X) Compute the volume density of the [`SymmetricPositiveDefinite`](@ref) manifold at `p` in direction `X`. See [ChevallierKalungaAngulo:2017](@cite), Section 6.2 for details. Note that metric in Manifolds.jl has a different scaling factor than the reference. """ -function volume_density(::SymmetricPositiveDefinite{n}, p, X) where {n} +function volume_density(::SymmetricPositiveDefinite, p, X) eig = eigvals(X) dens = 1.0 for i in 1:length(eig) diff --git a/src/manifolds/SymmetricPositiveDefiniteBuresWasserstein.jl b/src/manifolds/SymmetricPositiveDefiniteBuresWasserstein.jl index 95e06819bb..84732f071f 100644 --- a/src/manifolds/SymmetricPositiveDefiniteBuresWasserstein.jl +++ b/src/manifolds/SymmetricPositiveDefiniteBuresWasserstein.jl @@ -6,7 +6,7 @@ The Bures Wasserstein metric for symmetric positive definite matrices [MalagoMon struct BuresWassersteinMetric <: RiemannianMetric end @doc raw""" - change_representer(M::MetricManifold{ℝ,SymmetricPositiveDefinite,BuresWassersteinMetric}, E::EuclideanMetric, p, X) + change_representer(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,BuresWassersteinMetric}, E::EuclideanMetric, p, X) Given a tangent vector ``X ∈ T_p\mathcal M`` representing a linear function on the tangent space at `p` with respect to the [`EuclideanMetric`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.EuclideanMetric) @@ -23,7 +23,7 @@ for all ``Y`` and hence we get ``Z``= 2(A+A^{\mathrm{T}})`` with ``A=Xp``. """ change_representer( - ::MetricManifold{ℝ,SymmetricPositiveDefinite,BuresWassersteinMetric}, + ::MetricManifold{ℝ,<:SymmetricPositiveDefinite,BuresWassersteinMetric}, ::EuclideanMetric, p, X, @@ -73,7 +73,7 @@ the [`BuresWassersteinMetric`](@ref) given by where ``q=L_p(X)`` denotes the Lyapunov operator, i.e. it solves ``pq + qp = X``. """ -exp(::MetricManifold{ℝ,SymmetricPositiveDefinite,BuresWassersteinMetric}, p, X) +exp(::MetricManifold{ℝ,<:SymmetricPositiveDefinite,BuresWassersteinMetric}, p, X) function exp!( ::MetricManifold{ℝ,<:SymmetricPositiveDefinite,BuresWassersteinMetric}, @@ -127,7 +127,7 @@ the [`BuresWassersteinMetric`](@ref) given by where ``q=L_p(X)`` denotes the Lyapunov operator, i.e. it solves ``pq + qp = X``. """ -log(::MetricManifold{ℝ,SymmetricPositiveDefinite,BuresWassersteinMetric}, p, q) +log(::MetricManifold{ℝ,<:SymmetricPositiveDefinite,BuresWassersteinMetric}, p, q) function log!( ::MetricManifold{ℝ,<:SymmetricPositiveDefinite,BuresWassersteinMetric}, diff --git a/src/manifolds/SymmetricPositiveDefiniteGeneralizedBuresWasserstein.jl b/src/manifolds/SymmetricPositiveDefiniteGeneralizedBuresWasserstein.jl index b710406fa3..262654a864 100644 --- a/src/manifolds/SymmetricPositiveDefiniteGeneralizedBuresWasserstein.jl +++ b/src/manifolds/SymmetricPositiveDefiniteGeneralizedBuresWasserstein.jl @@ -12,7 +12,7 @@ struct GeneralizedBuresWassersteinMetric{T<:AbstractMatrix} <: RiemannianMetric end @doc raw""" - change_representer(M::MetricManifold{ℝ,SymmetricPositiveDefinite,GeneralizedBuresWassersteinMetric}, E::EuclideanMetric, p, X) + change_representer(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, E::EuclideanMetric, p, X) Given a tangent vector ``X ∈ T_p\mathcal M`` representing a linear function on the tangent space at `p` with respect to the [`EuclideanMetric`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.EuclideanMetric) @@ -28,7 +28,7 @@ it holds for all ``Y`` and hence we get ``Z = 2pXM + 2MXp``. """ change_representer( - ::MetricManifold{ℝ,SymmetricPositiveDefinite,GeneralizedBuresWassersteinMetric}, + ::MetricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, ::EuclideanMetric, p, X, @@ -67,7 +67,7 @@ function distance( end @doc raw""" - exp(::MatricManifold{ℝ,SymmetricPositiveDefinite,GeneralizedBuresWassersteinMetric}, p, X) + exp(::MatricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, p, X) Compute the exponential map on [`SymmetricPositiveDefinite`](@ref) with respect to the [`GeneralizedBuresWassersteinMetric`](@ref) given by @@ -78,7 +78,11 @@ the [`GeneralizedBuresWassersteinMetric`](@ref) given by where ``q=L_{M,p}(X)`` denotes the generalized Lyapunov operator, i.e. it solves ``pqM + Mqp = X``. """ -exp(::MetricManifold{ℝ,SymmetricPositiveDefinite,GeneralizedBuresWassersteinMetric}, p, X) +exp( + ::MetricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, + p, + X, +) function exp!( M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, @@ -95,7 +99,7 @@ function exp!( end @doc raw""" - inner(::MetricManifold{ℝ,SymmetricPositiveDefinite,GeneralizedBuresWassersteinMetric}, p, X, Y) + inner(::MetricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, p, X, Y) Compute the inner product [`SymmetricPositiveDefinite`](@ref) with respect to the [`GeneralizedBuresWassersteinMetric`](@ref) given by @@ -122,13 +126,13 @@ Return false. [`SymmetricPositiveDefinite`](@ref) with [`GeneralizedBuresWassers is not a flat manifold. """ function is_flat( - M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, + ::MetricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, ) return false end @doc raw""" - log(::MatricManifold{SymmetricPositiveDefinite,GeneralizedBuresWassersteinMetric}, p, q) + log(::MatricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, p, q) Compute the logarithmic map on [`SymmetricPositiveDefinite`](@ref) with respect to the [`BuresWassersteinMetric`](@ref) given by @@ -137,7 +141,11 @@ the [`BuresWassersteinMetric`](@ref) given by \log_p(q) = M(M^{-1}pM^{-1}q)^{\frac{1}{2}} + (qM^{-1}pM^{-1})^{\frac{1}{2}}M - 2 p. ``` """ -log(::MetricManifold{ℝ,SymmetricPositiveDefinite,GeneralizedBuresWassersteinMetric}, p, q) +log( + ::MetricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, + p, + q, +) function log!( M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,<:GeneralizedBuresWassersteinMetric}, diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 703dc0b241..6ac02ede73 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -35,12 +35,9 @@ where $x$ and $y$ are the cholesky factors of $p$ and $q$, respectively, $⌊\cdot⌋$ denbotes the strictly lower triangular matrix of its argument, and $\lVert\cdot\rVert_{\mathrm{F}}$ the Frobenius norm. """ -function distance( - ::MetricManifold{ℝ,SymmetricPositiveDefinite{N},LogCholeskyMetric}, - p, - q, -) where {N} - return distance(CholeskySpace{N}(), cholesky(p).L, cholesky(q).L) +function distance(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, p, q) + N = get_n(M.manifold) + return distance(CholeskySpace(N), cholesky(p).L, cholesky(q).L) end @doc raw""" @@ -60,28 +57,24 @@ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2} """ exp(::MetricManifold{ℝ,SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any...) -function exp!( - ::MetricManifold{ℝ,SymmetricPositiveDefinite{N},LogCholeskyMetric}, - q, - p, - X, -) where {N} +function exp!(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, q, p, X) + N = get_n(M.manifold) (y, W) = spd_to_cholesky(p, X) - z = exp(CholeskySpace{N}(), y, W) + z = exp(CholeskySpace(N), y, W) return copyto!(q, z * z') end function exp!( - M::MetricManifold{ℝ,SymmetricPositiveDefinite{N},LogCholeskyMetric}, + M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, q, p, X, t::Number, -) where {N} +) return exp!(M, q, p, t * X) end @doc raw""" - inner(M::MetricManifold{LogCholeskyMetric,ℝ,SymmetricPositiveDefinite}, p, X, Y) + inner(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, p, X, Y) Compute the inner product of two matrices `X`, `Y` in the tangent space of `p` on the [`SymmetricPositiveDefinite`](@ref) manifold `M`, as @@ -96,15 +89,11 @@ $z$ is the cholesky factor of $p$, $a_z(W) = z (z^{-1}Wz^{-\mathrm{T}})_{\frac{1}{2}}$, and $(\cdot)_\frac{1}{2}$ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2}$ """ -function inner( - ::MetricManifold{ℝ,SymmetricPositiveDefinite{N},LogCholeskyMetric}, - p, - X, - Y, -) where {N} +function inner(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, p, X, Y) + N = get_n(M.manifold) (z, Xz) = spd_to_cholesky(p, X) (z, Yz) = spd_to_cholesky(p, z, Y) - return inner(CholeskySpace{N}(), z, Xz, Yz) + return inner(CholeskySpace(N), z, Xz, Yz) end """ @@ -116,7 +105,7 @@ is not a flat manifold. is_flat(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}) = false @doc raw""" - log(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, p, q) + log(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, p, q) Compute the logarithmic map on [`SymmetricPositiveDefinite`](@ref) `M` with respect to the [`LogCholeskyMetric`](@ref) emanating from `p` to `q`. @@ -129,21 +118,17 @@ of $q$ and the just mentioned logarithmic map is the one on [`CholeskySpace`](@r """ log(::MetricManifold{ℝ,SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any...) -function log!( - ::MetricManifold{ℝ,SymmetricPositiveDefinite{N},LogCholeskyMetric}, - X, - p, - q, -) where {N} +function log!(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, X, p, q) + N = get_n(M.manifold) x = cholesky(p).L y = cholesky(q).L - log!(CholeskySpace{N}(), X, x, y) + log!(CholeskySpace(N), X, x, y) return tangent_cholesky_to_tangent_spd!(x, X) end @doc raw""" vector_transport_to( - M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}, + M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, p, X, q, @@ -163,21 +148,22 @@ transport on [`CholeskySpace`](@ref) from $x$ to $y$. The formula hear reads ```` """ parallel_transport_to( - ::MetricManifold{ℝ,SymmetricPositiveDefinite,LogCholeskyMetric}, + ::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any, ::Any, ::Any, ) function parallel_transport_to!( - ::MetricManifold{ℝ,SymmetricPositiveDefinite{N},LogCholeskyMetric}, + M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, Y, p, X, q, -) where {N} +) + N = get_n(M.manifold) y = cholesky(q).L (x, W) = spd_to_cholesky(p, X) - parallel_transport_to!(CholeskySpace{N}(), Y, x, W, y) + parallel_transport_to!(CholeskySpace(N), Y, x, W, y) return tangent_cholesky_to_tangent_spd!(y, Y) end diff --git a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl index 579facd4cd..ad579dffc1 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogEuclidean.jl @@ -7,7 +7,7 @@ into the Lie Algebra, i.e. performing a matrix logarithm beforehand. struct LogEuclideanMetric <: RiemannianMetric end @doc raw""" - distance(M::MetricManifold{SymmetricPositiveDefinite{N},LogEuclideanMetric}, p, q) + distance(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogEuclideanMetric}, p, q) Compute the distance on the [`SymmetricPositiveDefinite`](@ref) manifold between `p` and `q` as a [`MetricManifold`](@ref) with [`LogEuclideanMetric`](@ref). @@ -20,11 +20,7 @@ The formula reads where $\operatorname{Log}$ denotes the matrix logarithm and $\lVert\cdot\rVert_{\mathrm{F}}$ denotes the matrix Frobenius norm. """ -function distance( - ::MetricManifold{ℝ,SymmetricPositiveDefinite{N},LogEuclideanMetric}, - p, - q, -) where {N} +function distance(::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogEuclideanMetric}, p, q) return norm(log(Symmetric(p)) - log(Symmetric(q))) end diff --git a/test/groups/special_orthogonal.jl b/test/groups/special_orthogonal.jl index 9f3f0faa06..e8a862d4b3 100644 --- a/test/groups/special_orthogonal.jl +++ b/test/groups/special_orthogonal.jl @@ -3,10 +3,10 @@ include("group_utils.jl") @testset "Special Orthogonal group" begin for n in [2, 3] - G = SpecialOrthogonal(n) - @test repr(G) == "SpecialOrthogonal($n)" + G = SpecialOrthogonal(n; parameter=:type) + @test repr(G) == "SpecialOrthogonal($n; parameter=:type)" M = base_manifold(G) - @test M === Rotations(n) + @test M === Rotations(n; parameter=:type) p = Matrix(I, n, n) @test is_default_metric(MetricManifold(G, EuclideanMetric())) @test is_flat(G) == (n == 2) diff --git a/test/groups/translation_group.jl b/test/groups/translation_group.jl index f3ea6b0285..58c3520705 100644 --- a/test/groups/translation_group.jl +++ b/test/groups/translation_group.jl @@ -6,8 +6,8 @@ include("group_utils.jl") G = TranslationGroup(2, 3) @test repr(G) == "TranslationGroup(2, 3; field = ℝ)" @test repr(TranslationGroup(2, 3; field=ℂ)) == "TranslationGroup(2, 3; field = ℂ)" - @test repr(TranslationGroup(2, 3; field=ℂ, parameter=:field)) == - "TranslationGroup(2, 3; field = ℂ, parameter = :field)" + @test repr(TranslationGroup(2, 3; field=ℂ, parameter=:type)) == + "TranslationGroup(2, 3; field = ℂ, parameter = :type)" @test has_invariant_metric(G, LeftForwardAction()) @test has_invariant_metric(G, RightBackwardAction()) diff --git a/test/manifolds/stiefel.jl b/test/manifolds/stiefel.jl index 2daa966b0b..3f458b328c 100644 --- a/test/manifolds/stiefel.jl +++ b/test/manifolds/stiefel.jl @@ -2,10 +2,10 @@ include("../utils.jl") @testset "Stiefel" begin @testset "Real" begin - M = Stiefel(3, 2) + M = Stiefel(3, 2; parameter=:type) M2 = MetricManifold(M, EuclideanMetric()) @testset "Basics" begin - @test repr(M) == "Stiefel(3, 2, ℝ)" + @test repr(M) == "Stiefel(3, 2, ℝ; parameter=:type)" p = [1.0 0.0; 0.0 1.0; 0.0 0.0] @test is_default_metric(M, EuclideanMetric()) @test representation_size(M) == (3, 2) @@ -206,9 +206,9 @@ include("../utils.jl") end @testset "Complex" begin - M = Stiefel(3, 2, ℂ) + M = Stiefel(3, 2, ℂ; parameter=:type) @testset "Basics" begin - @test repr(M) == "Stiefel(3, 2, ℂ)" + @test repr(M) == "Stiefel(3, 2, ℂ; parameter=:type)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 8 @test !is_flat(M) From 7ee384a48950cc3e21d80a6298ea2842f6aa1b41 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 6 Sep 2023 22:39:00 +0200 Subject: [PATCH 14/81] remove `ProductRepr`, bugfixing, optional static reworking --- NEWS.md | 4 +- benchmark/benchmarks.jl | 10 +- docs/src/features/utilities.md | 1 - docs/src/manifolds/power.md | 4 +- docs/src/manifolds/product.md | 2 +- docs/src/manifolds/vector_bundle.md | 4 +- src/Manifolds.jl | 2 +- src/groups/product_group.jl | 43 +- src/groups/rotation_translation_action.jl | 121 ++--- src/groups/semidirect_product_group.jl | 4 +- src/groups/special_euclidean.jl | 8 +- src/manifolds/DirectSumBundle.jl | 7 +- src/manifolds/Fiber.jl | 12 +- src/manifolds/FiberBundle.jl | 51 -- src/manifolds/Flag.jl | 2 +- src/manifolds/GeneralizedGrassmann.jl | 55 +- src/manifolds/GeneralizedStiefel.jl | 56 +- src/manifolds/PowerManifold.jl | 6 - src/manifolds/ProductManifold.jl | 527 +++++++----------- src/manifolds/VectorBundle.jl | 8 - src/product_representations.jl | 170 ------ test/groups/metric.jl | 60 +-- test/groups/product_group.jl | 160 +++--- test/groups/rotation_translation_action.jl | 2 +- test/groups/semidirect_product_group.jl | 72 ++- test/manifolds/power_manifold.jl | 56 +- test/manifolds/product_manifold.jl | 598 ++++++++------------- test/manifolds/vector_bundle.jl | 49 +- 28 files changed, 780 insertions(+), 1314 deletions(-) diff --git a/NEWS.md b/NEWS.md index e7ba48b346..49bd67e5c8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,6 +22,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `CholeskySpace{N}`, - `Euclidean`, - `GeneralUnitaryMultiplicationGroup{n}`, + - `GeneralizedGrassmann{n,k}`, + - `GeneralizedStiefel{n,k}`, - `Grassmann{n,k}`, - `Orthogonal{n}`, - `SpecialOrthogonal{n}`, @@ -69,7 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 end ``` -- Argument order for type alias `RotationActionOnVector`: most often dispatched on argument is now first. +- Argument order for type aliases `RotationActionOnVector` and `RotationTranslationActionOnVector`: most often dispatched on argument is now first. ### Removed diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index b6d7d98165..8bf4cb3340 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -141,7 +141,7 @@ function add_manifold_benchmarks() inverse_retraction_methods=inverse_retraction_methods_rot, ) - pts_prod_mpoints = [Manifolds.ProductRepr(p[1], p[2]) for p in zip(pts_s2, pts_r2)] + pts_prod_mpoints = [ArrayPartition(p[1], p[2]) for p in zip(pts_s2, pts_r2)] add_manifold( m_prod, pts_prod_mpoints, @@ -155,14 +155,14 @@ function add_manifold_benchmarks() TB = TangentBundle(s2) pts_tb = [ - ProductRepr(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, -1.0, -1.0])), - ProductRepr(convert(T, [0.0, 1.0, 0.0]), convert(T, [2.0, 0.0, 1.0])), - ProductRepr(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, 2.0, -1.0])), + ArrayPartition(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, -1.0, -1.0])), + ArrayPartition(convert(T, [0.0, 1.0, 0.0]), convert(T, [2.0, 0.0, 1.0])), + ArrayPartition(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, 2.0, -1.0])), ] add_manifold( TB, pts_tb, - "Tangent bundle of S² using MVectors, ProductRepr"; + "Tangent bundle of S² using MVectors, ArrayPartition"; test_tangent_vector_broadcasting=false, ) end diff --git a/docs/src/features/utilities.md b/docs/src/features/utilities.md index 988feaa0f9..bd1663df5f 100644 --- a/docs/src/features/utilities.md +++ b/docs/src/features/utilities.md @@ -24,7 +24,6 @@ The following functions are of interest for extending and using the [`ProductMan ```@docs submanifold_component submanifold_components -ProductRepr ``` ## Specific exception types diff --git a/docs/src/manifolds/power.md b/docs/src/manifolds/power.md index 7d5f898f80..1326cf5be8 100644 --- a/docs/src/manifolds/power.md +++ b/docs/src/manifolds/power.md @@ -98,8 +98,8 @@ N = 5 GN = PowerManifold(G, NestedReplacingPowerRepresentation(), N) q = [1.0 0.0; 0.0 1.0] -p1 = [ProductRepr(SVector{2,Float64}([i - 0.1, -i]), SMatrix{2,2,Float64}(exp(R2, q, hat(R2, q, i)))) for i in 1:N] -p2 = [ProductRepr(SVector{2,Float64}([i - 0.1, -i]), SMatrix{2,2,Float64}(exp(R2, q, hat(R2, q, -i)))) for i in 1:N] +p1 = [ArrayPartition(SVector{2,Float64}([i - 0.1, -i]), SMatrix{2,2,Float64}(exp(R2, q, hat(R2, q, i)))) for i in 1:N] +p2 = [ArrayPartition(SVector{2,Float64}([i - 0.1, -i]), SMatrix{2,2,Float64}(exp(R2, q, hat(R2, q, -i)))) for i in 1:N] X = similar(p1); diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index 99c37988dc..46546ee098 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,7 +1,7 @@ # [Product manifold](@id ProductManifoldSection) Product manifold $\mathcal M = \mathcal{M}_1 × \mathcal{M}_2 × … × \mathcal{M}_n$ of manifolds $\mathcal{M}_1, \mathcal{M}_2, …, \mathcal{M}_n$. -Points on the product manifold can be constructed using [`ProductRepr`](@ref) with canonical projections $Π_i : \mathcal{M} → \mathcal{M}_i$ for $i ∈ 1, 2, …, n$ provided by [`submanifold_component`](@ref). +Points on the product manifold can be constructed using `ArrayPartition` (from `RecursiveArrayTools.jl`) with canonical projections $Π_i : \mathcal{M} → \mathcal{M}_i$ for $i ∈ 1, 2, …, n$ provided by [`submanifold_component`](@ref). ```@autodocs Modules = [Manifolds] diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index 306c2d5e62..8de9bb9157 100644 --- a/docs/src/manifolds/vector_bundle.md +++ b/docs/src/manifolds/vector_bundle.md @@ -37,8 +37,8 @@ The following code defines a point on the tangent bundle of the sphere $S^2$ and using Manifolds M = Sphere(2) TB = TangentBundle(M) -p = ProductRepr([1.0, 0.0, 0.0], [0.0, 1.0, 3.0]) -X = ProductRepr([0.0, 1.0, 0.0], [0.0, 0.0, -2.0]) +p = ArrayPartition([1.0, 0.0, 0.0], [0.0, 1.0, 3.0]) +X = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0, -2.0]) ``` An approximation of the exponential in the Sasaki metric using 1000 steps can be calculated as follows. diff --git a/src/Manifolds.jl b/src/Manifolds.jl index f7ebd3a933..4311cd8710 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -669,7 +669,7 @@ export AbstractPowerManifold, QuotientManifold export ProductManifold, EmbeddedManifold export GraphManifold, GraphManifoldType, VertexManifold, EdgeManifold -export ProductRepr, ArrayPartition +export ArrayPartition export ProjectedPointDistribution, TangentBundle, TangentBundleFibers export TangentSpace, TangentSpaceAtPoint, VectorSpaceAtPoint, VectorSpaceType, VectorBundle export VectorBundleFibers diff --git a/src/groups/product_group.jl b/src/groups/product_group.jl index 842dd4af61..19ef5dea1d 100644 --- a/src/groups/product_group.jl +++ b/src/groups/product_group.jl @@ -103,8 +103,8 @@ end inv!(::ProductGroup, q::Identity{ProductOperation}, ::Identity{ProductOperation}) = q _compose(G::ProductGroup, p, q) = _compose(G.manifold, p, q) -function _compose(M::ProductManifold, p::ProductRepr, q::ProductRepr) - return ProductRepr( +function _compose(M::ProductManifold, p::ArrayPartition, q::ArrayPartition) + return ArrayPartition( map( compose, M.manifolds, @@ -126,17 +126,6 @@ function _compose!(M::ProductManifold, x, p, q) return x end -function translate(M::ProductGroup, p::ProductRepr, q::ProductRepr, conv::ActionDirection) - return ProductRepr( - map( - translate, - M.manifold.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - repeated(conv), - )..., - ) -end function translate( M::ProductGroup, p::ArrayPartition, @@ -168,7 +157,7 @@ end function inverse_translate(G::ProductGroup, p, q, conv::ActionDirection) M = G.manifold - return ProductRepr( + return ArrayPartition( map( inverse_translate, M.manifolds, @@ -222,7 +211,7 @@ end function inverse_translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) M = G.manifold - return ProductRepr( + return ArrayPartition( map( inverse_translate_diff, M.manifolds, @@ -248,16 +237,6 @@ function inverse_translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirect return Y end -function Base.exp(M::ProductGroup, p::Identity{ProductOperation}, X::ProductRepr) - return ProductRepr( - map( - exp, - M.manifold.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - )..., - ) -end function Base.exp(M::ProductGroup, p::Identity{ProductOperation}, X::ArrayPartition) return ArrayPartition( map( @@ -291,16 +270,6 @@ function exp_lie!(G::ProductGroup, q, X) return q end -function Base.log(M::ProductGroup, p::Identity{ProductOperation}, q::ProductRepr) - return ProductRepr( - map( - log, - M.manifold.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - )..., - ) -end function Base.log(M::ProductGroup, p::Identity{ProductOperation}, q::ArrayPartition) return ArrayPartition( map( @@ -338,7 +307,7 @@ function log_lie!(G::ProductGroup, X, q::Identity{ProductOperation}) end Base.@propagate_inbounds function Base.getindex( - p::Union{ProductRepr,ArrayPartition}, + p::ArrayPartition, M::ProductGroup, i::Union{Integer,Colon,AbstractVector,Val}, ) @@ -346,7 +315,7 @@ Base.@propagate_inbounds function Base.getindex( end Base.@propagate_inbounds function Base.setindex!( - q::Union{ProductRepr,ArrayPartition}, + q::ArrayPartition, p, M::ProductGroup, i::Union{Integer,Colon,AbstractVector,Val}, diff --git a/src/groups/rotation_translation_action.jl b/src/groups/rotation_translation_action.jl index 4c588b948c..b753603a76 100644 --- a/src/groups/rotation_translation_action.jl +++ b/src/groups/rotation_translation_action.jl @@ -12,9 +12,9 @@ Left forward action corresponds to active transformations while right forward ac can be identified with passive transformations for a particular choice of a basis. """ struct RotationTranslationAction{ + TAD<:ActionDirection, TM<:AbstractManifold, TSE<:SpecialEuclidean, - TAD<:ActionDirection, } <: AbstractGroupAction{TAD} manifold::TM SEn::TSE @@ -25,27 +25,27 @@ function RotationTranslationAction( SEn::SpecialEuclidean, ::TAD=LeftForwardAction(), ) where {TAD<:ActionDirection} - return RotationTranslationAction{typeof(M),typeof(SEn),TAD}(M, SEn) + return RotationTranslationAction{TAD,typeof(M),typeof(SEn)}(M, SEn) end function Base.show(io::IO, A::RotationTranslationAction) return print(io, "RotationTranslationAction($(A.manifold), $(A.SEn), $(direction(A)))") end -const RotationTranslationActionOnVector{N,F,TAD} = RotationTranslationAction{ - <:Union{Euclidean{Tuple{N},F},TranslationGroup{Tuple{N},F}}, - SpecialEuclidean{N}, +const RotationTranslationActionOnVector{TAD,𝔽,TE,TSE} = RotationTranslationAction{ TAD, -} where {TAD<:ActionDirection} + <:Union{Euclidean{TE,𝔽},TranslationGroup{TE,𝔽}}, + SpecialEuclidean{TSE}, +} where {TAD<:ActionDirection,𝔽,TE,TSE} base_group(A::RotationTranslationAction) = A.SEn group_manifold(A::RotationTranslationAction) = A.manifold function switch_direction( - A::RotationTranslationAction{TM,TSO,TAD}, + A::RotationTranslationAction{TAD}, ::LeftRightSwitch=LeftRightSwitch(), -) where {TM<:AbstractManifold,TSO<:SpecialEuclidean,TAD<:ActionDirection} +) where {TAD<:ActionDirection} return RotationTranslationAction( A.manifold, A.SEn, @@ -53,175 +53,171 @@ function switch_direction( ) end -function apply( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, - a::ArrayPartition, - p, -) where {N,F} +function apply(::RotationTranslationActionOnVector{LeftForwardAction}, a::ArrayPartition, p) return a.x[2] * p + a.x[1] end function apply( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, - a::SpecialEuclideanIdentity{N}, + ::RotationTranslationActionOnVector{LeftForwardAction}, + a::SpecialEuclideanIdentity, p, -) where {N,F} +) return p end function apply( - ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + ::RotationTranslationActionOnVector{RightForwardAction}, a::ArrayPartition, p, -) where {N,F} +) return a.x[2] \ (p - a.x[1]) end function apply( - ::RotationTranslationActionOnVector{N,F,RightForwardAction}, - a::SpecialEuclideanIdentity{N}, + ::RotationTranslationActionOnVector{RightForwardAction}, + a::SpecialEuclideanIdentity, p, -) where {N,F} +) return p end function apply!( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftForwardAction}, q, a::ArrayPartition, p, -) where {N,F} +) mul!(q, a.x[2], p) q .+= a.x[1] return q end function apply!( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftForwardAction}, q, - a::SpecialEuclideanIdentity{N}, + a::SpecialEuclideanIdentity, p, -) where {N,F} +) copyto!(q, p) return q end function inverse_apply( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftForwardAction}, a::ArrayPartition, p, -) where {N,F} +) return a.x[2] \ (p - a.x[1]) end function inverse_apply( - ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + ::RotationTranslationActionOnVector{RightForwardAction}, a::ArrayPartition, p, -) where {N,F} +) return a.x[2] * p + a.x[1] end function apply_diff( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftForwardAction}, a::ArrayPartition, p, X, -) where {N,F} +) return a.x[2] * X end function apply_diff( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, - ::SpecialEuclideanIdentity{N}, + ::RotationTranslationActionOnVector{LeftForwardAction}, + ::SpecialEuclideanIdentity, p, X, -) where {N,F} +) return X end function apply_diff( - ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + ::RotationTranslationActionOnVector{RightForwardAction}, a::ArrayPartition, p, X, -) where {N,F} +) return a.x[2] \ X end function apply_diff( - ::RotationTranslationActionOnVector{N,F,RightForwardAction}, - a::SpecialEuclideanIdentity{N}, + ::RotationTranslationActionOnVector{RightForwardAction}, + a::SpecialEuclideanIdentity, p, X, -) where {N,F} +) return X end function apply_diff!( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftForwardAction}, Y, a::ArrayPartition, p, X, -) where {N,F} +) return mul!(Y, a.x[2], X) end function apply_diff!( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftForwardAction}, Y, - a::SpecialEuclideanIdentity{N}, + a::SpecialEuclideanIdentity, p, X, -) where {N,F} +) return copyto!(Y, X) end function apply_diff!( - ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + ::RotationTranslationActionOnVector{RightForwardAction}, Y, a::ArrayPartition, p, X, -) where {N,F} +) Y .= a.x[2] \ X return Y end function apply_diff!( - ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + ::RotationTranslationActionOnVector{RightForwardAction}, Y, - a::SpecialEuclideanIdentity{N}, + a::SpecialEuclideanIdentity, p, X, -) where {N,F} +) return copyto!(Y, X) end function apply_diff_group( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, - ::SpecialEuclideanIdentity{N}, + ::RotationTranslationActionOnVector{LeftForwardAction}, + ::SpecialEuclideanIdentity, X, p, -) where {N,F} +) return X.x[2] * p end function apply_diff_group!( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftForwardAction}, Y, - ::SpecialEuclideanIdentity{N}, + ::SpecialEuclideanIdentity, X::ArrayPartition, p, -) where {N,F} +) Y .= X.x[2] * p return Y end function inverse_apply_diff( - ::RotationTranslationActionOnVector{N,F,LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftForwardAction}, a::ArrayPartition, p, X, -) where {N,F} +) return a.x[2] \ X end function inverse_apply_diff( - ::RotationTranslationActionOnVector{N,F,RightForwardAction}, + ::RotationTranslationActionOnVector{RightForwardAction}, a::ArrayPartition, p, X, -) where {N,F} +) return a.x[2] * X end @@ -297,10 +293,11 @@ Compute optimal alignment of `p` to `q` under the forward left [`ColumnwiseSpeci The algorithm, in sequence, computes optimal translation and optimal rotation """ function optimal_alignment( - A::LeftColumnwiseSpecialEuclideanAction{<:AbstractManifold,<:SpecialEuclidean{N}}, + A::LeftColumnwiseSpecialEuclideanAction{<:AbstractManifold,<:SpecialEuclidean}, p, q, -) where {N} +) + N = get_n(A.SE) tr_opt = mean(q; dims=1) - mean(p; dims=1) p_moved = p + tr_opt diff --git a/src/groups/semidirect_product_group.jl b/src/groups/semidirect_product_group.jl index f144bc7ebb..5d850fed4e 100644 --- a/src/groups/semidirect_product_group.jl +++ b/src/groups/semidirect_product_group.jl @@ -249,7 +249,7 @@ function isapprox( end Base.@propagate_inbounds function Base.getindex( - p::Union{ProductRepr,ArrayPartition}, + p::ArrayPartition, M::SemidirectProductGroup, i::Union{Integer,Colon,AbstractVector,Val}, ) @@ -257,7 +257,7 @@ Base.@propagate_inbounds function Base.getindex( end Base.@propagate_inbounds function Base.setindex!( - q::Union{ProductRepr,ArrayPartition}, + q::ArrayPartition, p, M::SemidirectProductGroup, i::Union{Integer,Colon,AbstractVector,Val}, diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 44e189bfad..ff4438c6cc 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -584,20 +584,14 @@ function log!(M::SpecialEuclideanManifold, X::AbstractMatrix, p, q) 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 algebra. For the matrix representation (which can be obtained using [`screw_matrix`](@ref)) -the formula is ``[X, Y] = XY-YX``, while in the [`ProductRepr`](@ref) representation the +the formula is ``[X, Y] = XY-YX``, while in the `ArrayPartition` representation the formula reads ``[X, Y] = [(t_1, R_1), (t_2, R_2)] = (R_1 t_2 - R_2 t_1, R_1 R_2 - R_2 R_1)``. """ -function lie_bracket(G::SpecialEuclidean, X::ProductRepr, Y::ProductRepr) - nX, hX = submanifold_components(G, X) - 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) diff --git a/src/manifolds/DirectSumBundle.jl b/src/manifolds/DirectSumBundle.jl index 6cd05db62a..9482d4462e 100644 --- a/src/manifolds/DirectSumBundle.jl +++ b/src/manifolds/DirectSumBundle.jl @@ -40,8 +40,11 @@ function MultitangentBundleFibers{N}(M::AbstractManifold) where {N} return VectorBundleFibers(DirectSumType(ntuple(f -> TangentSpace, N)), M) end -const MultitangentSpaceAtPoint{N,M} = - VectorSpaceAtPoint{𝔽,MultitangentBundleFibers{N,M}} where {𝔽,M<:AbstractManifold{𝔽}} +const MultitangentSpaceAtPoint{𝔽,N,M} = VectorSpaceAtPoint{ + 𝔽, + M, + DirectSumType{NTuple{N,TangentSpaceType}}, +} where {𝔽,M<:AbstractManifold{𝔽}} function MultitangentSpaceAtPoint{N}(M::AbstractManifold, p) where {N} return VectorSpaceAtPoint(MultitangentBundleFibers{N}(M), p) end diff --git a/src/manifolds/Fiber.jl b/src/manifolds/Fiber.jl index caf03a9d1a..e150392205 100644 --- a/src/manifolds/Fiber.jl +++ b/src/manifolds/Fiber.jl @@ -102,14 +102,14 @@ struct FiberAtPoint{𝔽,TFiber<:BundleFibers{<:FiberType,<:AbstractManifold{ end """ - VectorSpaceAtPoint{𝔽,TFiber} + VectorSpaceAtPoint{𝔽,M,TFiber} Alias for [`FiberAtPoint`](@ref) when the fiber is a vector space. """ -const VectorSpaceAtPoint{𝔽,TFiber} = FiberAtPoint{ +const VectorSpaceAtPoint{𝔽,M,TSpaceType} = FiberAtPoint{ 𝔽, - TFiber, -} where {𝔽,TFiber<:VectorBundleFibers{<:FiberType,<:AbstractManifold{𝔽}}} + VectorBundleFibers{TSpaceType,M}, +} where {𝔽,M<:AbstractManifold{𝔽},TSpaceType<:VectorSpaceType} function VectorSpaceAtPoint(M::AbstractManifold, fiber::VectorSpaceFiberType, p) return FiberAtPoint(BundleFibers(fiber, M), p) @@ -122,7 +122,7 @@ VectorSpaceAtPoint(fiber::BundleFibers{<:VectorSpaceFiberType}, p) = FiberAtPoin Alias for [`VectorSpaceAtPoint`](@ref) for the tangent space at a point. """ const TangentSpaceAtPoint{𝔽,M} = - VectorSpaceAtPoint{𝔽,TangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} + VectorSpaceAtPoint{𝔽,M,TangentSpaceType} where {𝔽,M<:AbstractManifold{𝔽}} """ TangentSpaceAtPoint(M::AbstractManifold, p) @@ -397,6 +397,6 @@ at point `M.point`. """ zero_vector(::TangentSpaceAtPoint, ::Any...) -function zero_vector!(M::TangentSpaceAtPoint, X, p) +function zero_vector!(M::VectorSpaceAtPoint, X, p) return zero_vector!(M.fiber.manifold, X, M.point) end diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index 9a926abbf2..b43a741d2c 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -259,23 +259,6 @@ function get_vector!( return Y end -function get_vectors( - M::FiberBundle, - p::ProductRepr, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, -) where {𝔽} - xp1 = submanifold_component(p, Val(1)) - zero_m = zero_vector(M.manifold, xp1) - zero_f = zero_vector(M.fiber, xp1) - vs = typeof(ProductRepr(zero_m, zero_f))[] - for bv in get_vectors(M.manifold, xp1, B.data.base_basis) - push!(vs, ProductRepr(bv, zero_f)) - end - for bv in get_vectors(M.fiber, xp1, B.data.fiber_basis) - push!(vs, ProductRepr(zero_m, bv)) - end - return vs -end function get_vectors( M::FiberBundle, p::ArrayPartition, @@ -297,19 +280,6 @@ function get_vectors(M::BundleFibers, p, B::CachedBasis) return get_vectors(M.manifold, p, B) end -""" - getindex(p::ProductRepr, M::FiberBundle, s::Symbol) - p[M::FiberBundle, s] - -Access the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` by -using the symbols `:point` and `:vector` or `:fiber` for the base and vector or fiber -component, respectively. -""" -@inline function Base.getindex(p::ProductRepr, M::FiberBundle, s::Symbol) - (s === :point) && return submanifold_component(M, p, Val(1)) - (s === :vector || s === :fiber) && return submanifold_component(M, p, Val(2)) - return throw(DomainError(s, "unknown component $s on $M.")) -end """ getindex(p::ArrayPartition, M::FiberBundle, s::Symbol) p[M::FiberBundle, s] @@ -355,27 +325,6 @@ function Random.rand!(rng::AbstractRNG, M::FiberBundle, pX; vector_at=nothing) return pX end -""" - setindex!(p::ProductRepr, val, M::FiberBundle, s::Symbol) - p[M::VectorBundle, s] = val - -Set the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` to `val` by -using the symbols `:point` and `:fiber` or `:vector` for the base and fiber or vector -component, respectively. - -!!! note - - The *content* of element of `p` is replaced, not the element itself. -""" -@inline function Base.setindex!(x::ProductRepr, val, M::FiberBundle, s::Symbol) - if s === :point - return copyto!(submanifold_component(M, x, Val(1)), val) - elseif s === :vector || s === :fiber - return copyto!(submanifold_component(M, x, Val(2)), val) - else - throw(DomainError(s, "unknown component $s on $M.")) - end -end """ setindex!(p::ArrayPartition, val, M::FiberBundle, s::Symbol) p[M::VectorBundle, s] = val diff --git a/src/manifolds/Flag.jl b/src/manifolds/Flag.jl index 32f11ca28b..717450c38b 100644 --- a/src/manifolds/Flag.jl +++ b/src/manifolds/Flag.jl @@ -73,7 +73,7 @@ struct Flag{T,dp1} <: AbstractDecoratorManifold{ℝ} size::T end -function Flag(N, ns::Vararg{Int,I}; parameter::Symbol=:field) where {I} +function Flag(N::Int, ns::Vararg{Int,I}; parameter::Symbol=:field) where {I} if ns[1] <= 0 error( "First dimension in the sequence ns must be strictly positive, but is $(ns[1]).", diff --git a/src/manifolds/GeneralizedGrassmann.jl b/src/manifolds/GeneralizedGrassmann.jl index ee6456c951..5b38027eaf 100644 --- a/src/manifolds/GeneralizedGrassmann.jl +++ b/src/manifolds/GeneralizedGrassmann.jl @@ -1,5 +1,5 @@ @doc raw""" - GeneralizedGrassmann{n,k,𝔽} <: AbstractDecoratorManifold{𝔽} + GeneralizedGrassmann{T,𝔽,TB<:AbstractMatrix} <: AbstractDecoratorManifold{𝔽} The generalized Grassmann manifold $\operatorname{Gr}(n,k,B)$ consists of all subspaces spanned by $k$ linear independent vectors $𝔽^n$, where $𝔽 ∈ \{ℝ, ℂ\}$ is either the real- (or complex-) valued vectors. @@ -43,7 +43,8 @@ The manifold is named after Generate the (real-valued) Generalized Grassmann manifold of $n\times k$ dimensional orthonormal matrices with scalar product `B`. """ -struct GeneralizedGrassmann{n,k,𝔽,TB<:AbstractMatrix} <: AbstractDecoratorManifold{𝔽} +struct GeneralizedGrassmann{T,𝔽,TB<:AbstractMatrix} <: AbstractDecoratorManifold{𝔽} + size::T B::TB end @@ -51,9 +52,11 @@ function GeneralizedGrassmann( n::Int, k::Int, B::AbstractMatrix=Matrix{Float64}(I, n, n), - field::AbstractNumbers=ℝ, + 𝔽::AbstractNumbers=ℝ; + parameter::Symbol=:field, ) - return GeneralizedGrassmann{n,k,field,typeof(B)}(B) + size = wrap_type_parameter(parameter, (n, k)) + return GeneralizedGrassmann{typeof(size),𝔽,typeof(B)}(size, B) end active_traits(f, ::GeneralizedGrassmann, args...) = merge_traits(IsEmbeddedManifold()) @@ -93,18 +96,18 @@ function change_metric!(M::GeneralizedGrassmann, Y, ::EuclideanMetric, p, X) end @doc raw""" - check_point(M::GeneralizedGrassmann{n,k,𝔽}, p) + check_point(M::GeneralizedGrassmann, p) Check whether `p` is representing a point on the [`GeneralizedGrassmann`](@ref) `M`, i.e. its a `n`-by-`k` matrix of unitary column vectors with respect to the B inner prudct and of correct `eltype` with respect to `𝔽`. """ -function check_point(M::GeneralizedGrassmann{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} +function check_point(M::GeneralizedGrassmann, p; kwargs...) return nothing # everything already checked in the embedding (generalized Stiefel) end @doc raw""" - check_vector(M::GeneralizedGrassmann{n,k,𝔽}, p, X; kwargs...) + check_vector(M::GeneralizedGrassmann, p, X; kwargs...) Check whether `X` is a tangent vector in the tangent space of `p` on the [`GeneralizedGrassmann`](@ref) `M`, i.e. that `X` is of size and type as well as that @@ -116,7 +119,7 @@ the [`GeneralizedGrassmann`](@ref) `M`, i.e. that `X` is of size and type as wel where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transpose or Hermitian, $\overline{\cdot}$ the (elementwise) complex conjugate, and $0_k$ denotes the $k × k$ zero natrix. """ -function check_vector(M::GeneralizedGrassmann{n,k,𝔽}, p, X; kwargs...) where {n,k,𝔽} +function check_vector(M::GeneralizedGrassmann, p, X; kwargs...) return nothing # everything already checked in the embedding (generalized Stiefel) end @@ -188,9 +191,16 @@ Return true if [`GeneralizedGrassmann`](@ref) `M` is one-dimensional. """ is_flat(M::GeneralizedGrassmann) = manifold_dimension(M) == 1 -function get_embedding(M::GeneralizedGrassmann{N,K,𝔽}) where {N,K,𝔽} - return GeneralizedStiefel(N, K, M.B, 𝔽) +function get_embedding(::GeneralizedGrassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return GeneralizedStiefel(n, k, M.B, 𝔽; parameter=:type) end +function get_embedding(M::GeneralizedGrassmann{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_nk(M) + return GeneralizedStiefel(n, k, M.B, 𝔽) +end + +get_nk(::GeneralizedGrassmann{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) +get_nk(M::GeneralizedGrassmann{Tuple{Int,Int}}) = get_parameter(M.size) @doc raw""" inner(M::GeneralizedGrassmann, p, X, Y) @@ -204,7 +214,7 @@ g_p(X,Y) = \operatorname{tr}(X^{\mathrm{H}}BY), where $\cdot^{\mathrm{H}}$ denotes the complex conjugate transposed or Hermitian. """ -inner(M::GeneralizedGrassmann{n,k}, p, X, Y) where {n,k} = dot(X, M.B, Y) +inner(M::GeneralizedGrassmann, p, X, Y) = dot(X, M.B, Y) function _isapprox(M::GeneralizedGrassmann, p, X, Y; atol=sqrt(max_eps(X, Y)), kwargs...) return isapprox(norm(M, p, X - Y), 0; atol=atol, kwargs...) @@ -254,7 +264,8 @@ Return the dimension of the [`GeneralizedGrassmann(n,k,𝔽)`](@ref) manifold `M where $\dim_ℝ 𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ -function manifold_dimension(::GeneralizedGrassmann{n,k,𝔽}) where {n,k,𝔽} +function manifold_dimension(M::GeneralizedGrassmann{<:Any,𝔽}) where {𝔽} + n, k = get_nk(M) return k * (n - k) * real_dimension(𝔽) end @@ -270,7 +281,7 @@ end Compute the Riemannian [`mean`](@ref mean(M::AbstractManifold, args...)) of `x` using [`GeodesicInterpolationWithinRadius`](@ref). """ -mean(::GeneralizedGrassmann{n,k} where {n,k}, ::Any...) +mean(::GeneralizedGrassmann, ::Any...) function default_estimation_method(::GeneralizedGrassmann, ::typeof(mean)) return GeodesicInterpolationWithinRadius(π / 4) @@ -331,11 +342,12 @@ rand(::GeneralizedGrassmann; σ::Real=1.0) function Random.rand!( rng::AbstractRNG, - M::GeneralizedGrassmann{n,k,ℝ}, + M::GeneralizedGrassmann{<:Any,ℝ}, pX; vector_at=nothing, σ::Real=one(real(eltype(pX))), -) where {n,k} +) + n, k = get_nk(M) if vector_at === nothing A = σ * randn(rng, eltype(pX), n, k) project!(M, pX, Matrix(qr(A).Q)) @@ -348,12 +360,12 @@ function Random.rand!( end @doc raw""" - representation_size(M::GeneralizedGrassmann{n,k}) + representation_size(M::GeneralizedGrassmann) Return the represenation size or matrix dimension of a point on the [`GeneralizedGrassmann`](@ref) `M`, i.e. $(n,k)$ for both the real-valued and the complex value case. """ -@generated representation_size(::GeneralizedGrassmann{n,k}) where {n,k} = (n, k) +representation_size(M::GeneralizedGrassmann) = get_nk(M) @doc raw""" retract(M::GeneralizedGrassmann, p, X, ::PolarRetraction) @@ -375,7 +387,14 @@ function retract_project!(M::GeneralizedGrassmann, q, p, X, t::Number) return q end -function Base.show(io::IO, M::GeneralizedGrassmann{n,k,𝔽}) where {n,k,𝔽} +function Base.show( + io::IO, + M::GeneralizedGrassmann{TypeParameter{Tuple{n,k}},𝔽}, +) where {n,k,𝔽} + return print(io, "GeneralizedGrassmann($(n), $(k), $(M.B), $(𝔽); parameter=:type)") +end +function Base.show(io::IO, M::GeneralizedGrassmann{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_nk(M) return print(io, "GeneralizedGrassmann($(n), $(k), $(M.B), $(𝔽))") end diff --git a/src/manifolds/GeneralizedStiefel.jl b/src/manifolds/GeneralizedStiefel.jl index 755284c41f..3f66415f2a 100644 --- a/src/manifolds/GeneralizedStiefel.jl +++ b/src/manifolds/GeneralizedStiefel.jl @@ -1,5 +1,5 @@ @doc raw""" - GeneralizedStiefel{n,k,𝔽,B} <: AbstractDecoratorManifold{𝔽} + GeneralizedStiefel{T,𝔽,B} <: AbstractDecoratorManifold{𝔽} The Generalized Stiefel manifold consists of all $n\times k$, $n\geq k$ orthonormal matrices w.r.t. an arbitrary scalar product with symmetric positive definite matrix @@ -35,7 +35,8 @@ The manifold is named after Generate the (real-valued) Generalized Stiefel manifold of $n\times k$ dimensional orthonormal matrices with scalar product `B`. """ -struct GeneralizedStiefel{n,k,𝔽,TB<:AbstractMatrix} <: AbstractDecoratorManifold{𝔽} +struct GeneralizedStiefel{T,𝔽,TB<:AbstractMatrix} <: AbstractDecoratorManifold{𝔽} + size::T B::TB end @@ -43,9 +44,11 @@ function GeneralizedStiefel( n::Int, k::Int, B::AbstractMatrix=Matrix{Float64}(I, n, n), - 𝔽::AbstractNumbers=ℝ, + 𝔽::AbstractNumbers=ℝ; + parameter::Symbol=:field, ) - return GeneralizedStiefel{n,k,𝔽,typeof(B)}(B) + size = wrap_type_parameter(parameter, (n, k)) + return GeneralizedStiefel{typeof(size),𝔽,typeof(B)}(size, B) end active_traits(f, ::GeneralizedStiefel, args...) = merge_traits(IsEmbeddedManifold()) @@ -58,7 +61,7 @@ i.e. that it has the right [`AbstractNumbers`](https://juliamanifolds.github.io/ is (approximately) the identity, where $\cdot^{\mathrm{H}}$ is the complex conjugate transpose. The settings for approximately can be set with `kwargs...`. """ -function check_point(M::GeneralizedStiefel{n,k,𝔽}, p; kwargs...) where {n,k,𝔽} +function check_point(M::GeneralizedStiefel, p; kwargs...) c = p' * M.B * p if !isapprox(c, one(c); kwargs...) return DomainError( @@ -70,10 +73,10 @@ function check_point(M::GeneralizedStiefel{n,k,𝔽}, p; kwargs...) where {n,k, end # overwrite passing to embedding -function check_size(M::GeneralizedStiefel{n,k,𝔽}, p) where {n,k,𝔽} +function check_size(M::GeneralizedStiefel, p) return check_size(get_embedding(M), p) #avoid embed, since it uses copyto! end -function check_size(M::GeneralizedStiefel{n,k,𝔽}, p, X) where {n,k,𝔽} +function check_size(M::GeneralizedStiefel, p, X) return check_size(get_embedding(M), p, X) #avoid embed, since it uses copyto! end @@ -86,7 +89,7 @@ Check whether `X` is a valid tangent vector at `p` on the [`GeneralizedStiefel`] it (approximately) holds that $p^{\mathrm{H}}BX + \overline{X^{\mathrm{H}}Bp} = 0$, where `kwargs...` is passed to the `isapprox`. """ -function check_vector(M::GeneralizedStiefel{n,k,𝔽}, p, X; kwargs...) where {n,k,𝔽} +function check_vector(M::GeneralizedStiefel, p, X; kwargs...) if !isapprox(p' * M.B * X, -conj(X' * M.B * p); kwargs...) return DomainError( norm(p' * M.B * X + conj(X' * M.B * p)), @@ -96,7 +99,16 @@ function check_vector(M::GeneralizedStiefel{n,k,𝔽}, p, X; kwargs...) where {n return nothing end -get_embedding(::GeneralizedStiefel{N,K,𝔽}) where {N,K,𝔽} = Euclidean(N, K; field=𝔽) +function get_embedding(::GeneralizedStiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return Euclidean(n, k; field=𝔽, parameter=:type) +end +function get_embedding(M::GeneralizedStiefel{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_nk(M) + return Euclidean(n, k; field=𝔽) +end + +get_nk(::GeneralizedStiefel{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) +get_nk(M::GeneralizedStiefel{Tuple{Int,Int}}) = get_parameter(M.size) @doc raw""" inner(M::GeneralizedStiefel, p, X, Y) @@ -133,14 +145,21 @@ The dimension is given by \end{aligned} ```` """ -function manifold_dimension(::GeneralizedStiefel{n,k,ℝ}) where {n,k} +function manifold_dimension(M::GeneralizedStiefel{<:Any,ℝ}) + n, k = get_nk(M) return n * k - div(k * (k + 1), 2) end -manifold_dimension(::GeneralizedStiefel{n,k,ℂ}) where {n,k} = 2 * n * k - k * k -manifold_dimension(::GeneralizedStiefel{n,k,ℍ}) where {n,k} = 4 * n * k - k * (2k - 1) +function manifold_dimension(M::GeneralizedStiefel{<:Any,ℂ}) + n, k = get_nk(M) + return 2 * n * k - k * k +end +function manifold_dimension(M::GeneralizedStiefel{<:Any,ℍ}) + n, k = get_nk(M) + return 4 * n * k - k * (2k - 1) +end @doc raw""" - project(M::GeneralizedStiefel,p) + project(M::GeneralizedStiefel, p) Project `p` from the embedding onto the [`GeneralizedStiefel`](@ref) `M`, i.e. compute `q` as the polar decomposition of $p$ such that $q^{\mathrm{H}}Bq$ is the identity, @@ -194,11 +213,12 @@ rand(::GeneralizedStiefel; σ::Real=1.0) function Random.rand!( rng::AbstractRNG, - M::GeneralizedStiefel{n,k,ℝ}, + M::GeneralizedStiefel{<:Any,ℝ}, pX; vector_at=nothing, σ::Real=one(real(eltype(pX))), -) where {n,k} +) + n, k = get_nk(M) if vector_at === nothing A = σ * randn(rng, eltype(pX), n, k) project!(M, pX, Matrix(qr(A).Q)) @@ -237,6 +257,10 @@ function retract_project!(M::GeneralizedStiefel, q, p, X, t::Number) return q end -function Base.show(io::IO, M::GeneralizedStiefel{n,k,𝔽}) where {n,k,𝔽} +function Base.show(io::IO, M::GeneralizedStiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return print(io, "GeneralizedStiefel($(n), $(k), $(M.B), $(𝔽); parameter=:type)") +end +function Base.show(io::IO, M::GeneralizedStiefel{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_nk(M) return print(io, "GeneralizedStiefel($(n), $(k), $(M.B), $(𝔽))") end diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 56aaa46574..eae6d3e4ba 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -63,12 +63,6 @@ Base.:^(M::AbstractManifold, n) = PowerManifold(M, n...) function allocate(::PowerManifoldNestedReplacing, x::AbstractArray{<:SArray}) return similar(x) end -function allocate( - ::PowerManifoldNestedReplacing, - x::AbstractArray{<:ProductRepr{<:NTuple{N,SArray}}}, -) where {N} - return similar(x) -end function allocate( ::PowerManifoldNestedReplacing, x::AbstractArray{<:ArrayPartition{T,<:NTuple{N,SArray}}}, diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index d270b27a19..fcbf086d24 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -226,7 +226,7 @@ components, for which the tests fail is returned. The tolerance for the last test can be set using the `kwargs...`. """ -function check_point(M::ProductManifold, p::Union{ProductRepr,ArrayPartition}; kwargs...) +function check_point(M::ProductManifold, p::ArrayPartition; kwargs...) ts = ziptuples(Tuple(1:length(M.manifolds)), M.manifolds, submanifold_components(M, p)) e = [(t[1], check_point(t[2:end]...; kwargs...)) for t in ts] errors = filter((x) -> !(x[2] === nothing), e) @@ -238,7 +238,7 @@ end function check_point(M::ProductManifold, p; kwargs...) return DomainError( typeof(p), - "The point $p is not a point on $M, since currently only ProductRepr and ArrayPartition are supported types for points on arbitrary product manifolds", + "The point $p is not a point on $M, since currently only ArrayPartition is supported as type for points on arbitrary product manifolds", ) end @@ -251,7 +251,7 @@ components, for which the tests fail is returned. The tolerance for the last test can be set using the `kwargs...`. """ -function check_size(M::ProductManifold, p::Union{ProductRepr,ArrayPartition}) +function check_size(M::ProductManifold, p::ArrayPartition) ts = ziptuples(Tuple(1:length(M.manifolds)), M.manifolds, submanifold_components(M, p)) e = [(t[1], check_size(t[2:end]...)) for t in ts] errors = filter((x) -> !(x[2] === nothing), e) @@ -263,14 +263,10 @@ end function check_size(M::ProductManifold, p; kwargs...) return DomainError( typeof(p), - "The point $p is not a point on $M, since currently only ProductRepr and ArrayPartition are supported types for points on arbitrary product manifolds", + "The point $p is not a point on $M, since currently only ArrayPartition is supported as type for points on arbitrary product manifolds", ) end -function check_size( - M::ProductManifold, - p::Union{ProductRepr,ArrayPartition}, - X::Union{ProductRepr,ArrayPartition}, -) +function check_size(M::ProductManifold, p::ArrayPartition, X::ArrayPartition) ts = ziptuples( Tuple(1:length(M.manifolds)), M.manifolds, @@ -287,7 +283,7 @@ end function check_size(M::ProductManifold, p, X; kwargs...) return DomainError( typeof(X), - "The vector $X is not a tangent vector to any tangent space on $M, since currently only ProductRepr and ArrayPartition are supported types for tangent vectors on arbitrary product manifolds", + "The vector $X is not a tangent vector to any tangent space on $M, since currently only ArrayPartition is a supported type for tangent vectors on arbitrary product manifolds", ) end """ @@ -300,12 +296,7 @@ of all error messages of the components, for which the tests fail is returned. The tolerance for the last test can be set using the `kwargs...`. """ -function check_vector( - M::ProductManifold, - p::Union{ProductRepr,ArrayPartition}, - X::Union{ProductRepr,ArrayPartition}; - kwargs..., -) +function check_vector(M::ProductManifold, p::ArrayPartition, X::ArrayPartition; kwargs...) ts = ziptuples( Tuple(1:length(M.manifolds)), M.manifolds, @@ -322,34 +313,28 @@ end function check_vector(M::ProductManifold, p, X; kwargs...) return DomainError( typeof(X), - "The vector $X is not a tangent vector to any tangent space on $M, since currently only ProductRepr and ArrayPartition are supported types for tangent vectors on arbitrary product manifolds", + "The vector $X is not a tangent vector to any tangent space on $M, since currently only ArrayPartition is a supported type for tangent vectors on arbitrary product manifolds", ) end -for TP in [ProductRepr, ArrayPartition] - eval( - quote - function copyto!(M::ProductManifold, q::$TP, p::$TP) - map( - copyto!, - M.manifolds, - submanifold_components(q), - submanifold_components(p), - ) - return q - end - function copyto!(M::ProductManifold, Y::$TP, p::$TP, X::$TP) - map( - copyto!, - M.manifolds, - submanifold_components(Y), - submanifold_components(p), - submanifold_components(X), - ) - return Y - end - end, +function copyto!(M::ProductManifold, q::ArrayPartition, p::ArrayPartition) + map(copyto!, M.manifolds, submanifold_components(q), submanifold_components(p)) + return q +end +function copyto!( + M::ProductManifold, + Y::ArrayPartition, + p::ArrayPartition, + X::ArrayPartition, +) + map( + copyto!, + M.manifolds, + submanifold_components(Y), + submanifold_components(p), + submanifold_components(X), ) + return Y end @doc raw""" @@ -385,11 +370,6 @@ function default_retraction_method(M::ProductManifold, ::Type{T}) where {T<:Arra map(default_retraction_method, M.manifolds, T.parameters[2].parameters)..., ) end -function default_retraction_method(M::ProductManifold, ::Type{T}) where {T<:ProductRepr} - return ProductRetraction( - map(default_retraction_method, M.manifolds, T.parameters[1].parameters)..., - ) -end function default_inverse_retraction_method(M::ProductManifold) return InverseProductRetraction(map(default_inverse_retraction_method, M.manifolds)...) @@ -402,14 +382,6 @@ function default_inverse_retraction_method( map(default_inverse_retraction_method, M.manifolds, T.parameters[2].parameters)..., ) end -function default_inverse_retraction_method( - M::ProductManifold, - ::Type{T}, -) where {T<:ProductRepr} - return InverseProductRetraction( - map(default_inverse_retraction_method, M.manifolds, T.parameters[1].parameters)..., - ) -end function default_vector_transport_method(M::ProductManifold) return ProductVectorTransport(map(default_vector_transport_method, M.manifolds)...) @@ -422,14 +394,6 @@ function default_vector_transport_method( map(default_vector_transport_method, M.manifolds, T.parameters[2].parameters)..., ) end -function default_vector_transport_method( - M::ProductManifold, - ::Type{T}, -) where {T<:ProductRepr} - return ProductVectorTransport( - map(default_vector_transport_method, M.manifolds, T.parameters[1].parameters)..., - ) -end @doc raw""" distance(M::ProductManifold, p, q) @@ -457,16 +421,6 @@ compute the exponential map from `p` in the direction of `X` on the [`ProductMan which is the elementwise exponential map on the internal manifolds that build `M`. """ exp(::ProductManifold, ::Any...) -function Base.exp(M::ProductManifold, p::ProductRepr, X::ProductRepr) - return ProductRepr( - map( - exp, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - )..., - ) -end function Base.exp(M::ProductManifold, p::ArrayPartition, X::ArrayPartition) return ArrayPartition( map( @@ -477,16 +431,6 @@ function Base.exp(M::ProductManifold, p::ArrayPartition, X::ArrayPartition) )..., ) end -function Base.exp(M::ProductManifold, p::ProductRepr, X::ProductRepr, t::Number) - return ProductRepr( - map( - (N, pc, Xc) -> exp(N, pc, Xc, t), - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - )..., - ) -end function Base.exp(M::ProductManifold, p::ArrayPartition, X::ArrayPartition, t::Number) return ArrayPartition( map( @@ -626,38 +570,31 @@ function _get_dim_ranges(dims::NTuple{N,Any}) where {N} return ntuple(i -> (dims_acc[i]:(dims_acc[i] + dims[i] - 1)), Val(N)) end -for TP in [ProductRepr, ArrayPartition] - eval( - quote - function get_vector( - M::ProductManifold, - p::$TP, - Xⁱ, - B::AbstractBasis{𝔽,TangentSpaceType}, - ) where {𝔽} - dims = map(manifold_dimension, M.manifolds) - @assert length(Xⁱ) == sum(dims) - dim_ranges = _get_dim_ranges(dims) - tXⁱ = map(dr -> (@inbounds view(Xⁱ, dr)), dim_ranges) - ts = ziptuples(M.manifolds, submanifold_components(M, p), tXⁱ) - return $TP(map((@inline t -> get_vector(t..., B)), ts)) - end - function get_vector( - M::ProductManifold, - p::$TP, - Xⁱ, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:ProductBasisData}, - ) where {𝔽} - dims = map(manifold_dimension, M.manifolds) - @assert length(Xⁱ) == sum(dims) - dim_ranges = _get_dim_ranges(dims) - tXⁱ = map(dr -> (@inbounds view(Xⁱ, dr)), dim_ranges) - ts = - ziptuples(M.manifolds, submanifold_components(M, p), tXⁱ, B.data.parts) - return $TP(map((@inline t -> get_vector(t...)), ts)) - end - end, - ) +function get_vector( + M::ProductManifold, + p::ArrayPartition, + Xⁱ, + B::AbstractBasis{𝔽,TangentSpaceType}, +) where {𝔽} + dims = map(manifold_dimension, M.manifolds) + @assert length(Xⁱ) == sum(dims) + dim_ranges = _get_dim_ranges(dims) + tXⁱ = map(dr -> (@inbounds view(Xⁱ, dr)), dim_ranges) + ts = ziptuples(M.manifolds, submanifold_components(M, p), tXⁱ) + return ArrayPartition(map((@inline t -> get_vector(t..., B)), ts)) +end +function get_vector( + M::ProductManifold, + p::ArrayPartition, + Xⁱ, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:ProductBasisData}, +) where {𝔽} + dims = map(manifold_dimension, M.manifolds) + @assert length(Xⁱ) == sum(dims) + dim_ranges = _get_dim_ranges(dims) + tXⁱ = map(dr -> (@inbounds view(Xⁱ, dr)), dim_ranges) + ts = ziptuples(M.manifolds, submanifold_components(M, p), tXⁱ, B.data.parts) + return ArrayPartition(map((@inline t -> get_vector(t...)), ts)) end function get_vector!(M::ProductManifold, X, p, Xⁱ, B::AbstractBasis) @@ -700,21 +637,6 @@ function get_vector!( return X end -function get_vectors( - M::ProductManifold, - p::ProductRepr, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:ProductBasisData}, -) where {𝔽} - N = number_of_components(M) - xparts = submanifold_components(p) - BVs = map(t -> get_vectors(t...), ziptuples(M.manifolds, xparts, B.data.parts)) - zero_tvs = map(t -> zero_vector(t...), ziptuples(M.manifolds, xparts)) - vs = typeof(ProductRepr(zero_tvs...))[] - for i in 1:N, k in 1:length(BVs[i]) - push!(vs, ProductRepr(zero_tvs[1:(i - 1)]..., BVs[i][k], zero_tvs[(i + 1):end]...)) - end - return vs -end function get_vectors( M::ProductManifold, p::ArrayPartition, @@ -742,13 +664,6 @@ Access the element(s) at index `i` of a point `p` on a [`ProductManifold`](@ref) linear indexing. See also [Array Indexing](https://docs.julialang.org/en/v1/manual/arrays/#man-array-indexing-1) in Julia. """ -Base.@propagate_inbounds function Base.getindex( - p::ProductRepr, - M::ProductManifold, - i::Union{Integer,Colon,AbstractVector,Val}, -) - return get_component(M, p, i) -end Base.@propagate_inbounds function Base.getindex( p::ArrayPartition, M::ProductManifold, @@ -823,26 +738,20 @@ so the encapsulated inverse retraction methods have to be available per factor. """ inverse_retract(::ProductManifold, ::Any, ::Any, ::Any, ::InverseProductRetraction) -for TP in [ProductRepr, ArrayPartition] - eval( - quote - function inverse_retract( - M::ProductManifold, - p::$TP, - q::$TP, - method::InverseProductRetraction, - ) - return $TP( - map( - inverse_retract, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - method.inverse_retractions, - ), - ) - end - end, +function inverse_retract( + M::ProductManifold, + p::ArrayPartition, + q::ArrayPartition, + method::InverseProductRetraction, +) + return ArrayPartition( + map( + inverse_retract, + M.manifolds, + submanifold_components(M, p), + submanifold_components(M, q), + method.inverse_retractions, + ), ) end @@ -893,16 +802,6 @@ which can be computed using the logarithmic maps of the manifolds elementwise. """ log(::ProductManifold, ::Any...) -function Base.log(M::ProductManifold, p::ProductRepr, q::ProductRepr) - return ProductRepr( - map( - log, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - )..., - ) -end function Base.log(M::ProductManifold, p::ArrayPartition, q::ArrayPartition) return ArrayPartition( map( @@ -1013,7 +912,7 @@ number_of_components(::ProductManifold{𝔽,<:NTuple{N,Any}}) where {𝔽,N} = N function ProductFVectorDistribution( type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, - p::Union{AbstractArray,AbstractManifoldPoint,ProductRepr}, + p::Union{AbstractArray,AbstractManifoldPoint}, distributions::FVectorDistribution..., ) return ProductFVectorDistribution{typeof(type),typeof(distributions),typeof(p)}( @@ -1026,7 +925,7 @@ function ProductFVectorDistribution( type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, distributions::FVectorDistribution..., ) - p = ProductRepr(map(d -> support(d).point, distributions)) + p = ArrayPartition(map(d -> support(d).point, distributions)) return ProductFVectorDistribution(type, p, distributions...) end function ProductFVectorDistribution(distributions::FVectorDistribution...) @@ -1038,7 +937,7 @@ function ProductFVectorDistribution(distributions::FVectorDistribution...) ) end # Probably worth considering sum spaces in the future? - x = ProductRepr(map(d -> support(d).point, distributions)...) + x = ArrayPartition(map(d -> support(d).point, distributions)...) return ProductFVectorDistribution(VectorBundleFibers(fiber, M), x, distributions...) end @@ -1050,26 +949,20 @@ function ProductPointDistribution(distributions::MPointDistribution...) return ProductPointDistribution(M, distributions...) end -for TP in [ProductRepr, ArrayPartition] - eval( - quote - function parallel_transport_direction( - M::ProductManifold, - p::$TP, - X::$TP, - d::$TP, - ) - return $TP( - map( - parallel_transport_direction, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, d), - ), - ) - end - end, +function parallel_transport_direction( + M::ProductManifold, + p::ArrayPartition, + X::ArrayPartition, + d::ArrayPartition, +) + return ArrayPartition( + map( + parallel_transport_direction, + M.manifolds, + submanifold_components(M, p), + submanifold_components(M, X), + submanifold_components(M, d), + ), ) end function parallel_transport_direction!(M::ProductManifold, Y, p, X, d) @@ -1084,21 +977,20 @@ function parallel_transport_direction!(M::ProductManifold, Y, p, X, d) return Y end -for TP in [ProductRepr, ArrayPartition] - eval( - quote - function parallel_transport_to(M::ProductManifold, p::$TP, X::$TP, q::$TP) - return $TP( - map( - parallel_transport_to, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, q), - ), - ) - end - end, +function parallel_transport_to( + M::ProductManifold, + p::ArrayPartition, + X::ArrayPartition, + q::ArrayPartition, +) + return ArrayPartition( + map( + parallel_transport_to, + M.manifolds, + submanifold_components(M, p), + submanifold_components(M, X), + submanifold_components(M, q), + ), ) end function parallel_transport_to!(M::ProductManifold, Y, p, X, q) @@ -1113,9 +1005,6 @@ function parallel_transport_to!(M::ProductManifold, Y, p, X, q) return Y end -function project(M::ProductManifold, p::ProductRepr) - return ProductRepr(map(project, M.manifolds, submanifold_components(M, p))...) -end function project(M::ProductManifold, p::ArrayPartition) return ArrayPartition(map(project, M.manifolds, submanifold_components(M, p))...) end @@ -1125,16 +1014,6 @@ function project!(M::ProductManifold, q, p) return q end -function project(M::ProductManifold, p::ProductRepr, X::ProductRepr) - return ProductRepr( - map( - project, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - )..., - ) -end function project(M::ProductManifold, p::ArrayPartition, X::ArrayPartition) return ArrayPartition( map( @@ -1158,10 +1037,10 @@ function project!(M::ProductManifold, Y, p, X) end function Random.rand(rng::AbstractRNG, d::ProductPointDistribution) - return ProductRepr(map(d -> rand(rng, d), d.distributions)...) + return ArrayPartition(map(d -> rand(rng, d), d.distributions)...) end function Random.rand(rng::AbstractRNG, d::ProductFVectorDistribution) - return ProductRepr(map(d -> rand(rng, d), d.distributions)...) + return ArrayPartition(map(d -> rand(rng, d), d.distributions)...) end @doc raw""" @@ -1176,18 +1055,9 @@ function Random.rand( parts_kwargs=map(_ -> (;), M.manifolds), ) if vector_at === nothing - return ProductRepr( + return ArrayPartition( map((N, kwargs) -> rand(N; kwargs...), M.manifolds, parts_kwargs)..., ) - elseif isa(vector_at, ProductRepr) - return ProductRepr( - map( - (N, p, kwargs) -> rand(N; vector_at=p, kwargs...), - M.manifolds, - submanifold_components(M, vector_at), - parts_kwargs, - )..., - ) else return ArrayPartition( map( @@ -1206,18 +1076,9 @@ function Random.rand( parts_kwargs=map(_ -> (;), M.manifolds), ) if vector_at === nothing - return ProductRepr( + return ArrayPartition( map((N, kwargs) -> rand(rng, N; kwargs...), M.manifolds, parts_kwargs)..., ) - elseif isa(vector_at, ProductRepr) - return ProductRepr( - map( - (N, p, kwargs) -> rand(rng, N; vector_at=p, kwargs...), - M.manifolds, - submanifold_components(M, vector_at), - parts_kwargs, - )..., - ) else return ArrayPartition( map( @@ -1263,7 +1124,11 @@ function Distributions._rand!( ) return copyto!(x, rand(rng, d)) end -function Distributions._rand!(rng::AbstractRNG, d::ProductPointDistribution, p::ProductRepr) +function Distributions._rand!( + rng::AbstractRNG, + d::ProductPointDistribution, + p::ArrayPartition, +) map( t -> Distributions._rand!(rng, t[1], t[2]), d.distributions, @@ -1281,7 +1146,7 @@ end function Distributions._rand!( rng::AbstractRNG, d::ProductFVectorDistribution, - X::ProductRepr, + X::ArrayPartition, ) map( t -> Distributions._rand!(rng, t[1], t[2]), @@ -1301,27 +1166,21 @@ method has to be one that is available on the manifolds. """ retract(::ProductManifold, ::Any...) -for TP in [ProductRepr, ArrayPartition] - eval( - quote - function _retract( - M::ProductManifold, - p::$TP, - X::$TP, - t::Number, - method::ProductRetraction, - ) - return $TP( - map( - (N, pc, Xc, rm) -> retract(N, pc, Xc, t, rm), - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - method.retractions, - ), - ) - end - end, +function _retract( + M::ProductManifold, + p::ArrayPartition, + X::ArrayPartition, + t::Number, + method::ProductRetraction, +) + return ArrayPartition( + map( + (N, pc, Xc, rm) -> retract(N, pc, Xc, t, rm), + M.manifolds, + submanifold_components(M, p), + submanifold_components(M, X), + method.retractions, + ), ) end @@ -1374,22 +1233,22 @@ the [`ProductManifold`](@ref) `M`. """ riemann_tensor(M::ProductManifold, p, X, Y, X) -for TP in [ProductRepr, ArrayPartition] - eval( - quote - function riemann_tensor(M::ProductManifold, p::$TP, X::$TP, Y::$TP, Z::$TP) - return $TP( - map( - riemann_tensor, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, Y), - submanifold_components(M, Z), - ), - ) - end - end, +function riemann_tensor( + M::ProductManifold, + p::ArrayPartition, + X::ArrayPartition, + Y::ArrayPartition, + Z::ArrayPartition, +) + return ArrayPartition( + map( + riemann_tensor, + M.manifolds, + submanifold_components(M, p), + submanifold_components(M, X), + submanifold_components(M, Y), + submanifold_components(M, Z), + ), ) end @@ -1423,7 +1282,7 @@ set the element `[i...]` of a point `q` on a [`ProductManifold`](@ref) by linear See also [Array Indexing](https://docs.julialang.org/en/v1/manual/arrays/#man-array-indexing-1) in Julia. """ Base.@propagate_inbounds function Base.setindex!( - q::Union{ProductRepr,ArrayPartition}, + q::ArrayPartition, p, M::ProductManifold, i::Union{Integer,Colon,AbstractVector,Val}, @@ -1534,7 +1393,7 @@ Distributions.support(d::ProductPointDistribution) = MPointSupport(d.manifold) function Distributions.support(tvd::ProductFVectorDistribution) return FVectorSupport( tvd.type, - ProductRepr(map(d -> support(d).point, tvd.distributions)...), + ArrayPartition(map(d -> support(d).point, tvd.distributions)...), ) end @@ -1552,28 +1411,22 @@ function vector_bundle_transport(::FiberType, M::ProductManifold) return ProductVectorTransport(map(_ -> ParallelTransport(), M.manifolds)) end -for TP in [ProductRepr, ArrayPartition] - eval( - quote - function vector_transport_direction( - M::ProductManifold, - p::$TP, - X::$TP, - d::$TP, - m::ProductVectorTransport, - ) - return $TP( - map( - vector_transport_direction, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, d), - m.methods, - ), - ) - end - end, +function vector_transport_direction( + M::ProductManifold, + p::ArrayPartition, + X::ArrayPartition, + d::ArrayPartition, + m::ProductVectorTransport, +) + return ArrayPartition( + map( + vector_transport_direction, + M.manifolds, + submanifold_components(M, p), + submanifold_components(M, X), + submanifold_components(M, d), + m.methods, + ), ) end function vector_transport_direction!( @@ -1606,45 +1459,39 @@ base manifold. """ vector_transport_to(::ProductManifold, ::Any, ::Any, ::Any, ::ProductVectorTransport) -for TP in [ProductRepr, ArrayPartition] - eval( - quote - function vector_transport_to( - M::ProductManifold, - p::$TP, - X::$TP, - q::$TP, - m::ProductVectorTransport, - ) - return $TP( - map( - vector_transport_to, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, q), - m.methods, - ), - ) - end - function vector_transport_to( - M::ProductManifold, - p::$TP, - X::$TP, - q::$TP, - m::ParallelTransport, - ) - return $TP( - map( - (iM, ip, iX, id) -> vector_transport_to(iM, ip, iX, id, m), - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, q), - ), - ) - end - end, +function vector_transport_to( + M::ProductManifold, + p::ArrayPartition, + X::ArrayPartition, + q::ArrayPartition, + m::ProductVectorTransport, +) + return ArrayPartition( + map( + vector_transport_to, + M.manifolds, + submanifold_components(M, p), + submanifold_components(M, X), + submanifold_components(M, q), + m.methods, + ), + ) +end +function vector_transport_to( + M::ProductManifold, + p::ArrayPartition, + X::ArrayPartition, + q::ArrayPartition, + m::ParallelTransport, +) + return ArrayPartition( + map( + (iM, ip, iX, id) -> vector_transport_to(iM, ip, iX, id, m), + M.manifolds, + submanifold_components(M, p), + submanifold_components(M, X), + submanifold_components(M, q), + ), ) end diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index bdac73f5df..a3f14d4110 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -618,14 +618,6 @@ $\mathbf{0}_F$ is the zero element of the vector space $F$. """ zero_vector(::VectorBundle, ::Any...) -@doc raw""" - zero_vector(M::TangentSpaceAtPoint, p) - -Zero tangent vector at point `p` from the tangent space `M`, that is the zero tangent vector -at point `M.point`. -""" -zero_vector(::TangentSpaceAtPoint, ::Any...) - function zero_vector!(B::VectorBundle, X, p) xp, Vp = submanifold_components(B.manifold, p) VXM, VXF = submanifold_components(B.manifold, X) diff --git a/src/product_representations.jl b/src/product_representations.jl index 3930c9060d..095d714a15 100644 --- a/src/product_representations.jl +++ b/src/product_representations.jl @@ -27,176 +27,6 @@ submanifold_components(::Any...) @inline submanifold_components(p) = p.parts @inline submanifold_components(p::ArrayPartition) = p.x -function _show_component(io::IO, v; pre="", head="") - sx = sprint(show, "text/plain", v, context=io, sizehint=0) - sx = replace(sx, '\n' => "\n$(pre)") - return print(io, head, pre, sx) -end - -function _show_component_range(io::IO, vs, range; pre="", sym="Component ") - for i in range - _show_component(io, vs[i]; pre=pre, head="\n$(sym)$(i) =\n") - end - return nothing -end - -function _show_product_repr(io::IO, x; name="Product representation", nmax=4) - n = length(x.parts) - print(io, "$(name) with $(n) submanifold component$(n == 1 ? "" : "s"):") - half_nmax = div(nmax, 2) - pre = " " - sym = " Component " - if n ≤ nmax - _show_component_range(io, x.parts, 1:n; pre=pre, sym=sym) - else - _show_component_range(io, x.parts, 1:half_nmax; pre=pre, sym=sym) - print(io, "\n ⋮") - _show_component_range(io, x.parts, (n - half_nmax + 1):n; pre=pre, sym=sym) - end - return nothing -end - -""" - ProductRepr(parts) - -A more general but slower representation of points and tangent vectors on -a product manifold. - -# Example: - -A product point on a product manifold `Sphere(2) × Euclidean(2)` might be -created as - - ProductRepr([1.0, 0.0, 0.0], [2.0, 3.0]) - -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 - -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 - -@inline function number_eltype(x::ProductRepr) - @inline eti_to_one(eti) = one(number_eltype(eti)) - return typeof(sum(map(eti_to_one, x.parts))) -end - -allocate(x::ProductRepr) = ProductRepr(map(allocate, submanifold_components(x))...) -function allocate(x::ProductRepr, T::Type) - return ProductRepr(map(t -> allocate(t, T), submanifold_components(x))...) -end -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::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 - -function Base.:+(v1::ProductRepr, v2::ProductRepr) - return ProductRepr(map(+, submanifold_components(v1), submanifold_components(v2))...) -end - -function Base.:-(v1::ProductRepr, v2::ProductRepr) - return ProductRepr(map(-, submanifold_components(v1), submanifold_components(v2))...) -end -Base.:-(v::ProductRepr) = ProductRepr(map(-, submanifold_components(v))) - -Base.:*(a::Number, v::ProductRepr) = ProductRepr(map(t -> a * t, submanifold_components(v))) -Base.:*(v::ProductRepr, a::Number) = ProductRepr(map(t -> t * a, submanifold_components(v))) - -Base.:/(v::ProductRepr, a::Number) = ProductRepr(map(t -> t / a, submanifold_components(v))) - -function Base.convert(::Type{ProductRepr{TPR}}, x::ProductRepr) where {TPR<:Tuple} - if @isdefined TPR - return ProductRepr(convert(TPR, submanifold_components(x))) - else - return x - end -end - -function Base.show(io::IO, ::MIME"text/plain", x::ProductRepr) - return _show_product_repr(io, x; name="ProductRepr") -end - -ManifoldsBase._get_vector_cache_broadcast(::ProductRepr) = Val(false) - -# Tuple-like broadcasting of ProductRepr - -function Broadcast.BroadcastStyle(::Type{<:ProductRepr}) - return Broadcast.Style{ProductRepr}() -end -function Broadcast.BroadcastStyle( - ::Broadcast.AbstractArrayStyle{0}, - b::Broadcast.Style{ProductRepr}, -) - return b -end - -Broadcast.instantiate(bc::Broadcast.Broadcasted{Broadcast.Style{ProductRepr},Nothing}) = bc -function Broadcast.instantiate(bc::Broadcast.Broadcasted{Broadcast.Style{ProductRepr}}) - Broadcast.check_broadcast_axes(bc.axes, bc.args...) - return bc -end - -Broadcast.broadcastable(v::ProductRepr) = v - -@inline function Base.copy(bc::Broadcast.Broadcasted{Broadcast.Style{ProductRepr}}) - dim = axes(bc) - length(dim) == 1 || throw(DimensionMismatch("ProductRepr only supports one dimension")) - N = length(dim[1]) - return ProductRepr(ntuple(k -> @inbounds(Broadcast._broadcast_getindex(bc, k)), Val(N))) -end - -Base.@propagate_inbounds Broadcast._broadcast_getindex(v::ProductRepr, I) = v.parts[I[1]] - -Base.axes(v::ProductRepr) = axes(v.parts) - -@inline function Base.copyto!( - dest::ProductRepr, - bc::Broadcast.Broadcasted{Broadcast.Style{ProductRepr}}, -) - axes(dest) == axes(bc) || Broadcast.throwdm(axes(dest), axes(bc)) - # Performance optimization: broadcast!(identity, dest, A) is equivalent to copyto!(dest, A) if indices match - if bc.f === identity && bc.args isa Tuple{ProductRepr} # only a single input argument to broadcast! - A = bc.args[1] - if axes(dest) == axes(A) - return copyto!(dest, A) - end - end - bc′ = Broadcast.preprocess(dest, bc) - # Performance may vary depending on whether `@inbounds` is placed outside the - # for loop or not. (cf. https://github.com/JuliaLang/julia/issues/38086) - @inbounds @simd for I in eachindex(bc′) - copyto!(dest.parts[I], bc′[I]) - end - return dest -end - ## ArrayPartition ManifoldsBase._get_vector_cache_broadcast(::ArrayPartition) = Val(false) diff --git a/test/groups/metric.jl b/test/groups/metric.jl index 793c7333cb..ea032a04d2 100644 --- a/test/groups/metric.jl +++ b/test/groups/metric.jl @@ -149,37 +149,35 @@ end ([-2.0, 1.0, 0.5], hat(Rn, p, [-1.0, -0.5, 1.1])), ] - 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=[(), (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=false, - exp_log_atol_multiplier=50, - ) - end + 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 = ArrayPartition(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=[(), (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=false, + exp_log_atol_multiplier=50, + ) end end end diff --git a/test/groups/product_group.jl b/test/groups/product_group.jl index 9b84f87978..9c3ef05b03 100644 --- a/test/groups/product_group.jl +++ b/test/groups/product_group.jl @@ -29,91 +29,87 @@ using RecursiveArrayTools @test submanifold_component(G, i, 2) == Identity(Tn) end - for TRepr in (ProductRepr, ArrayPartition) - @testset "$TRepr" begin - pts = [TRepr(tp...) for tp in tuple_pts] - X_pts = [TRepr(tuple_v...)] + pts = [ArrayPartition(tp...) for tp in tuple_pts] + X_pts = [ArrayPartition(tuple_v...)] - @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 compose(G, pts[1], Identity(G)) == pts[1] - @test compose(G, Identity(G), pts[1]) == pts[1] - test_group( - G, - pts, - X_pts, - X_pts; - test_diff=true, - test_exp_from_identity=true, - test_log_from_identity=true, - test_vee_hat_from_identity=true, - ) - @test isapprox( - G, - Identity(G), - exp_lie(G, X_pts[1]), - TRepr( - exp_lie(SOn, submanifold_component(X_pts[1], 1)), - exp_lie(Tn, submanifold_component(X_pts[1], 2)), - ), - ) - @test isapprox( - G, - Identity(G), - log_lie(G, pts[1]), - TRepr( - log_lie(SOn, submanifold_component(pts[1], 1)), - log_lie(Tn, submanifold_component(pts[1], 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)), - ) + @test compose(G, pts[1], Identity(G)) == pts[1] + @test compose(G, Identity(G), pts[1]) == pts[1] + test_group( + G, + pts, + X_pts, + X_pts; + test_diff=true, + test_exp_from_identity=true, + test_log_from_identity=true, + test_vee_hat_from_identity=true, + ) + @test isapprox( + G, + Identity(G), + exp_lie(G, X_pts[1]), + ArrayPartition( + exp_lie(SOn, submanifold_component(X_pts[1], 1)), + exp_lie(Tn, submanifold_component(X_pts[1], 2)), + ), + ) + @test isapprox( + G, + Identity(G), + log_lie(G, pts[1]), + ArrayPartition( + log_lie(SOn, submanifold_component(pts[1], 1)), + log_lie(Tn, submanifold_component(pts[1], 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)), + ) - @test compose(G, pts[1], Identity(G)) == pts[1] - @test compose(G, Identity(G), pts[1]) == pts[1] - test_group(G, pts, X_pts, X_pts; test_diff=true, test_mutating=false) - test_manifold(G, pts; is_mutating=false) - @test isapprox( - G, - exp_lie(G, X_pts[1]), - TRepr( - exp_lie(SOn, submanifold_component(X_pts[1], 1)), - exp_lie(Tn, submanifold_component(X_pts[1], 2)), - ), - ) - @test isapprox( - G, - log_lie(G, pts[1]), - TRepr( - log_lie(SOn, submanifold_component(pts[1], 1)), - log_lie(Tn, submanifold_component(pts[1], 2)), - ), - ) - end - end + @test compose(G, pts[1], Identity(G)) == pts[1] + @test compose(G, Identity(G), pts[1]) == pts[1] + test_group(G, pts, X_pts, X_pts; test_diff=true, test_mutating=false) + test_manifold(G, pts; is_mutating=false) + @test isapprox( + G, + exp_lie(G, X_pts[1]), + ArrayPartition( + exp_lie(SOn, submanifold_component(X_pts[1], 1)), + exp_lie(Tn, submanifold_component(X_pts[1], 2)), + ), + ) + @test isapprox( + G, + log_lie(G, pts[1]), + ArrayPartition( + log_lie(SOn, submanifold_component(pts[1], 1)), + log_lie(Tn, submanifold_component(pts[1], 2)), + ), + ) @test sprint(show, "text/plain", G) === """ ProductGroup with 2 subgroups: SpecialOrthogonal(3) diff --git a/test/groups/rotation_translation_action.jl b/test/groups/rotation_translation_action.jl index 6949026a15..96398d0a12 100644 --- a/test/groups/rotation_translation_action.jl +++ b/test/groups/rotation_translation_action.jl @@ -2,7 +2,7 @@ include("../utils.jl") include("group_utils.jl") -@testset "Rotation action" begin +@testset "Rotation-translation action" begin G = SpecialEuclidean(2) M = base_manifold(G) A_left = RotationTranslationAction(Euclidean(2), G) diff --git a/test/groups/semidirect_product_group.jl b/test/groups/semidirect_product_group.jl index 7a31bf49e7..49c24acdd3 100644 --- a/test/groups/semidirect_product_group.jl +++ b/test/groups/semidirect_product_group.jl @@ -18,42 +18,40 @@ include("group_utils.jl") ts2 = Vector{Float64}.([1:2, 2:3, 3:4]) .* 10 tuple_pts = [zip(ts1, ts2)...] - 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) + pts = [ArrayPartition(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 diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index dbce456621..3a896cbf46 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -464,39 +464,29 @@ end SE2 = SpecialEuclidean(2) PSE2 = PowerManifold(SE2, NestedReplacingPowerRepresentation(), 2) - 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 + pse = ArrayPartition( + SA[1.0, 2.0], + SA[ + 0.5403023058681398 -0.8414709848078965 + 0.8414709848078965 0.5403023058681398 + ], + ) + p2 = [pse, pse] + @test allocate(PSE2, p2) isa Vector{ + ArrayPartition{Float64,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}}}, + } end @testset "Manifold volume" begin diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index ede68e775e..e35de8b8aa 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -23,12 +23,12 @@ using RecursiveArrayTools: ArrayPartition @test injectivity_radius(Mse, ExponentialRetraction()) ≈ π @test injectivity_radius( Mse, - ProductRepr([0.0, 1.0, 0.0], [0.0, 0.0]), + ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]), ProductRetraction(ExponentialRetraction(), ExponentialRetraction()), ) ≈ π @test injectivity_radius( Mse, - ProductRepr([0.0, 1.0, 0.0], [0.0, 0.0]), + ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]), ExponentialRetraction(), ) ≈ π @test is_default_metric(Mse, ProductMetric()) @@ -60,11 +60,11 @@ using RecursiveArrayTools: ArrayPartition ] @testset "get_component, set_component!, getindex and setindex!" begin - p1 = ProductRepr([0.0, 1.0, 0.0], [0.0, 0.0]) - @test get_component(Mse, p1, 1) == p1.parts[1] - @test get_component(Mse, p1, Val(1)) == p1.parts[1] - @test p1[Mse, 1] == p1.parts[1] - @test p1[Mse, Val(1)] == p1.parts[1] + p1 = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) + @test get_component(Mse, p1, 1) == p1.x[1] + @test get_component(Mse, p1, Val(1)) == p1.x[1] + @test p1[Mse, 1] == p1.x[1] + @test p1[Mse, Val(1)] == p1.x[1] @test p1[Mse, 1] isa Vector @test p1[Mse, Val(1)] isa Vector p2 = [10.0, 12.0] @@ -96,13 +96,13 @@ using RecursiveArrayTools: ArrayPartition @test p1ap[Mse, Val(2)] == 2 * p3 p1c = copy(p1) - p1c.parts[1][1] = -123.0 - @test p1c.parts[1][1] == -123.0 - @test p1.parts[1][1] == 0.0 + p1c.x[1][1] = -123.0 + @test p1c.x[1][1] == -123.0 + @test p1.x[1][1] == 0.0 copyto!(p1c, p1) - @test p1c.parts[1][1] == 0.0 + @test p1c.x[1][1] == 0.0 - p1c.parts[1][1] = -123.0 + p1c.x[1][1] = -123.0 copyto!(p1ap, p1c) @test p1ap.x[1][1] == -123.0 end @@ -122,43 +122,32 @@ using RecursiveArrayTools: ArrayPartition @test q[1] isa ArrayPartition @test q[1].x[1] isa Vector - p = ProductRepr([0.0, 1.0, 0.0], [0.0, 0.0]) + p = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) q = allocate([p]) - @test q[1] isa ProductRepr - @test q[1].parts[1] isa Vector - end - - @testset "copyto!" begin - p = ProductRepr([0.0, 1.0, 0.0], [0.0, 0.0]) - X = ProductRepr([1.0, 0.0, 0.0], [1.0, 0.0]) - q = allocate(p) - copyto!(Mse, q, p) - @test p.parts == q.parts - Y = allocate(X) - copyto!(Mse, Y, p, X) - @test Y.parts == X.parts + @test q[1] isa ArrayPartition + @test q[1].x[1] isa Vector end @testset "Broadcasting" begin - p1 = ProductRepr([0.0, 1.0, 0.0], [0.0, 1.0]) - p2 = ProductRepr([3.0, 4.0, 5.0], [2.0, 5.0]) + p1 = ArrayPartition([0.0, 1.0, 0.0], [0.0, 1.0]) + p2 = ArrayPartition([3.0, 4.0, 5.0], [2.0, 5.0]) br_result = p1 .+ 2.0 .* p2 - @test br_result isa ProductRepr - @test br_result.parts[1] ≈ [6.0, 9.0, 10.0] - @test br_result.parts[2] ≈ [4.0, 11.0] + @test br_result isa ArrayPartition + @test br_result.x[1] ≈ [6.0, 9.0, 10.0] + @test br_result.x[2] ≈ [4.0, 11.0] br_result .= 2.0 .* p1 .+ p2 - @test br_result.parts[1] ≈ [3.0, 6.0, 5.0] - @test br_result.parts[2] ≈ [2.0, 7.0] + @test br_result.x[1] ≈ [3.0, 6.0, 5.0] + @test br_result.x[2] ≈ [2.0, 7.0] br_result .= p1 - @test br_result.parts[1] ≈ [0.0, 1.0, 0.0] - @test br_result.parts[2] ≈ [0.0, 1.0] + @test br_result.x[1] ≈ [0.0, 1.0, 0.0] + @test br_result.x[2] ≈ [0.0, 1.0] - @test axes(p1) == (Base.OneTo(2),) + @test axes(p1) == (Base.OneTo(5),) # errors - p3 = ProductRepr([3.0, 4.0, 5.0], [2.0, 5.0], [3.0, 2.0]) + p3 = ArrayPartition([3.0, 4.0, 5.0], [2.0, 5.0], [3.0, 2.0]) @test_throws DimensionMismatch p1 .+ p3 @test_throws DimensionMismatch p1 .= p3 end @@ -169,10 +158,10 @@ using RecursiveArrayTools: ArrayPartition p2 = [0.0, 1.0, 0.0] X1 = [0.0, 1.0, 0.2] X2 = [1.0, 0.0, 0.2] - p = ProductRepr(p1, p2) - X = ProductRepr(X1, X2) - pf = ProductRepr(p1, X1) - Xf = ProductRepr(X1, p2) + p = ArrayPartition(p1, p2) + X = ArrayPartition(X1, X2) + pf = ArrayPartition(p1, X1) + Xf = ArrayPartition(X1, p2) @test is_point(Mpr, p, true) @test_throws CompositeManifoldError is_point(Mpr, X, true) @test_throws ComponentManifoldError is_vector(Mpr, pf, X, true) @@ -181,15 +170,15 @@ using RecursiveArrayTools: ArrayPartition @testset "arithmetic" begin Mee = ProductManifold(Euclidean(3), Euclidean(2)) - p1 = ProductRepr([0.0, 1.0, 0.0], [0.0, 1.0]) - p2 = ProductRepr([1.0, 2.0, 0.0], [2.0, 3.0]) - - @test isapprox(Mee, p1 + p2, ProductRepr([1.0, 3.0, 0.0], [2.0, 4.0])) - @test isapprox(Mee, p1 - p2, ProductRepr([-1.0, -1.0, 0.0], [-2.0, -2.0])) - @test isapprox(Mee, -p1, ProductRepr([0.0, -1.0, 0.0], [0.0, -1.0])) - @test isapprox(Mee, p1 * 2, ProductRepr([0.0, 2.0, 0.0], [0.0, 2.0])) - @test isapprox(Mee, 2 * p1, ProductRepr([0.0, 2.0, 0.0], [0.0, 2.0])) - @test isapprox(Mee, p1 / 2, ProductRepr([0.0, 0.5, 0.0], [0.0, 0.5])) + p1 = ArrayPartition([0.0, 1.0, 0.0], [0.0, 1.0]) + p2 = ArrayPartition([1.0, 2.0, 0.0], [2.0, 3.0]) + + @test isapprox(Mee, p1 + p2, ArrayPartition([1.0, 3.0, 0.0], [2.0, 4.0])) + @test isapprox(Mee, p1 - p2, ArrayPartition([-1.0, -1.0, 0.0], [-2.0, -2.0])) + @test isapprox(Mee, -p1, ArrayPartition([0.0, -1.0, 0.0], [0.0, -1.0])) + @test isapprox(Mee, p1 * 2, ArrayPartition([0.0, 2.0, 0.0], [0.0, 2.0])) + @test isapprox(Mee, 2 * p1, ArrayPartition([0.0, 2.0, 0.0], [0.0, 2.0])) + @test isapprox(Mee, p1 / 2, ArrayPartition([0.0, 0.5, 0.0], [0.0, 0.5])) end @testset "Show methods" begin @@ -212,71 +201,6 @@ using RecursiveArrayTools: ArrayPartition ProductManifold with 2 submanifolds: ProductManifold(Sphere(2, ℝ), Euclidean(2; field = ℝ)) ProductManifold(Sphere(2, ℝ), Euclidean(2; field = ℝ))""" - - p = Manifolds.ProductRepr(Float64[1, 0, 0]) - @test sprint(show, "text/plain", p) == """ - ProductRepr with 1 submanifold component: - Component 1 = - 3-element $(sprint(show, Vector{Float64})): - 1.0 - 0.0 - 0.0""" - - p = Manifolds.ProductRepr( - Float64[1, 0, 0], - Float64[0, 1, 0], - Float64[1, 2], - Float64[3, 4], - ) - @test sprint(show, "text/plain", p) == """ - ProductRepr with 4 submanifold components: - Component 1 = - 3-element $(sprint(show, Vector{Float64})): - 1.0 - 0.0 - 0.0 - Component 2 = - 3-element $(sprint(show, Vector{Float64})): - 0.0 - 1.0 - 0.0 - Component 3 = - 2-element $(sprint(show, Vector{Float64})): - 1.0 - 2.0 - Component 4 = - 2-element $(sprint(show, Vector{Float64})): - 3.0 - 4.0""" - - p = Manifolds.ProductRepr( - Float64[1, 0, 0], - Float64[0, 1, 0], - Float64[1, 2], - Float64[3, 4], - Float64[5, 6], - ) - @test sprint(show, "text/plain", p) == """ - ProductRepr with 5 submanifold components: - Component 1 = - 3-element $(sprint(show, Vector{Float64})): - 1.0 - 0.0 - 0.0 - Component 2 = - 3-element $(sprint(show, Vector{Float64})): - 0.0 - 1.0 - 0.0 - ⋮ - Component 4 = - 2-element $(sprint(show, Vector{Float64})): - 3.0 - 4.0 - Component 5 = - 2-element $(sprint(show, Vector{Float64})): - 5.0 - 6.0""" end M3 = Manifolds.Rotations(2) @@ -291,122 +215,101 @@ 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] - 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, - ) + pts = [ArrayPartition(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 = ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]) + q = ArrayPartition([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 - @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()) + @testset "Implicit product vector transport" begin + p = ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]) + q = ArrayPartition([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) - 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( + Z1 = vector_transport_to( Mse.manifolds[1], submanifold_component.([p, X, q], Ref(1))..., + m, ) - Z2 = parallel_transport_to( + Z2 = vector_transport_to( Mse.manifolds[2], submanifold_component.([p, X, q], Ref(2))..., + m, ) - Z = prod_type(Z1, Z2) + Z = ArrayPartition(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( + 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 = parallel_transport_direction( + Z2 = vector_transport_direction( Mse.manifolds[2], submanifold_component.([p, X, X], Ref(2))..., + m, ) - Z = prod_type(Z1, Z2) + Z = ArrayPartition(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 "ProductRepr" begin - @test (@inferred convert( - ProductRepr{Tuple{T,Float64,T} where T}, - ProductRepr(9, 10, 11), - )) == ProductRepr(9, 10.0, 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] - @test submanifold_component(p, 1) === p.parts[1] - @test submanifold_component(p, Val(1)) === p.parts[1] - @test submanifold_components(Mse, p) === p.parts - @test submanifold_components(p) === p.parts - @test allocate(p, Int, 10) isa Vector{Int} - @test length(allocate(p, Int, 10)) == 10 + @testset "Parallel transport" begin + p = ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]) + q = ArrayPartition([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 = ArrayPartition(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 = ArrayPartition(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 "ArrayPartition" begin @@ -419,103 +322,87 @@ using RecursiveArrayTools: ArrayPartition @test submanifold_components(p) === p.x end - for TP in [ProductRepr, ArrayPartition] - @testset "TP=$TP" begin - Ts = SizedVector{3,Float64} - Tr2 = SizedVector{2,Float64} - pts_sphere = [ - convert(Ts, [1.0, 0.0, 0.0]), - convert(Ts, [0.0, 1.0, 0.0]), - convert(Ts, [0.0, 0.0, 1.0]), - ] - pts_r2 = [ - convert(Tr2, [0.0, 0.0]), - convert(Tr2, [1.0, 0.0]), - convert(Tr2, [0.0, 0.1]), - ] - - pts = [TP(p[1], p[2]) for p in zip(pts_sphere, pts_r2)] - basis_types = ( - DefaultOrthonormalBasis(), - ProjectedOrthonormalBasis(:svd), - get_basis(Mse, pts[1], DefaultOrthonormalBasis()), - DiagonalizingOrthonormalBasis( - TP(SizedVector{3}([0.0, 1.0, 0.0]), SizedVector{2}([1.0, 0.0])), - ), - ) - distr_M1 = Manifolds.uniform_distribution(M1, pts_sphere[1]) - distr_M2 = Manifolds.projected_distribution( - M2, - Distributions.MvNormal(zero(pts_r2[1]), 1.0), - ) - distr_tv_M1 = Manifolds.normal_tvector_distribution(M1, pts_sphere[1], 1.0) - distr_tv_M2 = Manifolds.normal_tvector_distribution(M2, pts_r2[1], 1.0) - @test injectivity_radius(Mse, pts[1]) ≈ π - @test injectivity_radius(Mse) ≈ π - @test injectivity_radius(Mse, pts[1], ExponentialRetraction()) ≈ π - @test injectivity_radius(Mse, ExponentialRetraction()) ≈ π - - @test ManifoldsBase.allocate_coordinates( - Mse, - pts[1], - Float64, - number_of_coordinates(Mse, DefaultOrthogonalBasis()), - ) isa Vector{Float64} - - Y = allocate(pts[1]) - inverse_retract!(Mse, Y, pts[1], pts[2], default_inverse_retraction_method(Mse)) - @test isapprox( - Mse, - pts[1], - Y, - inverse_retract( - Mse, - pts[1], - pts[2], - default_inverse_retraction_method(Mse), - ), - ) + @testset "manifold tests (static size)" begin + Ts = SizedVector{3,Float64} + Tr2 = SizedVector{2,Float64} + pts_sphere = [ + convert(Ts, [1.0, 0.0, 0.0]), + convert(Ts, [0.0, 1.0, 0.0]), + convert(Ts, [0.0, 0.0, 1.0]), + ] + pts_r2 = + [convert(Tr2, [0.0, 0.0]), convert(Tr2, [1.0, 0.0]), convert(Tr2, [0.0, 0.1])] + + pts = [ArrayPartition(p[1], p[2]) for p in zip(pts_sphere, pts_r2)] + basis_types = ( + DefaultOrthonormalBasis(), + ProjectedOrthonormalBasis(:svd), + get_basis(Mse, pts[1], DefaultOrthonormalBasis()), + DiagonalizingOrthonormalBasis( + ArrayPartition(SizedVector{3}([0.0, 1.0, 0.0]), SizedVector{2}([1.0, 0.0])), + ), + ) + distr_M1 = Manifolds.uniform_distribution(M1, pts_sphere[1]) + distr_M2 = Manifolds.projected_distribution( + M2, + Distributions.MvNormal(zero(pts_r2[1]), 1.0), + ) + distr_tv_M1 = Manifolds.normal_tvector_distribution(M1, pts_sphere[1], 1.0) + distr_tv_M2 = Manifolds.normal_tvector_distribution(M2, pts_r2[1], 1.0) + @test injectivity_radius(Mse, pts[1]) ≈ π + @test injectivity_radius(Mse) ≈ π + @test injectivity_radius(Mse, pts[1], ExponentialRetraction()) ≈ π + @test injectivity_radius(Mse, ExponentialRetraction()) ≈ π + + @test ManifoldsBase.allocate_coordinates( + Mse, + pts[1], + Float64, + number_of_coordinates(Mse, DefaultOrthogonalBasis()), + ) isa Vector{Float64} - test_manifold( - Mse, - pts; - point_distributions=[ - Manifolds.ProductPointDistribution(distr_M1, distr_M2), - ], - tvector_distributions=[ - Manifolds.ProductFVectorDistribution(distr_tv_M1, distr_tv_M2), - ], - test_injectivity_radius=true, - test_musical_isomorphisms=true, - musical_isomorphism_bases=[DefaultOrthonormalBasis()], - test_tangent_vector_broadcasting=true, - test_project_tangent=true, - test_project_point=true, - test_mutating_rand=false, - retraction_methods=retraction_methods, - inverse_retraction_methods=inverse_retraction_methods, - test_riesz_representer=true, - test_default_vector_transport=true, - test_rand_point=true, - test_rand_tvector=true, - vector_transport_methods=[ - ProductVectorTransport(ParallelTransport(), ParallelTransport()), - ProductVectorTransport( - SchildsLadderTransport(), - SchildsLadderTransport(), - ), - ProductVectorTransport(PoleLadderTransport(), PoleLadderTransport()), - ], - basis_types_vecs=(basis_types[1], basis_types[3], basis_types[4]), - basis_types_to_from=basis_types, - is_tangent_atol_multiplier=1, - exp_log_atol_multiplier=1, - ) - @test number_eltype(pts[1]) === Float64 + Y = allocate(pts[1]) + inverse_retract!(Mse, Y, pts[1], pts[2], default_inverse_retraction_method(Mse)) + @test isapprox( + Mse, + pts[1], + Y, + inverse_retract(Mse, pts[1], pts[2], default_inverse_retraction_method(Mse)), + ) - @test (@inferred ManifoldsBase._get_vector_cache_broadcast(pts[1])) === - Val(false) - end + test_manifold( + Mse, + pts; + point_distributions=[Manifolds.ProductPointDistribution(distr_M1, distr_M2)], + tvector_distributions=[ + Manifolds.ProductFVectorDistribution(distr_tv_M1, distr_tv_M2), + ], + test_injectivity_radius=true, + test_musical_isomorphisms=true, + musical_isomorphism_bases=[DefaultOrthonormalBasis()], + test_tangent_vector_broadcasting=true, + test_project_tangent=true, + test_project_point=true, + test_mutating_rand=false, + retraction_methods=retraction_methods, + inverse_retraction_methods=inverse_retraction_methods, + test_riesz_representer=true, + test_default_vector_transport=true, + test_rand_point=true, + test_rand_tvector=true, + vector_transport_methods=[ + ProductVectorTransport(ParallelTransport(), ParallelTransport()), + ProductVectorTransport(SchildsLadderTransport(), SchildsLadderTransport()), + ProductVectorTransport(PoleLadderTransport(), PoleLadderTransport()), + ], + basis_types_vecs=(basis_types[1], basis_types[3], basis_types[4]), + basis_types_to_from=basis_types, + is_tangent_atol_multiplier=1, + exp_log_atol_multiplier=1, + ) + @test number_eltype(pts[1]) === Float64 + + @test (@inferred ManifoldsBase._get_vector_cache_broadcast(pts[1])) === Val(false) end @testset "vee/hat" begin @@ -524,7 +411,7 @@ using RecursiveArrayTools: ArrayPartition M = M1 × M2 e = Matrix{Float64}(I, 3, 3) - p = ProductRepr(exp(M1, e, hat(M1, e, [1.0, 2.0, 3.0])), [1.0, 2.0, 3.0]) + p = ArrayPartition(exp(M1, e, hat(M1, e, [1.0, 2.0, 3.0])), [1.0, 2.0, 3.0]) X = [0.1, 0.2, 0.3, -1.0, 2.0, -3.0] Xc = hat(M, p, X) @@ -534,8 +421,8 @@ using RecursiveArrayTools: ArrayPartition @testset "get_coordinates" begin # make sure `get_coordinates` does not return an `ArrayPartition` - p1 = ProductRepr([0.0, 1.0, 0.0], [0.0, 0.0]) - X1 = ProductRepr([1.0, 0.0, -1.0], [1.0, 0.0]) + p1 = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) + X1 = ArrayPartition([1.0, 0.0, -1.0], [1.0, 0.0]) Tp1Mse = TangentSpaceAtPoint(Mse, p1) c = get_coordinates(Tp1Mse, p1, X1, DefaultOrthonormalBasis()) @test c isa Vector @@ -548,7 +435,7 @@ using RecursiveArrayTools: ArrayPartition end @testset "Basis printing" begin - p = ProductRepr([1.0, 0.0, 0.0], [1.0, 0.0]) + p = ArrayPartition([1.0, 0.0, 0.0], [1.0, 0.0]) B = DefaultOrthonormalBasis() Bc = get_basis(Mse, p, B) Bc_components_s = sprint.(show, "text/plain", Bc.data.parts) @@ -562,19 +449,19 @@ using RecursiveArrayTools: ArrayPartition end @testset "Basis-related errors" begin - a = ProductRepr([1.0, 0.0, 0.0], [0.0, 0.0]) + a = ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]) B = CachedBasis(DefaultOrthonormalBasis(), ProductBasisData(([],))) @test_throws AssertionError get_vector!( Mse, a, - ProductRepr([1.0, 0.0, 0.0], [0.0, 0.0]), + ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]), [1.0, 2.0, 3.0, 4.0, 5.0], # this is one element too long, hence assertionerror B, ) @test_throws MethodError get_vector!( Mse, a, - ProductRepr([1.0, 0.0, 0.0], [0.0, 0.0]), + ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]), [1.0, 2.0, 3.0, 4.0], B, # empty elements yield a submanifold MethodError ) @@ -601,48 +488,46 @@ using RecursiveArrayTools: ArrayPartition @test is_point(Mss, rand(uniform_distribution(Mss, p))) 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 "Atlas & Induced Basis" begin + M = ProductManifold(Euclidean(2), Euclidean(2)) + p = ArrayPartition(zeros(2), ones(2)) + X = ArrayPartition(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 = 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 + @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 = ArrayPartition(p, q) + X = ArrayPartition(log(M, p, q), log(M, q, p)) + Y = change_metric(N, e, P, X) + Yc = ArrayPartition( + 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 = ArrayPartition( + 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 @testset "default retraction, inverse retraction and VT" begin Mstb = ProductManifold(M1, TangentBundle(M1)) T_p_ap = ArrayPartition{ Float64, - Tuple{Matrix{Float64},ProductRepr{Tuple{Matrix{Float64},Matrix{Float64}}}}, - } - T_p_pr = ProductRepr{ - Tuple{Matrix{Float64},ProductRepr{Tuple{Matrix{Float64},Matrix{Float64}}}}, + Tuple{ + Matrix{Float64}, + ArrayPartition{Float64,Tuple{Matrix{Float64},Matrix{Float64}}}, + }, } @test Manifolds.default_retraction_method(Mstb) === ProductRetraction( ExponentialRetraction(), @@ -652,10 +537,6 @@ using RecursiveArrayTools: ArrayPartition ExponentialRetraction(), Manifolds.FiberBundleProductRetraction(), ) - @test Manifolds.default_retraction_method(Mstb, T_p_pr) === ProductRetraction( - ExponentialRetraction(), - Manifolds.FiberBundleProductRetraction(), - ) @test Manifolds.default_inverse_retraction_method(Mstb) === Manifolds.InverseProductRetraction( @@ -667,11 +548,6 @@ using RecursiveArrayTools: ArrayPartition LogarithmicInverseRetraction(), Manifolds.FiberBundleInverseProductRetraction(), ) - @test Manifolds.default_inverse_retraction_method(Mstb, T_p_pr) === - Manifolds.InverseProductRetraction( - LogarithmicInverseRetraction(), - Manifolds.FiberBundleInverseProductRetraction(), - ) @test Manifolds.default_vector_transport_method(Mstb) === ProductVectorTransport( ParallelTransport(), @@ -680,7 +556,7 @@ using RecursiveArrayTools: ArrayPartition ParallelTransport(), ), ) - @test Manifolds.default_vector_transport_method(Mstb, T_p_pr) === + @test Manifolds.default_vector_transport_method(Mstb, T_p_ap) === ProductVectorTransport( ParallelTransport(), Manifolds.FiberBundleProductVectorTransport( @@ -688,7 +564,7 @@ using RecursiveArrayTools: ArrayPartition ParallelTransport(), ), ) - @test Manifolds.default_vector_transport_method(Mstb, T_p_pr) === + @test Manifolds.default_vector_transport_method(Mstb, T_p_ap) === ProductVectorTransport( ParallelTransport(), Manifolds.FiberBundleProductVectorTransport( diff --git a/test/manifolds/vector_bundle.jl b/test/manifolds/vector_bundle.jl index fa3328f732..c183cd740c 100644 --- a/test/manifolds/vector_bundle.jl +++ b/test/manifolds/vector_bundle.jl @@ -12,17 +12,6 @@ struct TestVectorSpaceType <: VectorSpaceType end @testset "Nice access to vector bundle components" begin TB = TangentBundle(M) - @testset "ProductRepr" begin - p = ProductRepr([1.0, 0.0, 0.0], [0.0, 2.0, 4.0]) - @test p[TB, :point] === p.parts[1] - @test p[TB, :vector] === p.parts[2] - p[TB, :vector] = [0.0, 3.0, 1.0] - @test p.parts[2] == [0.0, 3.0, 1.0] - p[TB, :point] = [0.0, 1.0, 0.0] - @test p.parts[1] == [0.0, 1.0, 0.0] - @test_throws DomainError p[TB, :error] - @test_throws DomainError p[TB, :error] = [1, 2, 3] - end @testset "ArrayPartition" begin p = ArrayPartition([1.0, 0.0, 0.0], [0.0, 2.0, 4.0]) @test p[TB, :point] === p.x[1] @@ -48,7 +37,7 @@ struct TestVectorSpaceType <: VectorSpaceType end TEST_FLOAT32 && push!(types, Vector{Float32}) TEST_STATIC_SIZED && push!(types, MVector{3,Float64}) - for T in types, prepr in [ProductRepr, ArrayPartition] + for T in types p = convert(T, [1.0, 0.0, 0.0]) TB = TangentBundle(M) @test injectivity_radius(TB) == 0 @@ -69,19 +58,16 @@ struct TestVectorSpaceType <: VectorSpaceType end "VectorBundle(TestVectorSpaceType(), Sphere(2, ℝ))" @testset "Type $T" begin pts_tb = [ - prepr(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, -1.0, -1.0])), - prepr(convert(T, [0.0, 1.0, 0.0]), convert(T, [2.0, 0.0, 1.0])), - prepr(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, 2.0, -1.0])), + ArrayPartition(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, -1.0, -1.0])), + ArrayPartition(convert(T, [0.0, 1.0, 0.0]), convert(T, [2.0, 0.0, 1.0])), + ArrayPartition(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, 2.0, -1.0])), ] - @inferred prepr(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, -1.0, -1.0])) - if prepr === ProductRepr - for pt in pts_tb - @test bundle_projection(TB, pt) ≈ pt.parts[1] - end - else - for pt in pts_tb - @test bundle_projection(TB, pt) ≈ pt.x[1] - end + @inferred ArrayPartition( + convert(T, [1.0, 0.0, 0.0]), + convert(T, [0.0, -1.0, -1.0]), + ) + for pt in pts_tb + @test bundle_projection(TB, pt) ≈ pt.x[1] end X12_prod = inverse_retract(TB, pts_tb[1], pts_tb[2], m_prod_invretr) X13_prod = inverse_retract(TB, pts_tb[1], pts_tb[3], m_prod_invretr) @@ -205,18 +191,21 @@ struct TestVectorSpaceType <: VectorSpaceType end @testset "product retraction and inverse retraction on tangent bundle for power and product manifolds" begin M = PowerManifold(Circle(ℝ), 2) N = TangentBundle(M) - p1 = ProductRepr([0.0, 0.0], [0.0, 0.0]) - p2 = ProductRepr([-1.047, -1.047], [0.0, 0.0]) + p1 = ArrayPartition([0.0, 0.0], [0.0, 0.0]) + p2 = ArrayPartition([-1.047, -1.047], [0.0, 0.0]) X1 = inverse_retract(N, p1, p2, m_prod_invretr) @test isapprox(N, p2, retract(N, p1, X1, m_prod_retr)) @test is_vector(N, p2, vector_transport_to(N, p1, X1, p2)) M2 = ProductManifold(Circle(ℝ), Euclidean(2)) N2 = TangentBundle(M2) - p1_2 = ProductRepr(ProductRepr([0.0], [0.0, 0.0]), ProductRepr([0.0], [0.0, 0.0])) - p2_2 = ProductRepr( - ProductRepr([-1.047], [1.0, 0.0]), - ProductRepr([-1.047], [0.0, 1.0]), + p1_2 = ArrayPartition( + ArrayPartition([0.0], [0.0, 0.0]), + ArrayPartition([0.0], [0.0, 0.0]), + ) + p2_2 = ArrayPartition( + ArrayPartition([-1.047], [1.0, 0.0]), + ArrayPartition([-1.047], [0.0, 1.0]), ) @test isapprox( N2, From 0b781acf594ed5488c2761ac5c4d6d7d040efdf1 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Thu, 7 Sep 2023 17:20:52 +0200 Subject: [PATCH 15/81] finish conversion to optionally static --- NEWS.md | 13 +- src/manifolds/CenteredMatrices.jl | 55 ++++++-- src/manifolds/Elliptope.jl | 46 +++++-- src/manifolds/FixedRankMatrices.jl | 121 +++++++++++------- src/manifolds/Multinomial.jl | 60 ++++++--- src/manifolds/MultinomialDoublyStochastic.jl | 56 +++++--- src/manifolds/MultinomialSymmetric.jl | 50 +++++--- src/manifolds/ProbabilitySimplex.jl | 104 ++++++++------- .../ProbabilitySimplexEuclideanMetric.jl | 5 +- src/manifolds/ProjectiveSpace.jl | 2 +- src/manifolds/Sphere.jl | 6 +- src/manifolds/SphereSymmetricMatrices.jl | 44 +++++-- src/manifolds/Symmetric.jl | 2 +- .../SymmetricPositiveSemidefiniteFixedRank.jl | 81 ++++++++---- src/manifolds/Tucker.jl | 83 +++++++----- test/manifolds/multinomial_matrices.jl | 5 +- 16 files changed, 488 insertions(+), 245 deletions(-) diff --git a/NEWS.md b/NEWS.md index 49bd67e5c8..90b1b45885 100644 --- a/NEWS.md +++ b/NEWS.md @@ -19,19 +19,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 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: - 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: + - `CenteredMatrices{m,n}`, - `CholeskySpace{N}`, + - `Elliptope{N,K}`, - `Euclidean`, + - `FixedRankMatrices{m,n,k}`, - `GeneralUnitaryMultiplicationGroup{n}`, - `GeneralizedGrassmann{n,k}`, - `GeneralizedStiefel{n,k}`, - `Grassmann{n,k}`, + - `MultinomialMatrices{N,M}`, + - `MultinomialDoublyStochastic{n}`, + - `MultinomialSymmetric{n}`, - `Orthogonal{n}`, + - `ProbabilitySimplex{n}`, - `SpecialOrthogonal{n}`, - `SpecialUnitary{n}`, - `SpecialEuclideanManifold{n}`, + - `SphereSymmetricMatrices{N}`, - `Stiefel{n,k}`, + - `SymmetricMatrices{N}`, - `SymmetricPositiveDefinite{n}`, - - `TranslationGroup`. + - `SymmetricPositiveSemidefiniteFixedRank{n,k}`, + - `TranslationGroup`, + - `Tucker`. For example diff --git a/src/manifolds/CenteredMatrices.jl b/src/manifolds/CenteredMatrices.jl index 49c9342bd7..724f952341 100644 --- a/src/manifolds/CenteredMatrices.jl +++ b/src/manifolds/CenteredMatrices.jl @@ -1,5 +1,5 @@ @doc raw""" - CenteredMatrices{m,n,𝔽} <: AbstractDecoratorManifold{𝔽} + CenteredMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} The manifold of $m × n$ real-valued or complex-valued matrices whose columns sum to zero, i.e. ````math @@ -8,20 +8,31 @@ The manifold of $m × n$ real-valued or complex-valued matrices whose columns su where $𝔽 ∈ \{ℝ,ℂ\}$. # Constructor - CenteredMatrices(m, n[, field=ℝ]) + CenteredMatrices(m, n[, field=ℝ]; parameter::Symbol=:field) Generate the manifold of `m`-by-`n` (`field`-valued) matrices whose columns sum to zero. + +`parameter`: whether a type parameter should be used to store `m` and `n`. By default size +is stored in a field. Value can either be `:field` or `:type`. """ -struct CenteredMatrices{M,N,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct CenteredMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end -function CenteredMatrices(m::Int, n::Int, field::AbstractNumbers=ℝ) - return CenteredMatrices{m,n,field}() +function CenteredMatrices( + m::Int, + n::Int, + field::AbstractNumbers=ℝ; + parameter::Symbol=:field, +) + size = wrap_type_parameter(parameter, (m, n)) + return CenteredMatrices{typeof(size),field}(size) end active_traits(f, ::CenteredMatrices, args...) = merge_traits(IsEmbeddedSubmanifold()) @doc raw""" - check_point(M::CenteredMatrices{m,n,𝔽}, p; kwargs...) + check_point(M::CenteredMatrices, p; kwargs...) Check whether the matrix is a valid point on the [`CenteredMatrices`](@ref) `M`, i.e. is an `m`-by-`n` matrix whose columns sum to @@ -29,7 +40,8 @@ zero. The tolerance for the column sums of `p` can be set using `kwargs...`. """ -function check_point(M::CenteredMatrices{m,n,𝔽}, p; kwargs...) where {m,n,𝔽} +function check_point(M::CenteredMatrices, p; kwargs...) + m, n = get_mn(M) if !isapprox(sum(p, dims=1), zeros(1, n); kwargs...) return DomainError( p, @@ -42,14 +54,15 @@ function check_point(M::CenteredMatrices{m,n,𝔽}, p; kwargs...) where {m,n, end """ - check_vector(M::CenteredMatrices{m,n,𝔽}, p, X; kwargs... ) + check_vector(M::CenteredMatrices, p, X; kwargs... ) Check whether `X` is a tangent vector to manifold point `p` on the [`CenteredMatrices`](@ref) `M`, i.e. that `X` is a matrix of size `(m, n)` whose columns sum to zero and its values are from the correct [`AbstractNumbers`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#number-system). The tolerance for the column sums of `p` and `X` can be set using `kwargs...`. """ -function check_vector(M::CenteredMatrices{m,n,𝔽}, p, X; kwargs...) where {m,n,𝔽} +function check_vector(M::CenteredMatrices, p, X; kwargs...) + m, n = get_mn(M) if !isapprox(sum(X, dims=1), zeros(1, n); kwargs...) return DomainError( X, @@ -62,7 +75,16 @@ end embed(::CenteredMatrices, p) = p embed(::CenteredMatrices, p, X) = X -get_embedding(::CenteredMatrices{m,n,𝔽}) where {m,n,𝔽} = Euclidean(m, n; field=𝔽) +function get_embedding(::CenteredMatrices{TypeParameter{Tuple{m,n}},𝔽}) where {m,n,𝔽} + return Euclidean(m, n; field=𝔽, parameter=:type) +end +function get_embedding(M::CenteredMatrices{Tuple{Int,Int},𝔽}) where {𝔽} + m, n = get_mn(M) + return Euclidean(m, n; field=𝔽) +end + +get_mn(::CenteredMatrices{TypeParameter{Tuple{m,n}}}) where {m,n} = (m, n) +get_mn(M::CenteredMatrices{Tuple{Int,Int}}) = get_parameter(M.size) """ is_flat(::CenteredMatrices) @@ -72,7 +94,7 @@ Return true. [`CenteredMatrices`](@ref) is a flat manifold. is_flat(M::CenteredMatrices) = true @doc raw""" - manifold_dimension(M::CenteredMatrices{m,n,𝔽}) + manifold_dimension(M::CenteredMatrices) Return the manifold dimension of the [`CenteredMatrices`](@ref) `m`-by-`n` matrix `M` over the number system `𝔽`, i.e. @@ -82,7 +104,8 @@ Return the manifold dimension of the [`CenteredMatrices`](@ref) `m`-by-`n` matri ```` where $\dim_ℝ 𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ -function manifold_dimension(::CenteredMatrices{m,n,𝔽}) where {m,n,𝔽} +function manifold_dimension(M::CenteredMatrices{<:Any,𝔽}) where {𝔽} + m, n = get_mn(M) return (m * n - n) * real_dimension(𝔽) end @@ -122,9 +145,13 @@ project(::CenteredMatrices, ::Any, ::Any) project!(::CenteredMatrices, Y, p, X) = (Y .= X .- mean(X, dims=1)) -@generated representation_size(::CenteredMatrices{m,n,𝔽}) where {m,n,𝔽} = (m, n) +representation_size(M::CenteredMatrices) = get_mn(M) -function Base.show(io::IO, ::CenteredMatrices{m,n,𝔽}) where {m,n,𝔽} +function Base.show(io::IO, ::CenteredMatrices{TypeParameter{Tuple{m,n}},𝔽}) where {m,n,𝔽} + return print(io, "CenteredMatrices($(m), $(n), $(𝔽); parameter=:type)") +end +function Base.show(io::IO, M::CenteredMatrices{Tuple{Int,Int},𝔽}) where {𝔽} + m, n = get_mn(M) return print(io, "CenteredMatrices($(m), $(n), $(𝔽))") end diff --git a/src/manifolds/Elliptope.jl b/src/manifolds/Elliptope.jl index 0f52dcce53..6d256a03f4 100644 --- a/src/manifolds/Elliptope.jl +++ b/src/manifolds/Elliptope.jl @@ -1,5 +1,5 @@ @doc raw""" - Elliptope{N,K} <: AbstractDecoratorManifold{ℝ} + Elliptope{T} <: AbstractDecoratorManifold{ℝ} The Elliptope manifold, also known as the set of correlation matrices, consists of all symmetric positive semidefinite matrices of rank $k$ with unit diagonal, i.e., @@ -35,15 +35,23 @@ investigated in[JourneeBachAbsilSepulchre:2010](@cite). # Constructor - Elliptope(n,k) + Elliptope(n::Int, k::Int; parameter::Symbol=:field) generates the manifold $\mathcal E(n,k) \subset ℝ^{n × n}$. + +`parameter`: whether a type parameter should be used to store `n` and `k`. By default size +is stored in a field. Value can either be `:field` or `:type`. """ -struct Elliptope{N,K} <: AbstractDecoratorManifold{ℝ} end +struct Elliptope{T} <: AbstractDecoratorManifold{ℝ} + size::T +end active_traits(f, ::Elliptope, args...) = merge_traits(IsEmbeddedManifold()) -Elliptope(n::Int, k::Int) = Elliptope{n,k}() +function Elliptope(n::Int, k::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n, k)) + return Elliptope{typeof(size)}(size) +end @doc raw""" check_point(M::Elliptope, q; kwargs...) @@ -55,7 +63,7 @@ Since by construction $p$ is symmetric, this is not explicitly checked. Since $p$ is by construction positive semidefinite, this is not checked. The tolerances for positive semidefiniteness and unit trace can be set using the `kwargs...`. """ -function check_point(M::Elliptope{N,K}, q; kwargs...) where {N,K} +function check_point(M::Elliptope, q; kwargs...) row_norms_sq = sum(abs2, q; dims=2) if !all(isapprox.(row_norms_sq, 1.0; kwargs...)) return DomainError( @@ -77,7 +85,7 @@ zero diagonal. The tolerance for the base point check and zero diagonal can be set using the `kwargs...`. Note that symmetric of $X$ holds by construction an is not explicitly checked. """ -function check_vector(M::Elliptope{N,K}, q, Y; kwargs...) where {N,K} +function check_vector(M::Elliptope, q, Y; kwargs...) X = q * Y' + Y * q' n = diag(X) if !all(isapprox.(n, 0.0; kwargs...)) @@ -89,7 +97,16 @@ function check_vector(M::Elliptope{N,K}, q, Y; kwargs...) where {N,K} return nothing end -get_embedding(M::Elliptope) = Euclidean(representation_size(M)...; field=ℝ) +function get_embedding(::Elliptope{TypeParameter{Tuple{n,k}}}) where {n,k} + return Euclidean(n, k; parameter=:type) +end +function get_embedding(M::Elliptope{Tuple{Int,Int}}) + n, k = get_nk(M) + return Euclidean(n, k) +end + +get_nk(::Elliptope{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) +get_nk(M::Elliptope{Tuple{Int,Int}}) = get_parameter(M.size) """ is_flat(::Elliptope) @@ -107,7 +124,8 @@ returns the dimension of \dim \mathcal E(n,k) = n(k-1) - \frac{k(k-1)}{2}. ```` """ -@generated function manifold_dimension(::Elliptope{N,K}) where {N,K} +function manifold_dimension(M::Elliptope) + N, K = get_nk(M) return N * (K - 1) - div(K * (K - 1), 2) end @@ -154,10 +172,14 @@ Return the size of an array representing an element on the [`Elliptope`](@ref) manifold `M`, i.e. $n × k$, the size of such factor of $p=qq^{\mathrm{T}}$ on $\mathcal M = \mathcal E(n,k)$. """ -@generated representation_size(::Elliptope{N,K}) where {N,K} = (N, K) +representation_size(M::Elliptope) = get_nk(M) -function Base.show(io::IO, ::Elliptope{N,K}) where {N,K} - return print(io, "Elliptope($(N), $(K))") +function Base.show(io::IO, ::Elliptope{TypeParameter{Tuple{n,k}}}) where {n,k} + return print(io, "Elliptope($(n), $(k); parameter=:type)") +end +function Base.show(io::IO, M::Elliptope{Tuple{Int,Int}}) + n, k = get_nk(M) + return print(io, "Elliptope($(n), $(k))") end """ @@ -177,4 +199,4 @@ definite matrix `p` on the [`Elliptope`](@ref) manifold `M`. """ zero_vector(::Elliptope, ::Any...) -zero_vector!(::Elliptope{N,K}, v, ::Any) where {N,K} = fill!(v, 0) +zero_vector!(::Elliptope, X, ::Any) = fill!(X, 0) diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 4b19ff6ff3..2778fb493d 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -1,5 +1,5 @@ @doc raw""" - FixedRankMatrices{m,n,k,𝔽} <: AbstractDecoratorManifold{𝔽} + FixedRankMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} The manifold of ``m × n`` real-valued or complex-valued matrices of fixed rank ``k``, i.e. ````math @@ -36,9 +36,19 @@ on ``ℝ^{m × n}`` to the tangent bundle [Vandereycken:2013](@cite). Generate the manifold of `m`-by-`n` (`field`-valued) matrices of rank `k`. """ -struct FixedRankMatrices{M,N,K,𝔽} <: AbstractDecoratorManifold{𝔽} end -function FixedRankMatrices(m::Int, n::Int, k::Int, field::AbstractNumbers=ℝ) - return FixedRankMatrices{m,n,k,field}() +struct FixedRankMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end + +function FixedRankMatrices( + m::Int, + n::Int, + k::Int, + field::AbstractNumbers=ℝ; + parameter::Symbol=:field, +) + size = wrap_type_parameter(parameter, (m, n, k)) + return FixedRankMatrices{typeof(size),field}(size) end active_traits(f, ::FixedRankMatrices, args...) = merge_traits(IsEmbeddedManifold()) @@ -117,13 +127,8 @@ function allocate(X::UMVTVector, ::Type{T}) where {T} return UMVTVector(allocate(X.U, T), allocate(X.M, T), allocate(X.Vt, T)) end -function allocate_result( - ::FixedRankMatrices{m,n,k}, - ::typeof(project), - X, - p, - vals..., -) where {m,n,k} +function allocate_result(M::FixedRankMatrices, ::typeof(project), X, p, vals...) + m, n, k = get_mnk(M) # vals are p and X, so we can use their fields to set up those of the UMVTVector return UMVTVector(allocate(p.U, m, k), allocate(p.S, k, k), allocate(p.Vt, k, n)) end @@ -184,15 +189,16 @@ Base.axes(::UMVTVector) = () end @doc raw""" - check_point(M::FixedRankMatrices{m,n,k}, p; kwargs...) + check_point(M::FixedRankMatrices, p; kwargs...) Check whether the matrix or [`SVDMPoint`](@ref) `x` ids a valid point on the -[`FixedRankMatrices`](@ref)`{m,n,k,𝔽}` `M`, i.e. is an `m`-by`n` matrix of +[`FixedRankMatrices`](@ref) `M`, i.e. is an `m`-by`n` matrix of rank `k`. For the [`SVDMPoint`](@ref) the internal representation also has to have the right shape, i.e. `p.U` and `p.Vt` have to be unitary. The keyword arguments are passed to the `rank` function that verifies the rank of `p`. """ -function check_point(M::FixedRankMatrices{m,n,k}, p; kwargs...) where {m,n,k} +function check_point(M::FixedRankMatrices, p; kwargs...) + m, n, k = get_mnk(M) r = rank(p; kwargs...) s = "The point $(p) does not lie on $(M), " if r > k @@ -200,7 +206,8 @@ function check_point(M::FixedRankMatrices{m,n,k}, p; kwargs...) where {m,n,k} end return nothing end -function check_point(M::FixedRankMatrices{m,n,k}, p::SVDMPoint; kwargs...) where {m,n,k} +function check_point(M::FixedRankMatrices, p::SVDMPoint; kwargs...) + m, n, k = get_mnk(M) s = "The point $(p) does not lie on $(M), " if !isapprox(p.U' * p.U, one(zeros(k, k)); kwargs...) return DomainError( @@ -217,7 +224,8 @@ function check_point(M::FixedRankMatrices{m,n,k}, p::SVDMPoint; kwargs...) where return nothing end -function check_size(M::FixedRankMatrices{m,n,k}, p::SVDMPoint) where {m,n,k} +function check_size(M::FixedRankMatrices, p::SVDMPoint) + m, n, k = get_mnk(M) if (size(p.U) != (m, k)) || (length(p.S) != k) || (size(p.Vt) != (k, n)) return DomainError( [size(p.U)..., length(p.S), size(p.Vt)...], @@ -225,7 +233,8 @@ function check_size(M::FixedRankMatrices{m,n,k}, p::SVDMPoint) where {m,n,k} ) end end -function check_size(M::FixedRankMatrices{m,n,k}, p) where {m,n,k} +function check_size(M::FixedRankMatrices, p) + m, n, k = get_mnk(M) pS = svd(p) if (size(pS.U) != (m, k)) || (length(pS.S) != k) || (size(pS.Vt) != (k, n)) return DomainError( @@ -234,7 +243,8 @@ function check_size(M::FixedRankMatrices{m,n,k}, p) where {m,n,k} ) end end -function check_size(M::FixedRankMatrices{m,n,k}, p, X::UMVTVector) where {m,n,k} +function check_size(M::FixedRankMatrices, p, X::UMVTVector) + m, n, k = get_mnk(M) if (size(X.U) != (m, k)) || (size(X.Vt) != (k, n)) || (size(X.M) != (k, k)) return DomainError( cat(size(X.U), size(X.M), size(X.Vt), dims=1), @@ -244,18 +254,14 @@ function check_size(M::FixedRankMatrices{m,n,k}, p, X::UMVTVector) where {m,n,k} end @doc raw""" - check_vector(M:FixedRankMatrices{m,n,k}, p, X; kwargs...) + check_vector(M:FixedRankMatrices, p, X; kwargs...) Check whether the tangent [`UMVTVector`](@ref) `X` is from the tangent space of the [`SVDMPoint`](@ref) `p` on the [`FixedRankMatrices`](@ref) `M`, i.e. that `v.U` and `v.Vt` are (columnwise) orthogonal to `x.U` and `x.Vt`, respectively, and its dimensions are consistent with `p` and `X.M`, i.e. correspond to `m`-by-`n` matrices of rank `k`. """ -function check_vector( - M::FixedRankMatrices{m,n,k}, - p::SVDMPoint, - X::UMVTVector; - kwargs..., -) where {m,n,k} +function check_vector(M::FixedRankMatrices, p::SVDMPoint, X::UMVTVector; kwargs...) + m, n, k = get_mnk(M) if !isapprox(X.U' * p.U, zeros(k, k); kwargs...) return DomainError( norm(X.U' * p.U - zeros(k, k)), @@ -344,7 +350,16 @@ function embed!(::FixedRankMatrices, Y, p::SVDMPoint, X::UMVTVector) return mul!(Y, p.U, X.Vt, true, true) end -get_embedding(::FixedRankMatrices{m,n,k,𝔽}) where {m,n,k,𝔽} = Euclidean(m, n; field=𝔽) +function get_embedding(::FixedRankMatrices{TypeParameter{Tuple{m,n,k}},𝔽}) where {m,n,k,𝔽} + return Euclidean(m, n; field=𝔽, parameter=:type) +end +function get_embedding(M::FixedRankMatrices{Tuple{Int,Int,Int},𝔽}) where {𝔽} + m, n, k = get_mnk(M) + return Euclidean(m, n; field=𝔽) +end + +get_mnk(::FixedRankMatrices{TypeParameter{Tuple{m,n,k}}}) where {m,n,k} = (m, n, k) +get_mnk(M::FixedRankMatrices{Tuple{Int,Int,Int}}) = get_parameter(M.size) """ injectivity_radius(::FixedRankMatrices) @@ -352,7 +367,7 @@ get_embedding(::FixedRankMatrices{m,n,k,𝔽}) where {m,n,k,𝔽} = Euclidean(m, Return the incjectivity radius of the manifold of [`FixedRankMatrices`](@ref), i.e. 0. See [HosseiniUschmajew:2017](@cite). """ -function injectivity_radius(::FixedRankMatrices{m,n,k}) where {m,n,k} +function injectivity_radius(::FixedRankMatrices) return 0.0 end @@ -398,7 +413,7 @@ function number_eltype(X::UMVTVector) end @doc raw""" - manifold_dimension(M::FixedRankMatrices{m,n,k,𝔽}) + manifold_dimension(M::FixedRankMatrices) Return the manifold dimension for the `𝔽`-valued [`FixedRankMatrices`](@ref) `M` of dimension `m`x`n` of rank `k`, namely @@ -409,7 +424,8 @@ of dimension `m`x`n` of rank `k`, namely where ``\dim_ℝ 𝔽`` is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ -function manifold_dimension(::FixedRankMatrices{m,n,k,𝔽}) where {m,n,k,𝔽} +function manifold_dimension(M::FixedRankMatrices{<:Any,𝔽}) where {𝔽} + m, n, k = get_mnk(M) return (m + n - k) * k * real_dimension(𝔽) end @@ -451,12 +467,8 @@ the point `vector_at` on the `FixedRankMatrices` manifold `M`. function Random.rand(M::FixedRankMatrices; vector_at=nothing, kwargs...) return rand(Random.default_rng(), M; vector_at=vector_at, kwargs...) end -function Random.rand( - rng::AbstractRNG, - M::FixedRankMatrices{m,n,k}; - vector_at=nothing, - kwargs..., -) where {m,n,k} +function Random.rand(rng::AbstractRNG, M::FixedRankMatrices; vector_at=nothing, kwargs...) + m, n, k = get_mnk(M) if vector_at === nothing p = SVDMPoint( Matrix{Float64}(undef, m, k), @@ -476,11 +488,12 @@ end function Random.rand!( rng::AbstractRNG, - ::FixedRankMatrices{m,n,k}, + M::FixedRankMatrices, pX; vector_at=nothing, kwargs..., -) where {m,n,k} +) + m, n, k = get_mnk(M) if vector_at === nothing U = rand(rng, Stiefel(m, k); kwargs...) S = sort(rand(rng, k); rev=true) @@ -503,12 +516,15 @@ function Random.rand!( end @doc raw""" - representation_size(M::FixedRankMatrices{m,n,k}) + representation_size(M::FixedRankMatrices) Return the element size of a point on the [`FixedRankMatrices`](@ref) `M`, i.e. the size of matrices on this manifold ``(m,n)``. """ -@generated representation_size(::FixedRankMatrices{m,n}) where {m,n} = (m, n) +function representation_size(M::FixedRankMatrices) + m, n, k = get_mnk(M) + return (m, n) +end @doc raw""" retract(M, p, X, ::PolarRetraction) @@ -524,12 +540,13 @@ singular values and ``U`` and ``V`` are shortened accordingly. retract(::FixedRankMatrices, ::Any, ::Any, ::PolarRetraction) function retract_polar!( - ::FixedRankMatrices{m,n,k}, + M::FixedRankMatrices, q::SVDMPoint, p::SVDMPoint, X::UMVTVector, t::Number, -) where {m,n,k} +) + m, n, k = get_mnk(M) tX = t * X QU, RU = qr([p.U tX.U]) QV, RV = qr([p.Vt' tX.Vt']) @@ -576,8 +593,15 @@ function riemannian_Hessian!(M::FixedRankMatrices, Y, p, G, H, X) return Y end -function Base.show(io::IO, ::FixedRankMatrices{M,N,K,𝔽}) where {M,N,K,𝔽} - return print(io, "FixedRankMatrices($(M), $(N), $(K), $(𝔽))") +function Base.show( + io::IO, + ::FixedRankMatrices{TypeParameter{Tuple{m,n,k}},𝔽}, +) where {m,n,k,𝔽} + return print(io, "FixedRankMatrices($(m), $(n), $(k), $(𝔽); parameter=:type)") +end +function Base.show(io::IO, M::FixedRankMatrices{Tuple{Int,Int,Int},𝔽}) where {𝔽} + m, n, k = get_mnk(M) + return print(io, "FixedRankMatrices($(m), $(n), $(k), $(𝔽))") end function Base.show(io::IO, ::MIME"text/plain", p::SVDMPoint) pre = " " @@ -632,7 +656,8 @@ Return a [`UMVTVector`](@ref) representing the zero tangent vector in the tangen `p` on the [`FixedRankMatrices`](@ref) `M`, for example all three elements of the resulting structure are zero matrices. """ -function zero_vector(::FixedRankMatrices{m,n,k}, p::SVDMPoint) where {m,n,k} +function zero_vector(M::FixedRankMatrices, p::SVDMPoint) + m, n, k = get_mnk(M) v = UMVTVector( zeros(eltype(p.U), m, k), zeros(eltype(p.S), k, k), @@ -641,9 +666,9 @@ function zero_vector(::FixedRankMatrices{m,n,k}, p::SVDMPoint) where {m,n,k} return v end -function zero_vector!(::FixedRankMatrices{m,n,k}, X::UMVTVector, p::SVDMPoint) where {m,n,k} - X.U .= zeros(eltype(X.U), m, k) - X.M .= zeros(eltype(X.M), k, k) - X.Vt .= zeros(eltype(X.Vt), k, n) +function zero_vector!(::FixedRankMatrices, X::UMVTVector, p::SVDMPoint) + X.U .= zero(eltype(X.U)) + X.M .= zero(eltype(X.M)) + X.Vt .= zero(eltype(X.Vt)) return X end diff --git a/src/manifolds/Multinomial.jl b/src/manifolds/Multinomial.jl index 53e5aad0da..d7468ba52b 100644 --- a/src/manifolds/Multinomial.jl +++ b/src/manifolds/Multinomial.jl @@ -18,22 +18,32 @@ The [`ProbabilitySimplex`](@ref) is stored internally within `M.manifold`, such # Constructor - MultinomialMatrices(n, m) + MultinomialMatrices(n::Int, m::Int; parameter::Symbol=:field) Generate the manifold of matrices $\mathbb R^{n×m}$ such that the $m$ columns are discrete probability distributions, i.e. sum up to one. + +`parameter`: whether a type parameter should be used to store `n` and `m`. By default size +is stored in a field. Value can either be `:field` or `:type`. """ -struct MultinomialMatrices{N,M,S} <: - AbstractPowerManifold{ℝ,ProbabilitySimplex{S},ArrayPowerRepresentation} where {N,M} - manifold::ProbabilitySimplex{S} +struct MultinomialMatrices{T,TPM<:ProbabilitySimplex} <: + AbstractPowerManifold{ℝ,TPM,ArrayPowerRepresentation} + size::T + manifold::TPM end -function MultinomialMatrices(n::Int, m::Int) - return MultinomialMatrices{n,m,n - 1}(ProbabilitySimplex(n - 1)) +function MultinomialMatrices(n::Int, m::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n, m)) + MPS = ProbabilitySimplex(n - 1; parameter=parameter) + return MultinomialMatrices{typeof(size),typeof(MPS)}(size, MPS) end -function Base.:^(M::ProbabilitySimplex{N}, m::Int) where {N} - return MultinomialMatrices{manifold_dimension(M) + 1,m,N}(M) +function Base.:^(::ProbabilitySimplex{TypeParameter{Tuple{N}}}, m::Int) where {N} + return MultinomialMatrices(N, m; parameter=:type) +end +function Base.:^(M::ProbabilitySimplex{Tuple{Int}}, m::Int) + n = get_n(M) + return MultinomialMatrices(n + 1, m) end @doc raw""" @@ -44,7 +54,8 @@ of `m` discrete probability distributions as columns from $\mathbb R^{n}$, i.e. [`ProbabilitySimplex`](@ref)`(n-1)`. """ check_point(::MultinomialMatrices, ::Any) -function check_point(M::MultinomialMatrices{n,m}, p; kwargs...) where {n,m} +function check_point(M::MultinomialMatrices, p; kwargs...) + n, m = get_nm(M) return check_point(PowerManifold(M.manifold, m), p; kwargs...) end @@ -55,17 +66,34 @@ Checks whether `X` is a valid tangent vector to `p` on the [`MultinomialMatrices This means, that `p` is valid, that `X` is of correct dimension and columnswise a tangent vector to the columns of `p` on the [`ProbabilitySimplex`](@ref). """ -function check_vector(M::MultinomialMatrices{n,m}, p, X; kwargs...) where {n,m} +function check_vector(M::MultinomialMatrices, p, X; kwargs...) + n, m = get_nm(M) return check_vector(PowerManifold(M.manifold, m), p, X; kwargs...) end -get_iterator(::MultinomialMatrices{n,m}) where {n,m} = Base.OneTo(m) +get_nm(::MultinomialMatrices{TypeParameter{Tuple{n,m}}}) where {n,m} = (n, m) +get_nm(M::MultinomialMatrices{Tuple{Int,Int}}) = get_parameter(M.size) -@generated manifold_dimension(::MultinomialMatrices{n,m}) where {n,m} = (n - 1) * m -@generated power_dimensions(::MultinomialMatrices{n,m}) where {n,m} = (m,) +function get_iterator(M::MultinomialMatrices) + n, m = get_nm(M) + return Base.OneTo(m) +end -@generated representation_size(::MultinomialMatrices{n,m}) where {n,m} = (n, m) +function manifold_dimension(M::MultinomialMatrices) + n, m = get_nm(M) + return (n - 1) * m +end +function power_dimensions(M::MultinomialMatrices) + n, m = get_nm(M) + return (m,) +end + +representation_size(M::MultinomialMatrices) = get_nm(M) -function Base.show(io::IO, ::MultinomialMatrices{n,m}) where {n,m} - return print(io, "MultinomialMatrices($(n),$(m))") +function Base.show(io::IO, ::MultinomialMatrices{TypeParameter{Tuple{n,m}}}) where {n,m} + return print(io, "MultinomialMatrices($(n), $(m); parameter=:type)") +end +function Base.show(io::IO, M::MultinomialMatrices{Tuple{Int,Int}}) + n, m = get_nm(M) + return print(io, "MultinomialMatrices($(n), $(m))") end diff --git a/src/manifolds/MultinomialDoublyStochastic.jl b/src/manifolds/MultinomialDoublyStochastic.jl index 9e02dd6e88..c2b408a0e7 100644 --- a/src/manifolds/MultinomialDoublyStochastic.jl +++ b/src/manifolds/MultinomialDoublyStochastic.jl @@ -1,18 +1,18 @@ @doc raw""" - AbstractMultinomialDoublyStochastic{N} <: AbstractDecoratorManifold{ℝ} + AbstractMultinomialDoublyStochastic <: AbstractDecoratorManifold{ℝ} A common type for manifolds that are doubly stochastic, for example by direct constraint [`MultinomialDoubleStochastic`](@ref) or by symmetry [`MultinomialSymmetric`](@ref), as long as they are also modeled as [`IsIsometricEmbeddedManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/decorator.html#ManifoldsBase.IsIsometricEmbeddedManifold). """ -abstract type AbstractMultinomialDoublyStochastic{N} <: AbstractDecoratorManifold{ℝ} end +abstract type AbstractMultinomialDoublyStochastic <: AbstractDecoratorManifold{ℝ} end function active_traits(f, ::AbstractMultinomialDoublyStochastic, args...) return merge_traits(IsIsometricEmbeddedManifold()) end @doc raw""" - MultinomialDoublyStochastic{n} <: AbstractMultinomialDoublyStochastic{N} + MultinomialDoublyStochastic{T} <: AbstractMultinomialDoublyStochastic The set of doubly stochastic multinomial matrices consists of all $n×n$ matrices with stochastic columns and rows, i.e. @@ -41,14 +41,17 @@ More details can be found in Section III [DouikHassibi:2019](@cite). # Constructor - MultinomialDoubleStochastic(n) + MultinomialDoubleStochastic(n::Int; parameter::Symbol=:field) Generate the manifold of matrices $\mathbb R^{n×n}$ that are doubly stochastic and symmetric. """ -struct MultinomialDoubleStochastic{N} <: AbstractMultinomialDoublyStochastic{N} end +struct MultinomialDoubleStochastic{T} <: AbstractMultinomialDoublyStochastic + size::T +end -function MultinomialDoubleStochastic(n::Int) - return MultinomialDoubleStochastic{n}() +function MultinomialDoubleStochastic(n::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return MultinomialDoubleStochastic{typeof(size)}(size) end @doc raw""" @@ -57,7 +60,8 @@ end Checks whether `p` is a valid point on the [`MultinomialDoubleStochastic`](@ref)`(n)` `M`, i.e. is a matrix with positive entries whose rows and columns sum to one. """ -function check_point(M::MultinomialDoubleStochastic{n}, p; kwargs...) where {n} +function check_point(M::MultinomialDoubleStochastic, p; kwargs...) + n = get_n(M) r = sum(p, dims=2) if !isapprox(norm(r - ones(n, 1)), 0.0; kwargs...) return DomainError( @@ -74,7 +78,7 @@ Checks whether `X` is a valid tangent vector to `p` on the [`MultinomialDoubleSt This means, that `p` is valid, that `X` is of correct dimension and sums to zero along any column or row. """ -function check_vector(M::MultinomialDoubleStochastic{n}, p, X; kwargs...) where {n} +function check_vector(M::MultinomialDoubleStochastic, p, X; kwargs...) r = sum(X, dims=2) # check for stochastic rows if !isapprox(norm(r), 0.0; kwargs...) return DomainError( @@ -85,10 +89,17 @@ function check_vector(M::MultinomialDoubleStochastic{n}, p, X; kwargs...) where return nothing end -function get_embedding(::MultinomialDoubleStochastic{N}) where {N} - return MultinomialMatrices(N, N) +function get_embedding(::MultinomialDoubleStochastic{TypeParameter{Tuple{n}}}) where {n} + return MultinomialMatrices(n, n; parameter=:type) +end +function get_embedding(M::MultinomialDoubleStochastic{Tuple{Int}}) + n = get_n(M) + return MultinomialMatrices(n, n) end +get_n(::MultinomialDoubleStochastic{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::MultinomialDoubleStochastic{Tuple{Int}}) = get_parameter(M.size)[1] + """ is_flat(::MultinomialDoubleStochastic) @@ -97,7 +108,7 @@ Return false. [`MultinomialDoubleStochastic`](@ref) is not a flat manifold. is_flat(M::MultinomialDoubleStochastic) = false @doc raw""" - manifold_dimension(M::MultinomialDoubleStochastic{n}) where {n} + manifold_dimension(M::MultinomialDoubleStochastic) returns the dimension of the [`MultinomialDoubleStochastic`](@ref) manifold namely @@ -105,12 +116,13 @@ namely \operatorname{dim}_{\mathcal{DP}(n)} = (n-1)^2. ```` """ -@generated function manifold_dimension(::MultinomialDoubleStochastic{n}) where {n} +function manifold_dimension(M::MultinomialDoubleStochastic) + n = get_n(M) return (n - 1)^2 end @doc raw""" - project(M::MultinomialDoubleStochastic{n}, p, Y) where {n} + project(M::MultinomialDoubleStochastic, p, Y) Project `Y` onto the tangent space at `p` on the [`MultinomialDoubleStochastic`](@ref) `M`, return the result in `X`. The formula reads @@ -130,7 +142,8 @@ where $I_n$ is the $n×n$ unit matrix and $\mathbf{1}_n$ is the vector of length """ project(::MultinomialDoubleStochastic, ::Any, ::Any) -function project!(::MultinomialDoubleStochastic{n}, X, p, Y) where {n} +function project!(M::MultinomialDoubleStochastic, X, p, Y) + n = get_n(M) ζ = [I p; p I] \ [sum(Y, dims=2); sum(Y, dims=1)'] # Formula (25) from 1802.02628 return X .= Y .- (repeat(ζ[1:n], 1, 3) .+ repeat(ζ[(n + 1):end]', 3, 1)) .* p end @@ -153,12 +166,12 @@ function project(M::AbstractMultinomialDoublyStochastic, p; kwargs...) end function project!( - ::AbstractMultinomialDoublyStochastic{n}, + ::AbstractMultinomialDoublyStochastic, q, p; maxiter=100, tolerance=eps(eltype(p)), -) where {n} +) any(p .<= 0) && throw( DomainError( "The matrix $p can not be projected, since it has nonpositive entries.", @@ -180,7 +193,8 @@ function project!( return q end -@generated function representation_size(::MultinomialDoubleStochastic{n}) where {n} +function representation_size(M::MultinomialDoubleStochastic) + n = get_n(M) return (n, n) end @@ -197,6 +211,10 @@ function retract_project!(M::MultinomialDoubleStochastic, q, p, X, t::Number) return project!(M, q, p .* exp.(t .* X ./ p)) end -function Base.show(io::IO, ::MultinomialDoubleStochastic{n}) where {n} +function Base.show(io::IO, ::MultinomialDoubleStochastic{TypeParameter{Tuple{n}}}) where {n} + return print(io, "MultinomialDoubleStochastic($(n); parameter=:type)") +end +function Base.show(io::IO, M::MultinomialDoubleStochastic{Tuple{Int}}) + n = get_n(M) return print(io, "MultinomialDoubleStochastic($(n))") end diff --git a/src/manifolds/MultinomialSymmetric.jl b/src/manifolds/MultinomialSymmetric.jl index 3ee993c120..756a15a774 100644 --- a/src/manifolds/MultinomialSymmetric.jl +++ b/src/manifolds/MultinomialSymmetric.jl @@ -1,5 +1,5 @@ @doc raw""" - MultinomialSymmetric{n} <: AbstractMultinomialDoublyStochastic{N} + MultinomialSymmetric{T} <: AbstractMultinomialDoublyStochastic{N} The multinomial symmetric matrices manifold consists of all symmetric $n×n$ matrices with positive entries such that each column sums to one, i.e. @@ -39,10 +39,13 @@ More details can be found in Section IV [DouikHassibi:2019](@cite). Generate the manifold of matrices $\mathbb R^{n×n}$ that are doubly stochastic and symmetric. """ -struct MultinomialSymmetric{N} <: AbstractMultinomialDoublyStochastic{N} end +struct MultinomialSymmetric{T} <: AbstractMultinomialDoublyStochastic + size::T +end -function MultinomialSymmetric(n::Int) - return MultinomialSymmetric{n}() +function MultinomialSymmetric(n::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return MultinomialSymmetric{typeof(size)}(size) end @doc raw""" @@ -51,7 +54,8 @@ end Checks whether `p` is a valid point on the [`MultinomialSymmetric`](@ref)`(m,n)` `M`, i.e. is a symmetric matrix with positive entries whose rows sum to one. """ -function check_point(M::MultinomialSymmetric{n}, p; kwargs...) where {n} +function check_point(M::MultinomialSymmetric, p; kwargs...) + n = get_n(M) return check_point(SymmetricMatrices(n, ℝ), p) end @doc raw""" @@ -61,17 +65,25 @@ Checks whether `X` is a valid tangent vector to `p` on the [`MultinomialSymmetri This means, that `p` is valid, that `X` is of correct dimension, symmetric, and sums to zero along any row. """ -function check_vector(M::MultinomialSymmetric{n}, p, X; kwargs...) where {n} +function check_vector(M::MultinomialSymmetric, p, X; kwargs...) + n = get_n(M) return check_vector(SymmetricMatrices(n, ℝ), p, X; kwargs...) end -function get_embedding(::MultinomialSymmetric{N}) where {N} - return MultinomialMatrices(N, N) -end - embed!(::MultinomialSymmetric, q, p) = copyto!(q, p) embed!(::MultinomialSymmetric, Y, ::Any, X) = copyto!(Y, X) +function get_embedding(::MultinomialSymmetric{TypeParameter{Tuple{n}}}) where {n} + return MultinomialMatrices(n, n; parameter=:type) +end +function get_embedding(M::MultinomialSymmetric{Tuple{Int}}) + n = get_n(M) + return MultinomialMatrices(n, n) +end + +get_n(::MultinomialSymmetric{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::MultinomialSymmetric{Tuple{Int}}) = get_parameter(M.size)[1] + """ is_flat(::MultinomialSymmetric) @@ -80,7 +92,7 @@ Return false. [`MultinomialSymmetric`](@ref) is not a flat manifold. is_flat(M::MultinomialSymmetric) = false @doc raw""" - manifold_dimension(M::MultinomialSymmetric{n}) where {n} + manifold_dimension(M::MultinomialSymmetric) returns the dimension of the [`MultinomialSymmetric`](@ref) manifold namely @@ -88,12 +100,13 @@ namely \operatorname{dim}_{\mathcal{SP}(n)} = \frac{n(n-1)}{2}. ```` """ -@generated function manifold_dimension(::MultinomialSymmetric{n}) where {n} +function manifold_dimension(M::MultinomialSymmetric) + n = get_n(M) return div(n * (n - 1), 2) end @doc raw""" - project(M::MultinomialSymmetric{n}, p, Y) where {n} + project(M::MultinomialSymmetric, p, Y) Project `Y` onto the tangent space at `p` on the [`MultinomialSymmetric`](@ref) `M`, return the result in `X`. The formula reads @@ -110,12 +123,13 @@ where $I_n$ is teh $n×n$ unit matrix and $\mathbf{1}_n$ is the vector of length """ project(::MultinomialSymmetric, ::Any, ::Any) -function project!(::MultinomialSymmetric{n}, X, p, Y) where {n} +function project!(::MultinomialSymmetric, X, p, Y) α = (I + p) \ sum(Y, dims=2) # Formula (49) from 1802.02628 return X .= Y .- (repeat(α, 1, 3) .+ repeat(α', 3, 1)) .* p end -@generated function representation_size(::MultinomialSymmetric{n}) where {n} +function representation_size(M::MultinomialSymmetric) + n = get_n(M) return (n, n) end @@ -132,6 +146,10 @@ function retract_project!(M::MultinomialSymmetric, q, p, X, t::Number) return project!(M, q, p .* exp.(t .* X ./ p)) end -function Base.show(io::IO, ::MultinomialSymmetric{n}) where {n} +function Base.show(io::IO, ::MultinomialSymmetric{TypeParameter{Tuple{n}}}) where {n} + return print(io, "MultinomialSymmetric($(n); parameter=:type)") +end +function Base.show(io::IO, M::MultinomialSymmetric{Tuple{Int}}) + n = get_n(M) return print(io, "MultinomialSymmetric($(n))") end diff --git a/src/manifolds/ProbabilitySimplex.jl b/src/manifolds/ProbabilitySimplex.jl index 83fea8ec58..a235c05075 100644 --- a/src/manifolds/ProbabilitySimplex.jl +++ b/src/manifolds/ProbabilitySimplex.jl @@ -1,5 +1,5 @@ @doc raw""" - ProbabilitySimplex{n,boundary} <: AbstractDecoratorManifold{𝔽} + ProbabilitySimplex{T,boundary} <: AbstractDecoratorManifold{𝔽} The (relative interior of) the probability simplex is the set ````math @@ -34,9 +34,11 @@ This implementation follows the notation in [AastroemPetraSchmitzerSchnoerr:2017 ProbabilitySimplex(n::Int; boundary::Symbol=:open) """ -struct ProbabilitySimplex{n,boundary} <: AbstractDecoratorManifold{ℝ} end +struct ProbabilitySimplex{T,boundary} <: AbstractDecoratorManifold{ℝ} + size::T +end -function ProbabilitySimplex(n::Int; boundary::Symbol=:open) +function ProbabilitySimplex(n::Int; boundary::Symbol=:open, parameter::Symbol=:field) if boundary !== :open && boundary !== :closed throw( ArgumentError( @@ -44,7 +46,8 @@ function ProbabilitySimplex(n::Int; boundary::Symbol=:open) ), ) end - return ProbabilitySimplex{n,boundary}() + size = wrap_type_parameter(parameter, (n,)) + return ProbabilitySimplex{typeof(size),boundary}(size) end """ @@ -96,7 +99,7 @@ Check whether `p` is a valid point on the [`ProbabilitySimplex`](@ref) `M`, i.e. the embedding with positive entries that sum to one The tolerance for the last test can be set using the `kwargs...`. """ -function check_point(M::ProbabilitySimplex{n,boundary}, p; kwargs...) where {n,boundary} +function check_point(M::ProbabilitySimplex{<:Any,boundary}, p; kwargs...) where {boundary} if boundary === :closed && minimum(p) < 0 return DomainError( minimum(p), @@ -180,15 +183,10 @@ function exp!(::ProbabilitySimplex, q, p, X) return q end -function get_coordinates_orthonormal!( - M::ProbabilitySimplex{N}, - Xc, - p, - X, - R::RealNumbers, -) where {N} +function get_coordinates_orthonormal!(M::ProbabilitySimplex, Xc, p, X, R::RealNumbers) + n = get_n(M) get_coordinates_orthonormal!( - Sphere(N), + Sphere(n), Xc, simplex_to_amplitude(M, p), simplex_to_amplitude_diff(M, p, X), @@ -197,28 +195,32 @@ function get_coordinates_orthonormal!( return Xc end -get_embedding(M::ProbabilitySimplex) = Euclidean(representation_size(M)...; field=ℝ) +function get_embedding(::ProbabilitySimplex{TypeParameter{Tuple{n}}}) where {n} + return Euclidean(n + 1; parameter=:type) +end +function get_embedding(M::ProbabilitySimplex{Tuple{Int}}) + n = get_n(M) + return Euclidean(n + 1) +end + +get_n(::ProbabilitySimplex{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::ProbabilitySimplex{Tuple{Int}}) = get_parameter(M.size)[1] -function get_vector_orthonormal!( - M::ProbabilitySimplex{N}, - Y, - p, - Xc, - R::RealNumbers, -) where {N} +function get_vector_orthonormal!(M::ProbabilitySimplex, Y, p, Xc, R::RealNumbers) + n = get_n(M) ps = simplex_to_amplitude(M, p) - X = get_vector_orthonormal(Sphere(N), ps, Xc, R) + X = get_vector_orthonormal(Sphere(n), ps, Xc, R) return amplitude_to_simplex_diff!(M, Y, ps, X) end @doc raw""" - injectivity_radius(M, p) + injectivity_radius(M::ProbabilitySimplex, p) Compute the injectivity radius on the [`ProbabilitySimplex`](@ref) `M` at the point `p`, i.e. the distanceradius to a point near/on the boundary, that could be reached by following the geodesic. """ -function injectivity_radius(::ProbabilitySimplex{n}, p) where {n} +function injectivity_radius(::ProbabilitySimplex, p) i = argmin(p) s = sum(p) - p[i] return 2 * acos(sqrt(s)) @@ -240,7 +242,7 @@ g_p(X,Y) = \sum_{i=1}^{n+1}\frac{X_iY_i}{p_i} When `M` includes boundary, we can just skip coordinates where ``p_i`` is equal to 0, see Proposition 2.1 in [AyJostLeSchwachhoefer:2017](@cite). """ -function inner(::ProbabilitySimplex{n,boundary}, p, X, Y) where {n,boundary} +function inner(::ProbabilitySimplex{<:Any,boundary}, p, X, Y) where {boundary} d = zero(Base.promote_eltype(p, X, Y)) if boundary === :closed @inbounds for i in eachindex(p, X, Y) @@ -267,7 +269,7 @@ where $\mathbb{1}^{m,n}$ is the size `(m,n)` matrix containing ones, and $\log$ """ inverse_retract(::ProbabilitySimplex, ::Any, ::Any, ::SoftmaxInverseRetraction) -function inverse_retract_softmax!(::ProbabilitySimplex{n}, X, p, q) where {n} +function inverse_retract_softmax!(::ProbabilitySimplex, X, p, q) X .= log.(q) .- log.(p) meanlogdiff = mean(X) X .-= meanlogdiff @@ -306,23 +308,26 @@ function log!(::ProbabilitySimplex, X, p, q) end @doc raw""" - manifold_dimension(M::ProbabilitySimplex{n}) + manifold_dimension(M::ProbabilitySimplex) Returns the manifold dimension of the probability simplex in $ℝ^{n+1}$, i.e. ````math \dim_{Δ^n} = n. ```` """ -manifold_dimension(::ProbabilitySimplex{n}) where {n} = n +manifold_dimension(M::ProbabilitySimplex) = get_n(M) @doc raw""" - manifold_volume(::ProbabilitySimplex{n}) where {n} + manifold_volume(::ProbabilitySimplex) Return the volume of the [`ProbabilitySimplex`](@ref), i.e. volume of the `n`-dimensional [`Sphere`](@ref) divided by ``2^{n+1}``, corresponding to the volume of its positive orthant. """ -manifold_volume(::ProbabilitySimplex{n}) where {n} = manifold_volume(Sphere(n)) / 2^(n + 1) +function manifold_volume(M::ProbabilitySimplex) + n = get_n(M) + return manifold_volume(Sphere(n)) / 2^(n + 1) +end @doc raw""" mean( @@ -340,10 +345,11 @@ mean(::ProbabilitySimplex, ::Any...) default_estimation_method(::ProbabilitySimplex, ::typeof(mean)) = GeodesicInterpolation() -function parallel_transport_to!(M::ProbabilitySimplex{N}, Y, p, X, q) where {N} +function parallel_transport_to!(M::ProbabilitySimplex, Y, p, X, q) + n = get_n(M) q_s = simplex_to_amplitude(M, q) Ys = parallel_transport_to( - Sphere(N), + Sphere(n), simplex_to_amplitude(M, p), simplex_to_amplitude_diff(M, p, X), q_s, @@ -430,12 +436,15 @@ function project!(::ProbabilitySimplex, q, p) end @doc raw""" - representation_size(::ProbabilitySimplex{n}) + representation_size(::ProbabilitySimplex) Return the representation size of points in the $n$-dimensional probability simplex, i.e. an array size of `(n+1,)`. """ -representation_size(::ProbabilitySimplex{n}) where {n} = (n + 1,) +function representation_size(M::ProbabilitySimplex) + n = get_n(M) + return (n + 1,) +end @doc raw""" retract(M::ProbabilitySimplex, p, X, ::SoftmaxRetraction) @@ -461,8 +470,8 @@ function retract_softmax!(::ProbabilitySimplex, q, p, X, t::Number) end @doc raw""" - X = riemannian_gradient(M::ProbabilitySimplex{n}, p, Y) - riemannian_gradient!(M::ProbabilitySimplex{n}, X, p, Y) + X = riemannian_gradient(M::ProbabilitySimplex, p, Y) + riemannian_gradient!(M::ProbabilitySimplex, X, p, Y) Given a gradient ``Y = \operatorname{grad} \tilde f(p)`` in the embedding ``ℝ^{n+1}`` of the [`ProbabilitySimplex`](@ref) ``Δ^n``, this function computes the Riemannian gradient @@ -490,10 +499,11 @@ It is computed using isometry with positive orthant of a sphere. """ riemann_tensor(::ProbabilitySimplex, p, X, Y, Z) -function riemann_tensor!(M::ProbabilitySimplex{N}, Xresult, p, X, Y, Z) where {N} +function riemann_tensor!(M::ProbabilitySimplex, Xresult, p, X, Y, Z) + n = get_n(M) pe = simplex_to_amplitude(M, p) Xrs = riemann_tensor( - Sphere(N), + Sphere(n), pe, simplex_to_amplitude_diff(M, p, X), simplex_to_amplitude_diff(M, p, Y), @@ -503,19 +513,27 @@ function riemann_tensor!(M::ProbabilitySimplex{N}, Xresult, p, X, Y, Z) where {N return Xresult end -function Base.show(io::IO, ::ProbabilitySimplex{n,boundary}) where {n,boundary} +function Base.show( + io::IO, + ::ProbabilitySimplex{TypeParameter{Tuple{n}},boundary}, +) where {n,boundary} + return print(io, "ProbabilitySimplex($(n); boundary=:$boundary, parameter=:type)") +end +function Base.show(io::IO, M::ProbabilitySimplex{Tuple{Int},boundary}) where {boundary} + n = get_n(M) return print(io, "ProbabilitySimplex($(n); boundary=:$boundary)") end @doc raw""" - volume_density(M::ProbabilitySimplex{N}, p, X) where {N} + volume_density(M::ProbabilitySimplex, p, X) Compute the volume density at point `p` on [`ProbabilitySimplex`](@ref) `M` for tangent vector `X`. It is computed using isometry with positive orthant of a sphere. """ -function volume_density(M::ProbabilitySimplex{N}, p, X) where {N} +function volume_density(M::ProbabilitySimplex, p, X) + n = get_n(M) pe = simplex_to_amplitude(M, p) - return volume_density(Sphere(N), pe, simplex_to_amplitude_diff(M, p, X)) + return volume_density(Sphere(n), pe, simplex_to_amplitude_diff(M, p, X)) end @doc raw""" @@ -545,7 +563,7 @@ function simplex_to_amplitude!(::ProbabilitySimplex, q, p) end @doc raw""" - amplitude_to_simplex(M::ProbabilitySimplex{N}, p) where {N} + amplitude_to_simplex(M::ProbabilitySimplex, p) Convert point (real) probability amplitude `p` on to a point on [`ProbabilitySimplex`](@ref Manifolds.ProbabilitySimplex). The formula reads ``(p_1^2, p_2^2, …, p_{N+1}^2)``. This is an isometry from the interior of diff --git a/src/manifolds/ProbabilitySimplexEuclideanMetric.jl b/src/manifolds/ProbabilitySimplexEuclideanMetric.jl index b44540a3a7..9deb63f70f 100644 --- a/src/manifolds/ProbabilitySimplexEuclideanMetric.jl +++ b/src/manifolds/ProbabilitySimplexEuclideanMetric.jl @@ -15,9 +15,8 @@ end Return the volume of the [`ProbabilitySimplex`](@ref) with the Euclidean metric. The formula reads ``\frac{\sqrt{n+1}}{n!}`` """ -function manifold_volume( - ::MetricManifold{ℝ,<:ProbabilitySimplex{n},<:EuclideanMetric}, -) where {n} +function manifold_volume(M::MetricManifold{ℝ,<:ProbabilitySimplex,<:EuclideanMetric}) + n = get_n(M.manifold) return sqrt(n + 1) / factorial(n) end diff --git a/src/manifolds/ProjectiveSpace.jl b/src/manifolds/ProjectiveSpace.jl index 2973d76115..4d23e24bef 100644 --- a/src/manifolds/ProjectiveSpace.jl +++ b/src/manifolds/ProjectiveSpace.jl @@ -91,7 +91,7 @@ end function ArrayProjectiveSpace( n::Vararg{Int,I}; field::AbstractNumbers=ℝ, - parameter=:field, + parameter::Symbol=:field, ) where {I} size = wrap_type_parameter(parameter, n) return ArrayProjectiveSpace{typeof(size),field}(size) diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index edb716f7b1..bb3bca0001 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -96,7 +96,11 @@ Generate sphere in $𝔽^{n_1, n_2, …, n_i}$, where $𝔽$ defaults to the rea struct ArraySphere{T,𝔽} <: AbstractSphere{𝔽} size::T end -function ArraySphere(n::Vararg{Int,I}; field::AbstractNumbers=ℝ, parameter=:field) where {I} +function ArraySphere( + n::Vararg{Int,I}; + field::AbstractNumbers=ℝ, + parameter::Symbol=:field, +) where {I} size = wrap_type_parameter(parameter, n) return ArraySphere{typeof(size),field}(size) end diff --git a/src/manifolds/SphereSymmetricMatrices.jl b/src/manifolds/SphereSymmetricMatrices.jl index dc4bf5465e..c730d53c9b 100644 --- a/src/manifolds/SphereSymmetricMatrices.jl +++ b/src/manifolds/SphereSymmetricMatrices.jl @@ -1,5 +1,5 @@ @doc raw""" - SphereSymmetricMatrices{n,𝔽} <: AbstractEmbeddedManifold{ℝ,TransparentIsometricEmbedding} + SphereSymmetricMatrices{T,𝔽} <: AbstractEmbeddedManifold{ℝ,TransparentIsometricEmbedding} The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) consisting of the $n × n$ symmetric matrices of unit Frobenius norm, i.e. @@ -14,10 +14,13 @@ and the field $𝔽 ∈ \{ ℝ, ℂ\}$. Generate the manifold of `n`-by-`n` symmetric matrices of unit Frobenius norm. """ -struct SphereSymmetricMatrices{N,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct SphereSymmetricMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end -function SphereSymmetricMatrices(n::Int, field::AbstractNumbers=ℝ) - return SphereSymmetricMatrices{n,field}() +function SphereSymmetricMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return SphereSymmetricMatrices{typeof(size),field}(size) end function active_traits(f, ::SphereSymmetricMatrices, arge...) @@ -25,14 +28,14 @@ function active_traits(f, ::SphereSymmetricMatrices, arge...) end @doc raw""" - check_point(M::SphereSymmetricMatrices{n,𝔽}, p; kwargs...) + check_point(M::SphereSymmetricMatrices, p; kwargs...) Check whether the matrix is a valid point on the [`SphereSymmetricMatrices`](@ref) `M`, i.e. is an `n`-by-`n` symmetric matrix of unit Frobenius norm. The tolerance for the symmetry of `p` can be set using `kwargs...`. """ -function check_point(M::SphereSymmetricMatrices{n,𝔽}, p; kwargs...) where {n,𝔽} +function check_point(M::SphereSymmetricMatrices, p; kwargs...) if !isapprox(norm(p - p'), 0.0; kwargs...) return DomainError( norm(p - p'), @@ -43,7 +46,7 @@ function check_point(M::SphereSymmetricMatrices{n,𝔽}, p; kwargs...) where {n, end """ - check_vector(M::SphereSymmetricMatrices{n,𝔽}, p, X; kwargs... ) + check_vector(M::SphereSymmetricMatrices, p, X; kwargs... ) Check whether `X` is a tangent vector to manifold point `p` on the [`SphereSymmetricMatrices`](@ref) `M`, i.e. `X` has to be a symmetric matrix of size `(n,n)` @@ -51,7 +54,7 @@ of unit Frobenius norm. The tolerance for the symmetry of `p` and `X` can be set using `kwargs...`. """ -function check_vector(M::SphereSymmetricMatrices{n,𝔽}, p, X; kwargs...) where {n,𝔽} +function check_vector(M::SphereSymmetricMatrices, p, X; kwargs...) if !isapprox(norm(X - X'), 0.0; kwargs...) return DomainError( norm(X - X'), @@ -64,10 +67,17 @@ end embed(::SphereSymmetricMatrices, p) = p embed(::SphereSymmetricMatrices, p, X) = X -function get_embedding(::SphereSymmetricMatrices{n,𝔽}) where {n,𝔽} +function get_embedding(::SphereSymmetricMatrices{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return ArraySphere(n, n; field=𝔽, parameter=:type) +end +function get_embedding(M::SphereSymmetricMatrices{Tuple{Int},𝔽}) where {𝔽} + n = get_n(M) return ArraySphere(n, n; field=𝔽) end +get_n(::SphereSymmetricMatrices{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::SphereSymmetricMatrices{Tuple{Int}}) = get_parameter(M.size)[1] + """ is_flat(::SphereSymmetricMatrices) @@ -76,7 +86,7 @@ Return false. [`SphereSymmetricMatrices`](@ref) is not a flat manifold. is_flat(M::SphereSymmetricMatrices) = false @doc raw""" - manifold_dimension(M::SphereSymmetricMatrices{n,𝔽}) + manifold_dimension(M::SphereSymmetricMatrices{<:Any,𝔽}) Return the manifold dimension of the [`SphereSymmetricMatrices`](@ref) `n`-by-`n` symmetric matrix `M` of unit Frobenius norm over the number system `𝔽`, i.e. @@ -88,7 +98,8 @@ Frobenius norm over the number system `𝔽`, i.e. \end{aligned} ```` """ -function manifold_dimension(::SphereSymmetricMatrices{n,𝔽}) where {n,𝔽} +function manifold_dimension(M::SphereSymmetricMatrices{<:Any,𝔽}) where {𝔽} + n = get_n(M) return div(n * (n + 1), 2) * real_dimension(𝔽) - (𝔽 === ℂ ? n : 0) - 1 end @@ -124,8 +135,15 @@ function project!(M::SphereSymmetricMatrices, Y, p, X) return project!(get_embedding(M), Y, p, (X .+ X') ./ 2) end -@generated representation_size(::SphereSymmetricMatrices{n,𝔽}) where {n,𝔽} = (n, n) +function representation_size(M::SphereSymmetricMatrices) + n = get_n(M) + return (n, n) +end -function Base.show(io::IO, ::SphereSymmetricMatrices{n,𝔽}) where {n,𝔽} +function Base.show(io::IO, ::SphereSymmetricMatrices{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return print(io, "SphereSymmetricMatrices($(n), $(𝔽); parameter=:type)") +end +function Base.show(io::IO, M::SphereSymmetricMatrices{Tuple{Int},𝔽}) where {𝔽} + n = get_n(M) return print(io, "SphereSymmetricMatrices($(n), $(𝔽))") end diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 8259f1bb17..47a171ba94 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -25,7 +25,7 @@ struct SymmetricMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T end -function SymmetricMatrices(n::Int, field::AbstractNumbers=ℝ; parameter=:field) +function SymmetricMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) size = wrap_type_parameter(parameter, (n,)) return SymmetricMatrices{typeof(size),field}(size) end diff --git a/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl b/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl index c1232f4d5b..c9e6532820 100644 --- a/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl +++ b/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl @@ -1,5 +1,5 @@ @doc raw""" - SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽} <: AbstractDecoratorManifold{𝔽} + SymmetricPositiveSemidefiniteFixedRank{T,𝔽} <: AbstractDecoratorManifold{𝔽} The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) $ \operatorname{SPS}_k(n)$ consisting of the real- or complex-valued symmetric positive semidefinite matrices of size $n × n$ and rank $k$, i.e. the set @@ -35,15 +35,23 @@ The metric was used in [JourneeBachAbsilSepulchre:2010](@cite)[MassartAbsil:2020 # Constructor - SymmetricPositiveSemidefiniteFixedRank(n::Int, k::Int, field::AbstractNumbers=ℝ) + SymmetricPositiveSemidefiniteFixedRank(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) Generate the manifold of $n × n$ symmetric positive semidefinite matrices of rank $k$ over the `field` of real numbers `ℝ` or complex numbers `ℂ`. """ -struct SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct SymmetricPositiveSemidefiniteFixedRank{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end -function SymmetricPositiveSemidefiniteFixedRank(n::Int, k::Int, field::AbstractNumbers=ℝ) - return SymmetricPositiveSemidefiniteFixedRank{n,k,field}() +function SymmetricPositiveSemidefiniteFixedRank( + n::Int, + k::Int, + field::AbstractNumbers=ℝ; + parameter::Symbol=:field, +) + size = wrap_type_parameter(parameter, (n, k)) + return SymmetricPositiveSemidefiniteFixedRank{typeof(size),field}(size) end function active_traits(f, ::SymmetricPositiveSemidefiniteFixedRank, args...) @@ -51,7 +59,7 @@ function active_traits(f, ::SymmetricPositiveSemidefiniteFixedRank, args...) end @doc raw""" - check_point(M::SymmetricPositiveSemidefiniteFixedRank{n,𝔽}, q; kwargs...) + check_point(M::SymmetricPositiveSemidefiniteFixedRank, q; kwargs...) Check whether `q` is a valid manifold point on the [`SymmetricPositiveSemidefiniteFixedRank`](@ref) `M`, i.e. whether `p=q*q'` is a symmetric matrix of size `(n,n)` with values from the corresponding @@ -59,11 +67,8 @@ whether `p=q*q'` is a symmetric matrix of size `(n,n)` with values from the corr The symmetry of `p` is not explicitly checked since by using `q` p is symmetric by construction. The tolerance for the symmetry of `p` can and the rank of `q*q'` be set using `kwargs...`. """ -function check_point( - M::SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽}, - q; - kwargs..., -) where {n,k,𝔽} +function check_point(M::SymmetricPositiveSemidefiniteFixedRank, q; kwargs...) + n, k = get_nk(M) p = q * q' r = rank(p * p'; kwargs...) if r < k @@ -76,7 +81,7 @@ function check_point( end """ - check_vector(M::SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽}, p, X; kwargs... ) + check_vector(M::SymmetricPositiveSemidefiniteFixedRank, p, X; kwargs... ) Check whether `X` is a tangent vector to manifold point `p` on the [`SymmetricPositiveSemidefiniteFixedRank`](@ref) `M`, i.e. `X` has to be a symmetric matrix of size `(n,n)` @@ -86,10 +91,25 @@ Due to the reduced representation this is fulfilled as soon as the matrix is of """ check_vector(M::SymmetricPositiveSemidefiniteFixedRank, q, Y; kwargs...) -function get_embedding(::SymmetricPositiveSemidefiniteFixedRank{N,K,𝔽}) where {N,K,𝔽} - return Euclidean(N, K; field=𝔽) +function get_embedding( + ::SymmetricPositiveSemidefiniteFixedRank{TypeParameter{Tuple{n,k}},𝔽}, +) where {n,k,𝔽} + return Euclidean(n, k; field=𝔽, parameter=:type) +end +function get_embedding( + M::SymmetricPositiveSemidefiniteFixedRank{Tuple{Int,Int},𝔽}, +) where {𝔽} + n, k = get_nk(M) + return Euclidean(n, k; field=𝔽) end +function get_nk( + ::SymmetricPositiveSemidefiniteFixedRank{TypeParameter{Tuple{n,k}}}, +) where {n,k} + return (n, k) +end +get_nk(M::SymmetricPositiveSemidefiniteFixedRank{Tuple{Int,Int}}) = get_parameter(M.size) + @doc raw""" distance(M::SymmetricPositiveSemidefiniteFixedRank, p, q) @@ -172,7 +192,7 @@ function log!(::SymmetricPositiveSemidefiniteFixedRank, Z, q, p) end @doc raw""" - manifold_dimension(M::SymmetricPositiveSemidefiniteFixedRank{n,k,𝔽}) + manifold_dimension(M::SymmetricPositiveSemidefiniteFixedRank) Return the dimension of the [`SymmetricPositiveSemidefiniteFixedRank`](@ref) matrix `M` over the number system `𝔽`, i.e. @@ -188,15 +208,13 @@ where the last $k^2$ is due to the zero imaginary part for Hermitian matrices di """ manifold_dimension(::SymmetricPositiveSemidefiniteFixedRank) -@generated function manifold_dimension( - ::SymmetricPositiveSemidefiniteFixedRank{N,K,ℝ}, -) where {N,K} - return K * N - div(K * (K - 1), 2) +function manifold_dimension(M::SymmetricPositiveSemidefiniteFixedRank{<:Any,ℝ}) + n, k = get_nk(M) + return k * n - div(k * (k - 1), 2) end -@generated function manifold_dimension( - ::SymmetricPositiveSemidefiniteFixedRank{N,K,ℂ}, -) where {N,K} - return 2 * K * N - K * K +function manifold_dimension(M::SymmetricPositiveSemidefiniteFixedRank{<:Any,ℂ}) + n, k = get_nk(M) + return 2 * k * n - k * k end function project!(::SymmetricPositiveSemidefiniteFixedRank, Z, q, Y) @@ -204,8 +222,21 @@ function project!(::SymmetricPositiveSemidefiniteFixedRank, Z, q, Y) return Z end -function Base.show(io::IO, ::SymmetricPositiveSemidefiniteFixedRank{n,k,F}) where {n,k,F} - return print(io, "SymmetricPositiveSemidefiniteFixedRank($(n), $(k), $(F))") +function Base.show( + io::IO, + ::SymmetricPositiveSemidefiniteFixedRank{TypeParameter{Tuple{n,k}},𝔽}, +) where {n,k,𝔽} + return print( + io, + "SymmetricPositiveSemidefiniteFixedRank($(n), $(k), $(𝔽); parameter=:type)", + ) +end +function Base.show( + io::IO, + M::SymmetricPositiveSemidefiniteFixedRank{Tuple{Int,Int},𝔽}, +) where {𝔽} + n, k = get_nk(M) + return print(io, "SymmetricPositiveSemidefiniteFixedRank($(n), $(k), $(𝔽))") end """ diff --git a/src/manifolds/Tucker.jl b/src/manifolds/Tucker.jl index 8ae708c871..0065765d47 100644 --- a/src/manifolds/Tucker.jl +++ b/src/manifolds/Tucker.jl @@ -1,6 +1,6 @@ @doc raw""" - Tucker{N, R, D, 𝔽} <: AbstractManifold{𝔽} + Tucker{T, D, 𝔽} <: AbstractManifold{𝔽} The manifold of ``N_1 \times \dots \times N_D`` real-valued or complex-valued tensors of fixed multilinear rank ``(R_1, \dots, R_D)`` . If ``R_1 = \dots = R_D = 1``, this is the @@ -36,15 +36,23 @@ where ``\mathcal{C}^\prime`` is arbitrary, ``U_d^{\mathrm{H}}`` is the Hermitian ``U_d``, and ``U_d^{\mathrm{H}} U_d^\prime = 0`` for all ``d``. # Constructor - Tucker(N::NTuple{D, Int}, R::NTuple{D, Int}[, field = ℝ]) + Tucker(N::NTuple{D, Int}, R::NTuple{D, Int}[, field = ℝ]; parameter::Symbol=:field) Generate the manifold of `field`-valued tensors of dimensions `N[1] × … × N[D]` and multilinear rank `R = (R[1], …, R[D])`. """ -struct Tucker{N,R,D,𝔽} <: AbstractManifold{𝔽} end -function Tucker(n⃗::NTuple{D,Int}, r⃗::NTuple{D,Int}, field::AbstractNumbers=ℝ) where {D} +struct Tucker{T,D,𝔽} <: AbstractManifold{𝔽} + size::T +end +function Tucker( + n⃗::NTuple{D,Int}, + r⃗::NTuple{D,Int}, + field::AbstractNumbers=ℝ; + parameter::Symbol=:field, +) where {D} @assert is_valid_mlrank(n⃗, r⃗) - return Tucker{n⃗,r⃗,D,field}() + size = wrap_type_parameter(parameter, (n⃗, r⃗)) + return Tucker{typeof(size),D,field}(size) end #= @@ -207,7 +215,7 @@ end #### @doc raw""" - check_point(M::Tucker{N,R,D}, p; kwargs...) where {N,R,D} + check_point(M::Tucker, p; kwargs...) Check whether the multidimensional array or [`TuckerPoint`](@ref) `p` is a point on the [`Tucker`](@ref) manifold, i.e. it is a `D`th order `N[1] × … × N[D]` tensor of multilinear @@ -215,7 +223,8 @@ rank `(R[1], …, R[D])`. The keyword arguments are passed to the matrix rank fu to the unfoldings. For a [`TuckerPoint`](@ref) it is checked that the point is in correct HOSVD form. """ -function check_point(M::Tucker{N,R,D}, x; kwargs...) where {N,R,D} +function check_point(M::Tucker, x; kwargs...) + N, R = get_nr(M) s = "The point $(x) does not lie on $(M), " size(x) == N || return DomainError(size(x), s * "since its size is not $(N).") x_buffer = similar(x) @@ -225,7 +234,8 @@ function check_point(M::Tucker{N,R,D}, x; kwargs...) where {N,R,D} end return nothing end -function check_point(M::Tucker{N,R,D}, x::TuckerPoint; kwargs...) where {N,R,D} +function check_point(M::Tucker, x::TuckerPoint; kwargs...) + N, R = get_nr(M) s = "The point $(x) does not lie on $(M), " U = x.hosvd.U ℭ = x.hosvd.core @@ -269,7 +279,7 @@ function check_point(M::Tucker{N,R,D}, x::TuckerPoint; kwargs...) where {N,R,D} end @doc raw""" - check_vector(M::Tucker{N,R,D}, p::TuckerPoint{T,D}, X::TuckerTVector) where {N,R,T,D} + check_vector(M::Tucker{<:Any,D}, p::TuckerPoint{T,D}, X::TuckerTVector) where {T,D} Check whether a [`TuckerTVector`](@ref) `X` is is in the tangent space to the `D`th order [`Tucker`](@ref) manifold `M` at the `D`th order [`TuckerPoint`](@ref) `p`. @@ -277,25 +287,21 @@ This is the case when the dimensions of the factors in `X` agree with those of `p` and the factor matrices of `X` are in the orthogonal complement of the HOSVD factors of `p`. """ -function check_vector( - M::Tucker{N,R,D}, - p::TuckerPoint{T,D}, - v::TuckerTVector, -) where {N,R,T,D} - s = "The tangent vector $(v) is not a tangent vector to $(p) on $(M), " - if size(p.hosvd.core) ≠ size(v.Ċ) || any(size.(v.U̇) .≠ size.(p.hosvd.U)) +function check_vector(M::Tucker{<:Any,D}, p::TuckerPoint{T,D}, X::TuckerTVector) where {T,D} + s = "The tangent vector $(X) is not a tangent vector to $(p) on $(M), " + if size(p.hosvd.core) ≠ size(X.Ċ) || any(size.(X.U̇) .≠ size.(p.hosvd.U)) return DomainError( - size(v.Ċ), - s * "since the array dimensons of $(p) and $(v)" * "do not agree.", + size(X.Ċ), + s * "since the array dimensons of $(p) and $(X)" * "do not agree.", ) end - for (U, U̇) in zip(p.hosvd.U, v.U̇) + for (U, U̇) in zip(p.hosvd.U, X.U̇) if norm(U' * U̇) ≥ √eps(eltype(U)) * √length(U) return DomainError( norm(U' * U̇), s * "since the columns of x.hosvd.U are not" * - "orthogonal to those of v.U̇.", + "orthogonal to those of X.U̇.", ) end end @@ -375,17 +381,19 @@ end end @doc raw""" - embed(::Tucker{N,R,D}, p::TuckerPoint) where {N,R,D} + embed(::Tucker, p::TuckerPoint) Convert a [`TuckerPoint`](@ref) `p` on the rank `R` [`Tucker`](@ref) manifold to a full `N[1] × … × N[D]`-array by evaluating the Tucker decomposition. - - embed(::Tucker{N,R,D}, p::TuckerPoint, X::TuckerTVector) where {N,R,D} +""" +embed(::Tucker, ::TuckerPoint) +@doc raw""" + embed(::Tucker, p::TuckerPoint, X::TuckerTVector) Convert a tangent vector `X` with base point `p` on the rank `R` [`Tucker`](@ref) manifold to a full tensor, represented as an `N[1] × … × N[D]`-array. """ -embed(::Tucker, ::Any, ::TuckerPoint) +embed(::Tucker, p::TuckerPoint, X::TuckerTVector) function embed!(::Tucker, q, p::TuckerPoint) return copyto!(q, reshape(⊗ᴿ(p.hosvd.U...) * vec(p.hosvd.core), size(p))) @@ -489,6 +497,9 @@ function get_coordinates( return get_coordinates(M, 𝔄, X, get_basis(M, 𝔄, ℬ)) end +get_nr(::Tucker{TypeParameter{Tuple{n,r}}}) where {n,r} = (n, r) +get_nr(M::Tucker{<:Tuple}) = get_parameter(M.size) + #= get_vector(::Tucker, A, x, b) @@ -623,7 +634,7 @@ function is_valid_mlrank(n⃗, r⃗) end @doc raw""" - manifold_dimension(::Tucker{N,R,D}) where {N,R,D} + manifold_dimension(::Tucker) The dimension of the manifold of ``N_1 \times \dots \times N_D`` tensors of multilinear rank ``(R_1, \dots, R_D)``, i.e. @@ -631,7 +642,10 @@ rank ``(R_1, \dots, R_D)``, i.e. \mathrm{dim}(\mathcal{M}) = \prod_{d=1}^D R_d + \sum_{d=1}^D R_d (N_d - R_d). ```` """ -manifold_dimension(::Tucker{n⃗,r⃗}) where {n⃗,r⃗} = prod(r⃗) + sum(r⃗ .* (n⃗ .- r⃗)) +function manifold_dimension(M::Tucker) + n⃗, r⃗ = get_nr(M) + return prod(r⃗) + sum(r⃗ .* (n⃗ .- r⃗)) +end @doc raw""" Base.ndims(p::TuckerPoint{T,D}) where {T,D} @@ -711,9 +725,18 @@ function retract_polar!( return q end -function Base.show(io::IO, ::MIME"text/plain", 𝒯::Tucker{N,R,D,𝔽}) where {N,R,D,𝔽} - return print(io, "Tucker(", N, ", ", R, ", ", 𝔽, ")") +function Base.show( + io::IO, + ::MIME"text/plain", + ::Tucker{TypeParameter{Tuple{n,r}},D,𝔽}, +) where {n,r,D,𝔽} + return print(io, "Tucker($(n), $(r), $(𝔽); parameter=:type)") end +function Base.show(io::IO, ::MIME"text/plain", M::Tucker{<:Tuple,D,𝔽}) where {D,𝔽} + n, r = get_nr(M) + return print(io, "Tucker($(n), $(r), $(𝔽))") +end + function Base.show(io::IO, ::MIME"text/plain", 𝔄::TuckerPoint) pre = " " summary(io, 𝔄) @@ -858,7 +881,7 @@ for fun in [:get_vector, :inverse_retract, :project, :zero_vector] end end -function ManifoldsBase.allocate_result(::Tucker{N}, f::typeof(embed), p, args...) where {N} - dims = N +function ManifoldsBase.allocate_result(M::Tucker, f::typeof(embed), p, args...) + dims = get_nr(M)[1] return Array{number_eltype(p),length(dims)}(undef, dims) end diff --git a/test/manifolds/multinomial_matrices.jl b/test/manifolds/multinomial_matrices.jl index 8ab55db73f..1f30592bba 100644 --- a/test/manifolds/multinomial_matrices.jl +++ b/test/manifolds/multinomial_matrices.jl @@ -6,8 +6,9 @@ include("../utils.jl") @test ProbabilitySimplex(2)^3 === MultinomialMatrices(3, 3) @test ProbabilitySimplex(2)^(3,) === PowerManifold(ProbabilitySimplex(2), 3) @test ^(ProbabilitySimplex(2), 2) === MultinomialMatrices(3, 2) - @test typeof(^(ProbabilitySimplex(2), 2)) == MultinomialMatrices{3,2,2} - @test repr(M) == "MultinomialMatrices(3,2)" + @test typeof(^(ProbabilitySimplex(2), 2)) == + MultinomialMatrices{Tuple{Int64,Int64},ProbabilitySimplex{Tuple{Int64},:open}} + @test repr(M) == "MultinomialMatrices(3, 2)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 4 @test !is_flat(M) From 85549d8fbf46f3caadb34a329add00ec0e96f96e Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Thu, 7 Sep 2023 21:09:53 +0200 Subject: [PATCH 16/81] updating Hyperbolic --- ext/ManifoldsRecipesBaseExt.jl | 10 +++-- src/manifolds/Hyperbolic.jl | 43 +++++++++++++++----- src/manifolds/HyperbolicHyperboloid.jl | 16 ++++---- src/manifolds/HyperbolicPoincareBall.jl | 28 +++++++------ src/manifolds/HyperbolicPoincareHalfspace.jl | 24 ++++++++--- src/manifolds/Lorentz.jl | 2 +- test/recipes.jl | 28 ++++++------- 7 files changed, 96 insertions(+), 55 deletions(-) diff --git a/ext/ManifoldsRecipesBaseExt.jl b/ext/ManifoldsRecipesBaseExt.jl index 6d9b831124..65068c4553 100644 --- a/ext/ManifoldsRecipesBaseExt.jl +++ b/ext/ManifoldsRecipesBaseExt.jl @@ -2,6 +2,7 @@ module ManifoldsRecipesBaseExt if isdefined(Base, :get_extension) using Manifolds + using Manifolds: TypeParameter using Colors: RGBA using RecipesBase: @recipe, @series @@ -9,6 +10,7 @@ else # imports need to be relative for Requires.jl-based workflows: # https://github.com/JuliaArrays/ArrayInterface.jl/pull/387 using ..Manifolds + using ..Manifolds: TypeParameter using ..RecipesBase: @recipe, @series using ..Colors: RGBA @@ -24,7 +26,7 @@ SURFACE_RESOLUTION_DEFAULT = 32 # Plotting Recipe – Poincaré Ball # @recipe function f( - M::Hyperbolic{2}, + M::Hyperbolic{TypeParameter{Tuple{2}}}, pts::AbstractVector{P}, vecs::Union{AbstractVector{T},Nothing}=nothing; circle_points=CIRCLE_DEFAULT_PLOT_POINTS, @@ -87,7 +89,7 @@ end # Plotting Recipe – Poincaré Half plane # @recipe function f( - M::Hyperbolic{2}, + M::Hyperbolic{TypeParameter{Tuple{2}}}, pts::AbstractVector{P}, vecs::Union{AbstractVector{T},Nothing}=nothing; geodesic_interpolation=-1, @@ -133,7 +135,7 @@ end # Plotting Recipe – Hyperboloid # @recipe function f( - M::Hyperbolic{2}, + M::Hyperbolic{TypeParameter{Tuple{2}}}, pts::Union{AbstractVector{P},Nothing}=nothing, vecs::Union{AbstractVector{T},Nothing}=nothing; geodesic_interpolation=-1, @@ -229,7 +231,7 @@ end # Plotting Recipe – Sphere # @recipe function f( - M::Sphere{2,ℝ}, + M::Sphere{TypeParameter{Tuple{2}},ℝ}, pts::Union{AbstractVector{P},Nothing}=nothing, vecs::Union{AbstractVector{T},Nothing}=nothing; geodesic_interpolation=-1, diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index 9e18b5ec22..bf40bee973 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -1,5 +1,5 @@ @doc raw""" - Hyperbolic{N} <: AbstractDecoratorManifold{ℝ} + Hyperbolic{T} <: AbstractDecoratorManifold{ℝ} The hyperbolic space $\mathcal H^n$ represented by $n+1$-Tuples, i.e. embedded in the [`Lorentz`](@ref)ian manifold equipped with the [`MinkowskiMetric`](@ref) @@ -29,13 +29,18 @@ and the Poincaré half space model, see [`PoincareHalfSpacePoint`](@ref) and [`P # Constructor - Hyperbolic(n) + Hyperbolic(n::Int; parameter::Symbol=:field) Generate the Hyperbolic manifold of dimension `n`. """ -struct Hyperbolic{N} <: AbstractDecoratorManifold{ℝ} end +struct Hyperbolic{T} <: AbstractDecoratorManifold{ℝ} + size::T +end -Hyperbolic(n::Int) = Hyperbolic{n}() +function Hyperbolic(n::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return Hyperbolic{typeof(size)}(size) +end function active_traits(f, ::Hyperbolic, args...) return merge_traits(IsIsometricEmbeddedManifold(), IsDefaultMetric(MinkowskiMetric())) @@ -152,7 +157,7 @@ last entry, i.e. $p_n>0$ check_point(::Hyperbolic, ::Any) @doc raw""" - check_vector(M::Hyperbolic{n}, p, X; kwargs... ) + check_vector(M::Hyperbolic, p, X; kwargs... ) Check whether `X` is a tangent vector to `p` on the [`Hyperbolic`](@ref) `M`, i.e. after [`check_point`](@ref)`(M,p)`, `X` has to be of the same dimension as `p`. @@ -166,12 +171,13 @@ For a the Poincaré ball as well as the Poincaré half plane model, `X` has to b check_vector(::Hyperbolic, ::Any, ::Any) function check_vector( - M::Hyperbolic{N}, + M::Hyperbolic, p, X::Union{PoincareBallTVector,PoincareHalfSpaceTVector}; kwargs..., -) where {N} - return check_point(Euclidean(N), X.value; kwargs...) +) + n = get_n(M) + return check_point(Euclidean(n), X.value; kwargs...) end # Define self conversions @@ -195,7 +201,16 @@ function diagonalizing_projectors(M::Hyperbolic, p, X) ) end -get_embedding(::Hyperbolic{N}) where {N} = Lorentz(N + 1, MinkowskiMetric()) +function get_embedding(::Hyperbolic{TypeParameter{Tuple{n}}}) where {n} + return Lorentz(n + 1, MinkowskiMetric(); parameter=:type) +end +function get_embedding(M::Hyperbolic{Tuple{Int}}) + n = get_n(M) + return Lorentz(n + 1, MinkowskiMetric()) +end + +get_n(::Hyperbolic{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::Hyperbolic{Tuple{Int}}) = get_parameter(M.size)[1] embed(::Hyperbolic, p::AbstractArray) = p embed(::Hyperbolic, p::AbstractArray, X::AbstractArray) = X @@ -297,7 +312,7 @@ end Return the dimension of the hyperbolic space manifold $\mathcal H^n$, i.e. $\dim(\mathcal H^n) = n$. """ -manifold_dimension(::Hyperbolic{N}) where {N} = N +manifold_dimension(M::Hyperbolic) = get_n(M) @doc raw""" manifold_dimension(M::Hyperbolic) @@ -342,7 +357,13 @@ the [`Lorentz`](@ref)ian manifold. """ project(::Hyperbolic, ::Any, ::Any) -Base.show(io::IO, ::Hyperbolic{N}) where {N} = print(io, "Hyperbolic($(N))") +function Base.show(io::IO, ::Hyperbolic{TypeParameter{Tuple{n}}}) where {n} + return print(io, "Hyperbolic($(n); parameter=:type)") +end +function Base.show(io::IO, M::Hyperbolic{Tuple{Int}}) + n = get_n(M) + return print(io, "Hyperbolic($(n))") +end for T in _HyperbolicTypes @eval Base.show(io::IO, p::$T) = print(io, "$($T)($(p.value))") end diff --git a/src/manifolds/HyperbolicHyperboloid.jl b/src/manifolds/HyperbolicHyperboloid.jl index 4bd14068e7..e465bf35bc 100644 --- a/src/manifolds/HyperbolicHyperboloid.jl +++ b/src/manifolds/HyperbolicHyperboloid.jl @@ -1,5 +1,5 @@ @doc raw""" - change_representer(M::Hyperbolic{n}, ::EuclideanMetric, p, X) + change_representer(M::Hyperbolic, ::EuclideanMetric, p, X) Change the Eucliden representer `X` of a cotangent vector at point `p`. We only have to correct for the metric, which means that the sign of the last entry changes, since @@ -262,7 +262,8 @@ function _get_basis( return get_basis_orthonormal(M, p, ℝ) end -function get_basis_orthonormal(M::Hyperbolic{n}, p, r::RealNumbers) where {n} +function get_basis_orthonormal(M::Hyperbolic, p, r::RealNumbers) + n = get_n(M) V = [ _hyperbolize(M, p, [i == k ? one(eltype(p)) : zero(eltype(p)) for k in 1:n]) for i in 1:n @@ -364,8 +365,8 @@ i.e. $X_{n+1} = \frac{⟨\tilde p, Y⟩}{p_{n+1}}$, where $\tilde p = (p_1,\ldot _hyperbolize(::Hyperbolic, p, Y) = vcat(Y, dot(p[1:(end - 1)], Y) / p[end]) @doc raw""" - inner(M::Hyperbolic{n}, p, X, Y) - inner(M::Hyperbolic{n}, p::HyperboloidPoint, X::HyperboloidTVector, Y::HyperboloidTVector) + inner(M::Hyperbolic, p, X, Y) + inner(M::Hyperbolic, p::HyperboloidPoint, X::HyperboloidTVector, Y::HyperboloidTVector) Cmpute the inner product in the Hyperboloid model, i.e. the [`minkowski_metric`](@ref) in the embedding. The formula reads @@ -409,11 +410,12 @@ end function Random.rand!( rng::AbstractRNG, - M::Hyperbolic{N}, + M::Hyperbolic, pX; vector_at=nothing, - σ=one(eltype(pX)), -) where {N} + σ::Real=one(eltype(pX)), +) + N = get_n(M) if vector_at === nothing a = randn(rng, N) f = 1 + σ * abs(randn(rng)) diff --git a/src/manifolds/HyperbolicPoincareBall.jl b/src/manifolds/HyperbolicPoincareBall.jl index a5252c963e..624ff1a7d1 100644 --- a/src/manifolds/HyperbolicPoincareBall.jl +++ b/src/manifolds/HyperbolicPoincareBall.jl @@ -1,5 +1,5 @@ @doc raw""" - change_representer(M::Hyperbolic{n}, ::EuclideanMetric, p::PoincareBallPoint, X::PoincareBallTVector) + change_representer(M::Hyperbolic, ::EuclideanMetric, p::PoincareBallPoint, X::PoincareBallTVector) Since in the metric we have the term `` α = \frac{2}{1-\sum_{i=1}^n p_i^2}`` per element, the correction for the gradient reads `` Y = \frac{1}{α^2}X``. @@ -24,7 +24,7 @@ function change_representer!( end @doc raw""" - change_metric(M::Hyperbolic{n}, ::EuclideanMetric, p::PoincareBallPoint, X::PoincareBallTVector) + change_metric(M::Hyperbolic, ::EuclideanMetric, p::PoincareBallPoint, X::PoincareBallTVector) Since in the metric we always have the term `` α = \frac{2}{1-\sum_{i=1}^n p_i^2}`` per element, the correction for the metric reads ``Z = \frac{1}{α}X``. @@ -43,7 +43,7 @@ function change_metric!( return Y end -function check_point(M::Hyperbolic{N}, p::PoincareBallPoint; kwargs...) where {N} +function check_point(M::Hyperbolic, p::PoincareBallPoint; kwargs...) if !(norm(p.value) < 1) return DomainError( norm(p.value), @@ -52,7 +52,8 @@ function check_point(M::Hyperbolic{N}, p::PoincareBallPoint; kwargs...) where {N end end -function check_size(M::Hyperbolic{N}, p::PoincareBallPoint) where {N} +function check_size(M::Hyperbolic, p::PoincareBallPoint) + N = get_n(M) if size(p.value, 1) != N !(norm(p.value) < 1) return DomainError( @@ -62,12 +63,8 @@ function check_size(M::Hyperbolic{N}, p::PoincareBallPoint) where {N} end end -function check_size( - M::Hyperbolic{N}, - p::PoincareBallPoint, - X::PoincareBallTVector; - kwargs..., -) where {N} +function check_size(M::Hyperbolic, p::PoincareBallPoint, X::PoincareBallTVector; kwargs...) + N = get_n(M) if size(X.value, 1) != N return DomainError( size(X.value, 1), @@ -273,12 +270,19 @@ embed(::Hyperbolic, p::PoincareBallPoint) = p.value embed!(::Hyperbolic, q, p::PoincareBallPoint) = copyto!(q, p.value) embed(::Hyperbolic, p::PoincareBallPoint, X::PoincareBallTVector) = X.value embed!(::Hyperbolic, Y, p::PoincareBallPoint, X::PoincareBallTVector) = copyto!(Y, X.value) -get_embedding(::Hyperbolic{n}, p::PoincareBallPoint) where {n} = Euclidean(n) + +function get_embedding(::Hyperbolic{TypeParameter{Tuple{n}}}, ::PoincareBallPoint) where {n} + return Euclidean(n; parameter=:type) +end +function get_embedding(M::Hyperbolic{Tuple{Int}}, ::PoincareBallPoint) + n = get_n(M) + return Euclidean(n) +end @doc raw""" inner(::Hyperbolic, p::PoincareBallPoint, X::PoincareBallTVector, Y::PoincareBallTVector) -Compute the inner producz in the Poincaré ball model. The formula reads +Compute the inner product in the Poincaré ball model. The formula reads ````math g_p(X,Y) = \frac{4}{(1-\lVert p \rVert^2)^2} ⟨X, Y⟩ . ```` diff --git a/src/manifolds/HyperbolicPoincareHalfspace.jl b/src/manifolds/HyperbolicPoincareHalfspace.jl index 973a39b8e7..64ad2ac394 100644 --- a/src/manifolds/HyperbolicPoincareHalfspace.jl +++ b/src/manifolds/HyperbolicPoincareHalfspace.jl @@ -1,4 +1,4 @@ -function check_point(M::Hyperbolic{N}, p::PoincareHalfSpacePoint; kwargs...) where {N} +function check_point(M::Hyperbolic, p::PoincareHalfSpacePoint; kwargs...) if !(last(p.value) > 0) return DomainError( norm(p.value), @@ -7,7 +7,8 @@ function check_point(M::Hyperbolic{N}, p::PoincareHalfSpacePoint; kwargs...) whe end end -function check_size(M::Hyperbolic{N}, p::PoincareHalfSpacePoint) where {N} +function check_size(M::Hyperbolic, p::PoincareHalfSpacePoint) + N = get_n(M) if size(p.value, 1) != N !(norm(p.value) < 1) return DomainError( @@ -18,11 +19,12 @@ function check_size(M::Hyperbolic{N}, p::PoincareHalfSpacePoint) where {N} end function check_size( - M::Hyperbolic{N}, + M::Hyperbolic, p::PoincareHalfSpacePoint, X::PoincareHalfSpaceTVector; kwargs..., -) where {N} +) + N = get_n(M) if size(X.value, 1) != N return DomainError( size(X.value, 1), @@ -210,11 +212,21 @@ embed(::Hyperbolic, p::PoincareHalfSpacePoint, X::PoincareHalfSpaceTVector) = X. function embed!(::Hyperbolic, Y, p::PoincareHalfSpacePoint, X::PoincareHalfSpaceTVector) return copyto!(Y, X.value) end -get_embedding(::Hyperbolic{n}, p::PoincareHalfSpacePoint) where {n} = Euclidean(n) + +function get_embedding( + ::Hyperbolic{TypeParameter{Tuple{n}}}, + ::PoincareHalfSpacePoint, +) where {n} + return Euclidean(n; parameter=:type) +end +function get_embedding(M::Hyperbolic{Tuple{Int}}, ::PoincareHalfSpacePoint) + n = get_n(M) + return Euclidean(n) +end @doc raw""" inner( - ::Hyperbolic{n}, + ::Hyperbolic, p::PoincareHalfSpacePoint, X::PoincareHalfSpaceTVector, Y::PoincareHalfSpaceTVector diff --git a/src/manifolds/Lorentz.jl b/src/manifolds/Lorentz.jl index 831d4fee68..95d1d78bb1 100644 --- a/src/manifolds/Lorentz.jl +++ b/src/manifolds/Lorentz.jl @@ -16,7 +16,7 @@ see [`minkowski_metric`](@ref) for the formula. struct MinkowskiMetric <: LorentzMetric end @doc raw""" - Lorentz{N} = MetricManifold{Euclidean{N,ℝ},LorentzMetric} + Lorentz{T} = MetricManifold{Euclidean{T,ℝ},LorentzMetric} The Lorentz manifold (or Lorentzian) is a pseudo-Riemannian manifold. diff --git a/test/recipes.jl b/test/recipes.jl index f595fa3044..831dcbda67 100644 --- a/test/recipes.jl +++ b/test/recipes.jl @@ -10,7 +10,7 @@ include("utils.jl") ENV["GKSwstype"] = "100" gr() # function Hyp2PB_plot() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) p2 = convert.(Ref(PoincareBallPoint), p) # return @@ -19,7 +19,7 @@ include("utils.jl") # @plottest Hyp2PB_plot joinpath(references_folder, "Hyp2PBPlot.png") false # function Hyp2PB_plot_geo() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) p2 = convert.(Ref(PoincareBallPoint), p) # return @@ -28,7 +28,7 @@ include("utils.jl") # @plottest Hyp2PB_plot_geo joinpath(references_folder, "Hyp2PBPlotGeo.png") false # function Hyp2PB_quiver() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) 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])] @@ -38,7 +38,7 @@ include("utils.jl") # @plottest Hyp2PB_quiver joinpath(references_folder, "Hyp2PBQuiver.png") false # function Hyp2PH_plot() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) p2 = convert.(Ref(PoincareHalfSpacePoint), p) # return @@ -47,7 +47,7 @@ include("utils.jl") # @plottest Hyp2PH_plot joinpath(references_folder, "Hyp2PHPlot.png") false # function Hyp2PH_plot_geo() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) p2 = convert.(Ref(PoincareHalfSpacePoint), p) # return @@ -56,7 +56,7 @@ include("utils.jl") # @plottest Hyp2PH_plot_geo joinpath(references_folder, "Hyp2PHPlotGeo.png") false # function Hyp2PH_quiver() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) 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])] @@ -69,7 +69,7 @@ include("utils.jl") ENV["GKSwstype"] = "100" gr() # function Hyp2_plot() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) # return plot(M, p) @@ -77,7 +77,7 @@ include("utils.jl") # @plottest Hyp2_plot joinpath(references_folder, "Hyp2Plot.png") false # function Hyp2_surfplot() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) # return plot(M, p; surface=true) @@ -85,7 +85,7 @@ include("utils.jl") #@plottest Hyp2_surfplot joinpath(references_folder, "Hyp2SurfPlot.png") false # function Hyp2_plot_geo() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) # return plot(M, p; geodesic_interpolation=80) @@ -93,7 +93,7 @@ include("utils.jl") #@plottest Hyp2_plot_geo joinpath(references_folder, "Hyp2PlotGeo.png") false # function Hyp2_quiver() - M = Hyperbolic(2) + M = Hyperbolic(2; parameter=:type) 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 @@ -104,7 +104,7 @@ include("utils.jl") @testset "3D Recipes in pythonplot" begin pythonplot() # function Sphere2_plot() - M = Sphere(2) + M = Sphere(2; parameter=:type) 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) @@ -112,7 +112,7 @@ include("utils.jl") # @plottest Sphere2_plot joinpath(references_folder, "Sphere2Plot.png") false # function Sphere2_surfplot() - M = Sphere(2) + M = Sphere(2; parameter=:type) 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) @@ -120,7 +120,7 @@ include("utils.jl") # @plottest Sphere2_surfplot joinpath(references_folder, "Sphere2SurfPlot.png") false # function Sphere2_plot_geo() - M = Sphere(2) + M = Sphere(2; parameter=:type) 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) @@ -129,7 +129,7 @@ include("utils.jl") # function Sphere2_quiver() pythonplot() - M = Sphere(2) + M = Sphere(2; parameter=:type) 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)) From 83c027215416e0d82f305267810d8e1b83c5b54e Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Thu, 7 Sep 2023 22:10:15 +0200 Subject: [PATCH 17/81] fix CI issue --- test/groups/power_group.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/groups/power_group.jl b/test/groups/power_group.jl index 164196e39b..d67fbe7590 100644 --- a/test/groups/power_group.jl +++ b/test/groups/power_group.jl @@ -2,7 +2,7 @@ include("../utils.jl") include("group_utils.jl") @testset "Power group" begin - Mr = SpecialOrthogonal(3) + Mr = SpecialOrthogonal(3; parameter=:type) Mr1 = PowerManifold(Mr, 5) Mrn1 = PowerManifold(Mr, Manifolds.NestedPowerRepresentation(), 5) Mrnr1 = PowerManifold(Mr, Manifolds.NestedReplacingPowerRepresentation(), 5) From 60aed687e7e9977182c2904052a72a1fd01851a5 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Fri, 8 Sep 2023 09:46:47 +0200 Subject: [PATCH 18/81] use type parameter in a couple of places --- test/manifolds/power_manifold.jl | 5 +++-- test/manifolds/product_manifold.jl | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index 3a896cbf46..b325d48703 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -29,7 +29,7 @@ end @test power_dimensions(Ms2n) == (7,) @test manifold_dimension(Ms2n) == 70 - Mr = Manifolds.Rotations(3) + Mr = Rotations(3; parameter=:type) Mr1 = PowerManifold(Mr, 5) Mrn1 = PowerManifold(Mr, Manifolds.NestedPowerRepresentation(), 5) @test PowerManifold(PowerManifold(Mr, 2), 3) == PowerManifold(Mr, 2, 3) @@ -46,7 +46,8 @@ end @test manifold_dimension(Mrn2) == 105 @test repr(Ms1) == "PowerManifold(Sphere(2, ℝ), 5)" - @test repr(Mrn1) == "PowerManifold(Rotations(3), NestedPowerRepresentation(), 5)" + @test repr(Mrn1) == + "PowerManifold(Rotations(3; parameter=:type), NestedPowerRepresentation(), 5)" @test Manifolds.allocation_promotion_function(Ms, exp, ([1],)) == Manifolds.allocation_promotion_function(Ms1, exp, (1,)) diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index e35de8b8aa..f8a448c8e8 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -203,7 +203,7 @@ using RecursiveArrayTools: ArrayPartition ProductManifold(Sphere(2, ℝ), Euclidean(2; field = ℝ))""" end - M3 = Manifolds.Rotations(2) + M3 = Rotations(2; parameter=:type) Mser = ProductManifold(M1, M2, M3) @test submanifold(Mser, 2) == M2 @@ -406,7 +406,7 @@ using RecursiveArrayTools: ArrayPartition end @testset "vee/hat" begin - M1 = Rotations(3) + M1 = Rotations(3; parameter=:type) M2 = Euclidean(3) M = M1 × M2 From 9472f7bdb3e9663132f9449504866b8e8a477cdf Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Fri, 8 Sep 2023 13:26:46 +0200 Subject: [PATCH 19/81] updates --- .github/workflows/ci.yml | 2 +- .github/workflows/nightly.yml | 27 ------- NEWS.md | 5 ++ src/groups/general_linear.jl | 67 +++++++++++------ src/groups/heisenberg.jl | 100 ++++++++++++++++--------- src/groups/orthogonal.jl | 8 +- src/manifolds/KendallsPreShapeSpace.jl | 33 ++++++-- src/manifolds/KendallsShapeSpace.jl | 50 ++++++++++--- test/groups/general_linear.jl | 6 +- test/groups/product_group.jl | 6 +- 10 files changed, 190 insertions(+), 114 deletions(-) delete mode 100644 .github/workflows/nightly.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cabaab2e47..d3f0e3b3be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - julia-version: ["1.6", "1.8", "~1.9.0-0"] + julia-version: ["1.6", "~1.9.0-0"] os: [ubuntu-latest, macOS-latest] group: - 'test_manifolds' diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml deleted file mode 100644 index 46bb83d5d0..0000000000 --- a/.github/workflows/nightly.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Nightly -on: - pull_request: - -jobs: - test: - name: Julia nightly - ${{ matrix.group }} - ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macOS-latest, windows-latest] - group: - - 'test_manifolds' - - 'test_lie_groups' - steps: - - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v1 - with: - version: nightly - arch: x64 - - uses: julia-actions/cache@v1 - - uses: julia-actions/julia-buildpkg@latest - - uses: julia-actions/julia-runtest@latest - env: - PYTHON: "" - MANIFOLDS_TEST_GROUP: ${{ matrix.group }} diff --git a/NEWS.md b/NEWS.md index 90b1b45885..38a4f81df7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -24,10 +24,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Elliptope{N,K}`, - `Euclidean`, - `FixedRankMatrices{m,n,k}`, + - `KendallsPreShapeSpace{n,k}`, + - `KendallsShapeSpace{n,k}`, + - `GeneralLinear{n}`, - `GeneralUnitaryMultiplicationGroup{n}`, - `GeneralizedGrassmann{n,k}`, - `GeneralizedStiefel{n,k}`, - `Grassmann{n,k}`, + - `Heisenberg{n}`, + - `Hyperbolic{n}`, - `MultinomialMatrices{N,M}`, - `MultinomialDoublyStochastic{n}`, - `MultinomialSymmetric{n}`, diff --git a/src/groups/general_linear.jl b/src/groups/general_linear.jl index 844d3b429c..d734d5daef 100644 --- a/src/groups/general_linear.jl +++ b/src/groups/general_linear.jl @@ -1,6 +1,5 @@ @doc raw""" - GeneralLinear{n,𝔽} <: - AbstractDecoratorManifold{𝔽} + GeneralLinear{T,𝔽} <: AbstractDecoratorManifold{𝔽} The general linear group, that is, the group of all invertible matrices in ``𝔽^{n×n}``. @@ -16,7 +15,9 @@ vector in the Lie algebra, and ``⟨⋅,⋅⟩_\mathrm{F}`` denotes the Frobeniu By default, tangent vectors ``X_p`` are represented with their corresponding Lie algebra vectors ``X_e = p^{-1}X_p``. """ -struct GeneralLinear{n,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct GeneralLinear{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end function active_traits(f, ::GeneralLinear, args...) return merge_traits( @@ -27,9 +28,12 @@ function active_traits(f, ::GeneralLinear, args...) ) end -GeneralLinear(n, 𝔽::AbstractNumbers=ℝ) = GeneralLinear{n,𝔽}() +function GeneralLinear(n::Int, 𝔽::AbstractNumbers=ℝ; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return GeneralLinear{typeof(size),𝔽}(size) +end -function allocation_promotion_function(::GeneralLinear{n,ℂ}, f, ::Tuple) where {n} +function allocation_promotion_function(::GeneralLinear{<:Any,ℂ}, f, ::Tuple) return complex end @@ -88,12 +92,12 @@ end function exp!(G::GeneralLinear, q, p, X, t::Number) return exp!(G, q, p, t * X) end -function exp!(::GeneralLinear{1}, q, p, X) +function exp!(::GeneralLinear{TypeParameter{Tuple{1}}}, q, p, X) p1 = p isa Identity ? p : p[1] q[1] = p1 * exp(X[1]) return q end -function exp!(G::GeneralLinear{2}, q, p, X) +function exp!(G::GeneralLinear{TypeParameter{Tuple{2}}}, q, p, X) if isnormal(X; atol=sqrt(eps(real(eltype(X))))) return compose!(G, q, p, exp(SizedMatrix{2,2}(X))) end @@ -105,50 +109,62 @@ function exp!(G::GeneralLinear{2}, q, p, X) end function get_coordinates( - ::GeneralLinear{n,ℝ}, + ::GeneralLinear{<:Any,ℝ}, p, X, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, -) where {n} +) return vec(X) end function get_coordinates!( - ::GeneralLinear{n,ℝ}, + ::GeneralLinear{<:Any,ℝ}, Xⁱ, p, X, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, -) where {n} +) return copyto!(Xⁱ, X) end -get_embedding(::GeneralLinear{n,𝔽}) where {n,𝔽} = Euclidean(n, n; field=𝔽) +function get_embedding(::GeneralLinear{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return Euclidean(n, n; field=𝔽, parameter=:type) +end +function get_embedding(M::GeneralLinear{Tuple{Int},𝔽}) where {𝔽} + n = get_n(M) + return Euclidean(n, n; field=𝔽) +end + +get_n(::GeneralLinear{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::GeneralLinear{Tuple{Int}}) = get_parameter(M.size)[1] function get_vector( - ::GeneralLinear{n,ℝ}, + M::GeneralLinear{<:Any,ℝ}, p, Xⁱ, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, -) where {n} +) + n = get_n(M) return reshape(Xⁱ, n, n) end function get_vector!( - ::GeneralLinear{n,ℝ}, + ::GeneralLinear{<:Any,ℝ}, X, p, Xⁱ, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, -) where {n} +) return copyto!(X, Xⁱ) end -function exp_lie!(::GeneralLinear{1}, q, X) +function exp_lie!(::GeneralLinear{TypeParameter{Tuple{1}}}, q, X) q[1] = exp(X[1]) return q end -exp_lie!(::GeneralLinear{2}, q, X) = copyto!(q, exp(SizedMatrix{2,2}(X))) +function exp_lie!(::GeneralLinear{TypeParameter{Tuple{2}}}, q, X) + return copyto!(q, exp(SizedMatrix{2,2}(X))) +end inner(::GeneralLinear, p, X, Y) = dot(X, Y) @@ -189,7 +205,8 @@ function log(M::GeneralLinear, p, q) return log!(M, X, p, q) end -function log!(G::GeneralLinear{n,𝔽}, X, p, q) where {n,𝔽} +function log!(G::GeneralLinear{<:Any,𝔽}, X, p, q) where {𝔽} + n = get_n(G) pinvq = inverse_translate(G, p, q, LeftForwardAction()) 𝔽 === ℝ && det(pinvq) ≤ 0 && throw(OutOfInjectivityRadiusError()) if isnormal(pinvq; atol=sqrt(eps(real(eltype(pinvq))))) @@ -208,13 +225,13 @@ function log!(G::GeneralLinear{n,𝔽}, X, p, q) where {n,𝔽} translate_diff!(G, X, p, Identity(G), X, LeftForwardAction()) return X end -function log!(::GeneralLinear{1}, X, p, q) +function log!(::GeneralLinear{TypeParameter{Tuple{1}}}, X, p, q) p1 = p isa Identity ? p : p[1] X[1] = log(p1 \ q[1]) return X end -function _log_lie!(::GeneralLinear{1}, X, p) +function _log_lie!(::GeneralLinear{TypeParameter{Tuple{1}}}, X, p) X[1] = log(p[1]) return X end @@ -253,7 +270,13 @@ function Random.rand!(rng::AbstractRNG, G::GeneralLinear, pX; kwargs...) return pX end -Base.show(io::IO, ::GeneralLinear{n,𝔽}) where {n,𝔽} = print(io, "GeneralLinear($n, $𝔽)") +function Base.show(io::IO, ::GeneralLinear{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return print(io, "GeneralLinear($n, $𝔽; parameter=:type)") +end +function Base.show(io::IO, M::GeneralLinear{Tuple{Int},𝔽}) where {𝔽} + n = get_n(M) + return print(io, "GeneralLinear($n, $𝔽)") +end translate_diff(::GeneralLinear, p, q, X, ::LeftForwardAction) = X translate_diff(::GeneralLinear, p, q, X, ::RightBackwardAction) = p \ X * p diff --git a/src/groups/heisenberg.jl b/src/groups/heisenberg.jl index e155b9337f..28e7945e2c 100644 --- a/src/groups/heisenberg.jl +++ b/src/groups/heisenberg.jl @@ -1,6 +1,6 @@ @doc raw""" - HeisenbergGroup{n} <: AbstractDecoratorManifold{ℝ} + HeisenbergGroup{T} <: AbstractDecoratorManifold{ℝ} Heisenberg group `HeisenbergGroup(n)` is the group of ``(n+2) × (n+2)`` matrices [BinzPods:2008](@cite) @@ -16,7 +16,14 @@ The group operation is matrix multiplication. The left-invariant metric on the manifold is used. """ -struct HeisenbergGroup{n} <: AbstractDecoratorManifold{ℝ} end +struct HeisenbergGroup{T} <: AbstractDecoratorManifold{ℝ} + size::T +end + +function HeisenbergGroup(n::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n,)) + return HeisenbergGroup{typeof(size)}(size) +end function active_traits(f, ::HeisenbergGroup, args...) return merge_traits( @@ -26,16 +33,17 @@ function active_traits(f, ::HeisenbergGroup, args...) ) end -function _heisenberg_a_view(::HeisenbergGroup{n}, p) where {n} +function _heisenberg_a_view(M::HeisenbergGroup, p) + n = get_n(M) return view(p, 1, 2:(n + 1)) end -function _heisenberg_b_view(::HeisenbergGroup{n}, p) where {n} +function _heisenberg_b_view(M::HeisenbergGroup, p) + n = get_n(M) return view(p, 2:(n + 1), n + 2) end -HeisenbergGroup(n::Int) = HeisenbergGroup{n}() - -function check_point(G::HeisenbergGroup{n}, p; kwargs...) where {n} +function check_point(G::HeisenbergGroup, p; kwargs...) + n = get_n(G) if !isone(p[1, 1]) return DomainError( p[1, 1], @@ -70,7 +78,8 @@ function check_point(G::HeisenbergGroup{n}, p; kwargs...) where {n} return nothing end -function check_vector(G::HeisenbergGroup{n}, p, X; kwargs...) where {n} +function check_vector(G::HeisenbergGroup, p, X; kwargs...) + n = get_n(G) if !iszero(X[1, 1]) return DomainError( X[1, 1], @@ -109,24 +118,29 @@ the coordinates are concatenated vectors ``\mathbf{a}``, ``\mathbf{b}``, and num """ get_coordinates(::HeisenbergGroup, p, X, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) -function get_coordinates_orthonormal(M::HeisenbergGroup{n}, p, X, ::RealNumbers) where {n} +function get_coordinates_orthonormal(M::HeisenbergGroup, p, X, ::RealNumbers) + n = get_n(M) return vcat(_heisenberg_a_view(M, X), _heisenberg_b_view(M, X), X[1, n + 2]) end -function get_coordinates_orthonormal!( - M::HeisenbergGroup{n}, - Xⁱ, - p, - X, - ::RealNumbers, -) where {n} +function get_coordinates_orthonormal!(M::HeisenbergGroup, Xⁱ, p, X, ::RealNumbers) + n = get_n(M) Xⁱ[1:n] .= _heisenberg_a_view(M, X) Xⁱ[(n + 1):(2 * n)] .= _heisenberg_b_view(M, X) Xⁱ[2 * n + 1] = X[1, n + 2] return Xⁱ end -get_embedding(::HeisenbergGroup{n}) where {n} = Euclidean(n + 2, n + 2) +function get_embedding(::HeisenbergGroup{TypeParameter{Tuple{n}}}) where {n} + return Euclidean(n + 2, n + 2; parameter=:type) +end +function get_embedding(M::HeisenbergGroup{Tuple{Int}}) + n = get_n(M) + return Euclidean(n + 2, n + 2) +end + +get_n(::HeisenbergGroup{TypeParameter{Tuple{n}}}) where {n} = n +get_n(M::HeisenbergGroup{Tuple{Int}}) = get_parameter(M.size)[1] @doc raw""" get_vector(M::HeisenbergGroup, p, Xⁱ, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) @@ -141,7 +155,8 @@ Given a vector of coordinates ``\begin{bmatrix}\mathbb{a} & \mathbb{b} & c\end{b """ get_vector(M::HeisenbergGroup, p, c, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) -function get_vector_orthonormal(::HeisenbergGroup{n}, p, Xⁱ, ::RealNumbers) where {n} +function get_vector_orthonormal(M::HeisenbergGroup, p, Xⁱ, ::RealNumbers) + n = get_n(M) return [ 0 Xⁱ[1:n] Xⁱ[2 * n + 1] zeros(n, n + 1) Xⁱ[(n + 1):(2 * n)]' @@ -149,7 +164,8 @@ function get_vector_orthonormal(::HeisenbergGroup{n}, p, Xⁱ, ::RealNumbers) wh ] end -function get_vector_orthonormal!(::HeisenbergGroup{n}, X, p, Xⁱ, ::RealNumbers) where {n} +function get_vector_orthonormal!(M::HeisenbergGroup, X, p, Xⁱ, ::RealNumbers) + n = get_n(M) fill!(X, 0) X[1, 2:(n + 1)] .= Xⁱ[1:n] X[2:(n + 1), n + 2] .= Xⁱ[(n + 1):(2 * n)] @@ -174,7 +190,8 @@ and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors. """ exp_lie(M::HeisenbergGroup, X) -function exp_lie!(M::HeisenbergGroup{n}, q, X) where {n} +function exp_lie!(M::HeisenbergGroup, q, X) + n = get_n(M) copyto!(q, I) a_view = _heisenberg_a_view(M, X) b_view = _heisenberg_b_view(M, X) @@ -204,7 +221,8 @@ and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors. """ exp(M::HeisenbergGroup, p, X) -function exp!(M::HeisenbergGroup{n}, q, p, X) where {n} +function exp!(M::HeisenbergGroup, q, p, X) + n = get_n(M) copyto!(q, I) a_p_view = _heisenberg_a_view(M, p) b_p_view = _heisenberg_b_view(M, p) @@ -224,7 +242,8 @@ Return the injectivity radius on the [`HeisenbergGroup`](@ref) `M`, which is `` """ injectivity_radius(::HeisenbergGroup) = Inf -function inner(M::HeisenbergGroup{n}, p, X, Y) where {n} +function inner(M::HeisenbergGroup, p, X, Y) + n = get_n(M) X_a_view = _heisenberg_a_view(M, X) X_b_view = _heisenberg_b_view(M, X) Y_a_view = _heisenberg_a_view(M, Y) @@ -254,7 +273,8 @@ and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors. """ log(::HeisenbergGroup, p, q) -function log!(M::HeisenbergGroup{n}, X, p, q) where {n} +function log!(M::HeisenbergGroup, X, p, q) + n = get_n(M) fill!(X, 0) a_p_view = _heisenberg_a_view(M, p) b_p_view = _heisenberg_b_view(M, p) @@ -285,7 +305,8 @@ and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors. """ log_lie(M::HeisenbergGroup, p) -function log_lie!(M::HeisenbergGroup{n}, X, p) where {n} +function log_lie!(M::HeisenbergGroup, X, p) + n = get_n(M) fill!(X, 0) view_a_X = _heisenberg_a_view(M, X) view_b_X = _heisenberg_b_view(M, X) @@ -300,20 +321,21 @@ function log_lie!(::HeisenbergGroup, X, ::Identity{MultiplicationOperation}) return X end -manifold_dimension(::HeisenbergGroup{n}) where {n} = 2 * n + 1 +manifold_dimension(M::HeisenbergGroup) = 2 * get_n(M) + 1 parallel_transport_to(::HeisenbergGroup, p, X, q) = X parallel_transport_to!(::HeisenbergGroup, Y, p, X, q) = copyto!(Y, X) """ - project(M::HeisenbergGroup{n}, p) + project(M::HeisenbergGroup, p) Project a matrix `p` in the Euclidean embedding onto the [`HeisenbergGroup`](@ref) `M`. Sets the diagonal elements to 1 and all non-diagonal elements except the first row and the last column to 0. """ -function project(M::HeisenbergGroup{n}, p) where {n} +function project(M::HeisenbergGroup, p) + n = get_n(M) return [ 1 p[1, 2:(n + 2)]' zeros(n, 1) Matrix(I, n, n) _heisenberg_b_view(M, p) @@ -321,14 +343,15 @@ function project(M::HeisenbergGroup{n}, p) where {n} ] end """ - project(M::HeisenbergGroup{n}, p, X) + project(M::HeisenbergGroup, p, X) Project a matrix `X` in the Euclidean embedding onto the Lie algebra of [`HeisenbergGroup`](@ref) `M`. Sets the diagonal elements to 0 and all non-diagonal elements except the first row and the last column to 0. """ -function project(M::HeisenbergGroup{n}, p, X) where {n} +function project(M::HeisenbergGroup, p, X) + n = get_n(M) return [ 0 X[1, 2:(n + 2)]' zeros(n, n + 1) _heisenberg_b_view(M, X) @@ -336,13 +359,15 @@ function project(M::HeisenbergGroup{n}, p, X) where {n} ] end -function project!(M::HeisenbergGroup{n}, q, p) where {n} +function project!(M::HeisenbergGroup, q, p) + n = get_n(M) copyto!(q, I) q[1, 2:(n + 2)] .= p[1, 2:(n + 2)] q[2:(n + 1), n + 2] .= _heisenberg_b_view(M, p) return q end -function project!(M::HeisenbergGroup{n}, Y, p, X) where {n} +function project!(M::HeisenbergGroup, Y, p, X) + n = get_n(M) fill!(Y, 0) Y[1, 2:(n + 2)] .= X[1, 2:(n + 2)] Y[2:(n + 1), n + 2] .= _heisenberg_b_view(M, X) @@ -364,11 +389,12 @@ rand(M::HeisenbergGroup; vector_at=nothing, σ::Real=1.0) function Random.rand!( rng::AbstractRNG, - ::HeisenbergGroup{n}, + M::HeisenbergGroup, pX; σ::Real=one(eltype(pX)), vector_at=nothing, -) where {n} +) + n = get_n(M) if vector_at === nothing copyto!(pX, I) va = view(pX, 1, 2:(n + 2)) @@ -386,7 +412,13 @@ function Random.rand!( return pX end -Base.show(io::IO, ::HeisenbergGroup{n}) where {n} = print(io, "HeisenbergGroup($n)") +function Base.show(io::IO, ::HeisenbergGroup{TypeParameter{Tuple{n}}}) where {n} + return print(io, "HeisenbergGroup($(n); parameter=:type)") +end +function Base.show(io::IO, M::HeisenbergGroup{Tuple{Int}}) + n = get_n(M) + return print(io, "HeisenbergGroup($(n))") +end translate_diff(::HeisenbergGroup, p, q, X, ::LeftForwardAction) = X translate_diff(::HeisenbergGroup, p, q, X, ::RightBackwardAction) = p \ X * p diff --git a/src/groups/orthogonal.jl b/src/groups/orthogonal.jl index 4940e8a569..9f4672d860 100644 --- a/src/groups/orthogonal.jl +++ b/src/groups/orthogonal.jl @@ -1,15 +1,15 @@ @doc raw""" - Orthogonal{n} = GeneralUnitaryMultiplicationGroup{n,ℝ,AbsoluteDeterminantOneMatrices} + Orthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,AbsoluteDeterminantOneMatrices} Orthogonal group $\mathrm{O}(n)$ represented by [`OrthogonalMatrices`](@ref). # Constructor - Orthogonal(n) + Orthogonal(n::Int; parameter::Symbol=:field) """ -const Orthogonal{n} = GeneralUnitaryMultiplicationGroup{n,ℝ,AbsoluteDeterminantOneMatrices} +const Orthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,AbsoluteDeterminantOneMatrices} -function Orthogonal(n; parameter::Symbol=:field) +function Orthogonal(n::Int; parameter::Symbol=:field) return GeneralUnitaryMultiplicationGroup(OrthogonalMatrices(n; parameter=parameter)) end diff --git a/src/manifolds/KendallsPreShapeSpace.jl b/src/manifolds/KendallsPreShapeSpace.jl index f069a493af..28127dfded 100644 --- a/src/manifolds/KendallsPreShapeSpace.jl +++ b/src/manifolds/KendallsPreShapeSpace.jl @@ -1,6 +1,6 @@ @doc raw""" - KendallsPreShapeSpace{n,k} <: AbstractSphere{ℝ} + KendallsPreShapeSpace{T} <: AbstractSphere{ℝ} Kendall's pre-shape space of ``k`` landmarks in ``ℝ^n`` represented by n×k matrices. In each row the sum of elements of a matrix is equal to 0. The Frobenius norm of the matrix @@ -11,20 +11,25 @@ translation and scaling of all points, so this can be thought of as a quotient m # Constructor - KendallsPreShapeSpace(n::Int, k::Int) + KendallsPreShapeSpace(n::Int, k::Int; parameter::Symbol=:field) # See also [`KendallsShapeSpace`](@ref), esp. for the references """ -struct KendallsPreShapeSpace{n,k} <: AbstractSphere{ℝ} end +struct KendallsPreShapeSpace{T} <: AbstractSphere{ℝ} + size::T +end -KendallsPreShapeSpace(n::Int, k::Int) = KendallsPreShapeSpace{n,k}() +function KendallsPreShapeSpace(n::Int, k::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n, k)) + return KendallsPreShapeSpace{typeof(size)}(size) +end function active_traits(f, ::KendallsPreShapeSpace, args...) return merge_traits(IsEmbeddedSubmanifold()) end -representation_size(::KendallsPreShapeSpace{n,k}) where {n,k} = (n, k) +representation_size(M::KendallsPreShapeSpace) = get_nk(M) """ check_point(M::KendallsPreShapeSpace, p; atol=sqrt(max_eps(X, Y)), kwargs...) @@ -71,9 +76,18 @@ embed(::KendallsPreShapeSpace, p, X) = X Return the space [`KendallsPreShapeSpace`](@ref) `M` is embedded in, i.e. [`ArraySphere`](@ref) of matrices of the same shape. """ -function get_embedding(::KendallsPreShapeSpace{N,K}) where {N,K} - return ArraySphere(N, K) +get_embedding(::KendallsPreShapeSpace) + +function get_embedding(::KendallsPreShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} + return ArraySphere(n, k, parameter=:type) end +function get_embedding(M::KendallsPreShapeSpace{Tuple{Int,Int}}) + n, k = get_nk(M) + return ArraySphere(n, k) +end + +get_nk(::KendallsPreShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) +get_nk(M::KendallsPreShapeSpace{Tuple{Int,Int}}) = get_parameter(M.size) @doc raw""" manifold_dimension(M::KendallsPreShapeSpace) @@ -81,7 +95,10 @@ end Return the dimension of the [`KendallsPreShapeSpace`](@ref) manifold `M`. The dimension is given by ``n(k - 1) - 1``. """ -manifold_dimension(::KendallsPreShapeSpace{n,k}) where {n,k} = n * (k - 1) - 1 +function manifold_dimension(M::KendallsPreShapeSpace) + n, k = get_nk(M) + return n * (k - 1) - 1 +end """ project(M::KendallsPreShapeSpace, p) diff --git a/src/manifolds/KendallsShapeSpace.jl b/src/manifolds/KendallsShapeSpace.jl index 0bcb29db52..50f6213992 100644 --- a/src/manifolds/KendallsShapeSpace.jl +++ b/src/manifolds/KendallsShapeSpace.jl @@ -1,6 +1,6 @@ @doc raw""" - KendallsShapeSpace{n,k} <: AbstractDecoratorManifold{ℝ} + KendallsShapeSpace{T} <: AbstractDecoratorManifold{ℝ} Kendall's shape space, defined as quotient of a [`KendallsPreShapeSpace`](@ref) (represented by n×k matrices) by the action [`ColumnwiseMultiplicationAction`](@ref). @@ -12,29 +12,45 @@ This manifold possesses the [`IsQuotientManifold`](@ref) trait. # Constructor - KendallsShapeSpace(n::Int, k::Int) + KendallsShapeSpace(n::Int, k::Int; parameter::Symbol=:field) # References """ -struct KendallsShapeSpace{n,k} <: AbstractDecoratorManifold{ℝ} end +struct KendallsShapeSpace{T} <: AbstractDecoratorManifold{ℝ} + size::T +end -KendallsShapeSpace(n::Int, k::Int) = KendallsShapeSpace{n,k}() +function KendallsShapeSpace(n::Int, k::Int; parameter::Symbol=:field) + size = wrap_type_parameter(parameter, (n, k)) + return KendallsShapeSpace{typeof(size)}(size) +end function active_traits(f, ::KendallsShapeSpace, args...) return merge_traits(IsIsometricEmbeddedManifold(), IsQuotientManifold()) end -function get_orbit_action(M::KendallsShapeSpace{n,k}) where {n,k} +function get_orbit_action(M::KendallsShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} + return ColumnwiseMultiplicationAction(M, SpecialOrthogonal(n; parameter=:type)) +end +function get_orbit_action(M::KendallsShapeSpace{Tuple{Int,Int}}) + n, k = get_nk(M) return ColumnwiseMultiplicationAction(M, SpecialOrthogonal(n)) end @doc raw""" - get_total_space(::Grassmann{n,k}) + get_total_space(::KendallsShapeSpace) Return the total space of the [`KendallsShapeSpace`](@ref) manifold, which is the [`KendallsPreShapeSpace`](@ref) manifold. """ -get_total_space(::KendallsShapeSpace{n,k}) where {n,k} = KendallsPreShapeSpace(n, k) +get_total_space(::KendallsShapeSpace) +function get_total_space(::KendallsShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} + return KendallsPreShapeSpace(n, k, parameter=:type) +end +function get_total_space(M::KendallsShapeSpace{Tuple{Int,Int}}) + n, k = get_nk(M) + return KendallsPreShapeSpace(n, k) +end function distance(M::KendallsShapeSpace, p, q) A = get_orbit_action(M) @@ -66,9 +82,18 @@ embed(::KendallsShapeSpace, p, X) = X Get the manifold in which [`KendallsShapeSpace`](@ref) `M` is embedded, i.e. [`KendallsPreShapeSpace`](@ref) of matrices of the same shape. """ -function get_embedding(::KendallsShapeSpace{N,K}) where {N,K} - return KendallsPreShapeSpace(N, K) +get_embedding(::KendallsShapeSpace) + +function get_embedding(::KendallsShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} + return KendallsPreShapeSpace(n, k, parameter=:type) end +function get_embedding(M::KendallsShapeSpace{Tuple{Int,Int}}) + n, k = get_nk(M) + return KendallsPreShapeSpace(n, k) +end + +get_nk(::KendallsShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) +get_nk(M::KendallsShapeSpace{Tuple{Int,Int}}) = get_parameter(M.size) """ horizontal_component(::KendallsShapeSpace, p, X) @@ -129,7 +154,8 @@ given by ``n(k - 1) - 1 - n(n - 1)/2`` in the typical case where ``k \geq n+1``, ``(k + 1)(k - 2) / 2`` otherwise, unless ``k`` is equal to 1, in which case the dimension is 0. See [Kendall:1984](@cite) for a discussion of the over-dimensioned case. """ -function manifold_dimension(::KendallsShapeSpace{n,k}) where {n,k} +function manifold_dimension(M::KendallsShapeSpace) + n, k = get_nk(M) if k < n + 1 # over-dimensioned case if k == 1 return 0 @@ -167,11 +193,11 @@ rand(::KendallsShapeSpace; σ::Real=1.0) function Random.rand!( rng::AbstractRNG, - M::KendallsShapeSpace{n,k}, + M::KendallsShapeSpace, pX; vector_at=nothing, σ::Real=one(eltype(pX)), -) where {n,k} +) rand!(rng, get_embedding(M), pX; vector_at=vector_at, σ=σ) return pX end diff --git a/test/groups/general_linear.jl b/test/groups/general_linear.jl index b41ca1abd4..4afb0c6fc8 100644 --- a/test/groups/general_linear.jl +++ b/test/groups/general_linear.jl @@ -36,7 +36,7 @@ using NLsolve @testset "GL(1,𝔽) special cases" begin @testset "real" begin - G = GeneralLinear(1) + G = GeneralLinear(1; parameter=:type) p = 3.0 * ones(1, 1) X = 1.0 * ones(1, 1) @test exp(G, p, X) ≈ p * exp(X)' * exp(X - X') @@ -49,7 +49,7 @@ using NLsolve log_lie(G, Identity(G)) == zeros(1, 1) # Matrix to matrix end @testset "complex" begin - G = GeneralLinear(1, ℂ) + G = GeneralLinear(1, ℂ; parameter=:type) p = (1 + im) * ones(1, 1) X = (1 - im) * ones(1, 1) @test exp(G, p, X) ≈ p * exp(X)' * exp(X - X') @@ -138,7 +138,7 @@ using NLsolve end @testset "Complex" begin - G = GeneralLinear(2, ℂ) + G = GeneralLinear(2, ℂ; parameter=:type) @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 2, 3), true) @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 3, 3), true) diff --git a/test/groups/product_group.jl b/test/groups/product_group.jl index 9c3ef05b03..c2aaf6c73a 100644 --- a/test/groups/product_group.jl +++ b/test/groups/product_group.jl @@ -3,9 +3,9 @@ include("group_utils.jl") using RecursiveArrayTools @testset "Product group" begin - SOn = SpecialOrthogonal(3) + SOn = SpecialOrthogonal(3; parameter=:type) Tn = TranslationGroup(2) - Rn = Rotations(3) + Rn = Rotations(3; parameter=:type) M = ProductManifold(SOn, Tn) G = ProductGroup(M) @test_throws ErrorException ProductGroup(ProductManifold(Rotations(3), Stiefel(3, 2))) @@ -112,6 +112,6 @@ using RecursiveArrayTools ) @test sprint(show, "text/plain", G) === """ ProductGroup with 2 subgroups: - SpecialOrthogonal(3) + SpecialOrthogonal(3; parameter=:type) TranslationGroup(2; field = ℝ)""" end From 19cf3919b98a7e8f086d95ea124124112a7531d4 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 11 Sep 2023 14:56:49 +0200 Subject: [PATCH 20/81] more tests and some fixes --- src/groups/special_euclidean.jl | 26 +++++++++--------- src/manifolds/CholeskySpace.jl | 2 +- src/manifolds/Flag.jl | 6 ++-- src/manifolds/FlagOrthogonal.jl | 2 +- src/manifolds/Orthogonal.jl | 2 +- src/manifolds/Sphere.jl | 4 +-- src/manifolds/Symmetric.jl | 4 +-- src/manifolds/SymmetricPositiveDefinite.jl | 2 +- src/manifolds/Unitary.jl | 3 +- test/manifolds/euclidean.jl | 32 ++++++++++++++++++++++ test/manifolds/flag.jl | 7 +++++ test/manifolds/sphere.jl | 6 ++++ test/manifolds/unitary_matrices.jl | 4 +++ 13 files changed, 74 insertions(+), 26 deletions(-) diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index ff4438c6cc..fad35614b1 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -357,7 +357,7 @@ exponential (see [`exp_lie`](@ref)). exp_lie(::SpecialEuclidean, ::Any) @doc raw""" - exp_lie(G::SpecialEuclidean{2}, X) + exp_lie(G::SpecialEuclidean{TypeParameter{Tuple{2}}}, X) Compute the group exponential of $X = (b, Ω) ∈ 𝔰𝔢(2)$, where $b ∈ 𝔱(2)$ and $Ω ∈ 𝔰𝔬(2)$: @@ -374,10 +374,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. """ -exp_lie(::SpecialEuclidean{2}, ::Any) +exp_lie(::SpecialEuclidean{TypeParameter{Tuple{2}}}, ::Any) @doc raw""" - exp_lie(G::SpecialEuclidean{3}, X) + exp_lie(G::SpecialEuclidean{TypeParameter{Tuple{3}}}, X) Compute the group exponential of $X = (b, Ω) ∈ 𝔰𝔢(3)$, where $b ∈ 𝔱(3)$ and $Ω ∈ 𝔰𝔬(3)$: @@ -394,7 +394,7 @@ 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. """ -exp_lie(::SpecialEuclidean{3}, ::Any) +exp_lie(::SpecialEuclidean{TypeParameter{Tuple{3}}}, ::Any) function exp_lie!(G::SpecialEuclidean, q, X) Xmat = screw_matrix(G, X) @@ -403,7 +403,7 @@ function exp_lie!(G::SpecialEuclidean, q, X) _padpoint!(G, q) return q end -function exp_lie!(G::SpecialEuclidean{2}, q, X) +function exp_lie!(G::SpecialEuclidean{TypeParameter{Tuple{2}}}, q, X) SO2 = submanifold(G, 2) b, Ω = submanifold_components(G, X) t, R = submanifold_components(G, q) @@ -432,7 +432,7 @@ function exp_lie!(G::SpecialEuclidean{2}, q, X) end return q end -function exp_lie!(G::SpecialEuclidean{3}, q, X) +function exp_lie!(G::SpecialEuclidean{TypeParameter{Tuple{3}}}, q, X) SO3 = submanifold(G, 2) b, Ω = submanifold_components(G, X) t, R = submanifold_components(G, q) @@ -461,7 +461,7 @@ function exp_lie!(G::SpecialEuclidean{3}, q, X) end @doc raw""" - log_lie(G::SpecialEuclidean{n}, p) where {n} + log_lie(G::SpecialEuclidean, p) Compute the group logarithm of $p = (t, R) ∈ \mathrm{SE}(n)$, where $t ∈ \mathrm{T}(n)$ and $R ∈ \mathrm{SO}(n)$: @@ -478,7 +478,7 @@ In the [`affine_matrix`](@ref) representation, the group logarithm is the matrix log_lie(::SpecialEuclidean, ::Any) @doc raw""" - log_lie(G::SpecialEuclidean{2}, p) + log_lie(G::SpecialEuclidean{TypeParameter{Tuple{2}}}, p) Compute the group logarithm of $p = (t, R) ∈ \mathrm{SE}(2)$, where $t ∈ \mathrm{T}(2)$ and $R ∈ \mathrm{SO}(2)$: @@ -496,10 +496,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. """ -log_lie(::SpecialEuclidean{2}, ::Any) +log_lie(::SpecialEuclidean{TypeParameter{Tuple{2}}}, ::Any) @doc raw""" - log_lie(G::SpecialEuclidean{3}, p) + log_lie(G::SpecialEuclidean{TypeParameter{Tuple{3}}}, p) Compute the group logarithm of $p = (t, R) ∈ \mathrm{SE}(3)$, where $t ∈ \mathrm{T}(3)$ and $R ∈ \mathrm{SO}(3)$: @@ -517,7 +517,7 @@ 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. """ -log_lie(::SpecialEuclidean{3}, ::Any) +log_lie(::SpecialEuclidean{TypeParameter{Tuple{3}}}, ::Any) function _log_lie!(G::SpecialEuclidean, X, q) qmat = affine_matrix(G, q) @@ -526,7 +526,7 @@ function _log_lie!(G::SpecialEuclidean, X, q) _padvector!(G, X) return X end -function _log_lie!(G::SpecialEuclidean{2}, X, q) +function _log_lie!(G::SpecialEuclidean{TypeParameter{Tuple{2}}}, X, q) SO2 = submanifold(G, 2) b, Ω = submanifold_components(G, X) t, R = submanifold_components(G, q) @@ -544,7 +544,7 @@ function _log_lie!(G::SpecialEuclidean{2}, X, q) end return X end -function _log_lie!(G::SpecialEuclidean{3}, X, q) +function _log_lie!(G::SpecialEuclidean{TypeParameter{Tuple{3}}}, X, q) b, Ω = submanifold_components(G, X) t, R = submanifold_components(G, q) @assert size(Ω) == (3, 3) diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 1c713aceaf..6755ab5c7a 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -110,7 +110,7 @@ function exp!(::CholeskySpace, q, p, X) return q end -get_n(::CholeskySpace{TypeParameter{N}}) where {N} = N +get_n(::CholeskySpace{TypeParameter{Tuple{N}}}) where {N} = N get_n(M::CholeskySpace{Tuple{Int}}) = get_parameter(M.size)[1] @doc raw""" diff --git a/src/manifolds/Flag.jl b/src/manifolds/Flag.jl index 717450c38b..afa9971566 100644 --- a/src/manifolds/Flag.jl +++ b/src/manifolds/Flag.jl @@ -105,11 +105,11 @@ Get the embedding of the [`Flag`](@ref) manifold `M`, i.e. the [`Stiefel`](@ref) function get_embedding(M::Flag{Tuple{Int},dp1}) where {dp1} return Stiefel(M.size[1], M.subspace_dimensions[dp1 - 1]) end -function get_embedding(M::Flag{TypeParameter{N},dp1}) where {N,dp1} +function get_embedding(M::Flag{TypeParameter{Tuple{N}},dp1}) where {N,dp1} return Stiefel(N, M.subspace_dimensions[dp1 - 1]; parameter=:type) end -get_n(::Flag{TypeParameter{N}}) where {N} = N +get_n(::Flag{TypeParameter{Tuple{N}}}) where {N} = N get_n(M::Flag{Tuple{Int}}) = get_parameter(M.size)[1] @doc raw""" @@ -148,7 +148,7 @@ function manifold_dimension(M::Flag{<:Any,dp1}) where {dp1} return dim end -function Base.show(io::IO, M::Flag{TypeParameter{N}}) where {N} +function Base.show(io::IO, M::Flag{TypeParameter{Tuple{N}}}) where {N} print(io, "Flag($(N)") for d_i in M.subspace_dimensions.x[1:(end - 1)] print(io, ", $d_i") diff --git a/src/manifolds/FlagOrthogonal.jl b/src/manifolds/FlagOrthogonal.jl index f1de3f89b3..8795933c73 100644 --- a/src/manifolds/FlagOrthogonal.jl +++ b/src/manifolds/FlagOrthogonal.jl @@ -59,7 +59,7 @@ end Get embedding of [`Flag`](@ref) manifold `M`, i.e. the manifold [`OrthogonalMatrices`](@ref). """ -function get_embedding(::Flag{TypeParameter{N}}, p::OrthogonalPoint) where {N} +function get_embedding(::Flag{TypeParameter{Tuple{N}}}, p::OrthogonalPoint) where {N} return OrthogonalMatrices(N; parameter=:type) end get_embedding(M::Flag{Tuple{Int}}, p::OrthogonalPoint) = OrthogonalMatrices(M.size[1]) diff --git a/src/manifolds/Orthogonal.jl b/src/manifolds/Orthogonal.jl index ba4360ed91..b5e1b24c0c 100644 --- a/src/manifolds/Orthogonal.jl +++ b/src/manifolds/Orthogonal.jl @@ -36,7 +36,7 @@ function Random.rand!( return pX end -function Base.show(io::IO, ::OrthogonalMatrices{TypeParameter{n}}) where {n} +function Base.show(io::IO, ::OrthogonalMatrices{TypeParameter{Tuple{n}}}) where {n} return print(io, "OrthogonalMatrices($(n); parameter=:type)") end function Base.show(io::IO, M::OrthogonalMatrices{Tuple{Int}}) diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index bb3bca0001..46e4d68e4d 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -504,10 +504,10 @@ function Base.show(io::IO, M::Sphere{Tuple{Int},𝔽}) where {𝔽} n = get_n(M) return print(io, "Sphere($(n), $(𝔽))") end -function Base.show(io::IO, ::ArraySphere{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} +function Base.show(io::IO, ::ArraySphere{TypeParameter{tn},𝔽}) where {tn,𝔽} return print( io, - "ArraySphere($(join(n.parameters, ", ")); field = $(𝔽), parameter=:type)", + "ArraySphere($(join(tn.parameters, ", ")); field = $(𝔽), parameter=:type)", ) end function Base.show(io::IO, M::ArraySphere{<:Tuple,𝔽}) where {𝔽} diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 47a171ba94..902948ec03 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -128,7 +128,7 @@ function get_coordinates_orthonormal!( return Y end -function get_embedding(::SymmetricMatrices{TypeParameter{N},𝔽}) where {N,𝔽} +function get_embedding(::SymmetricMatrices{TypeParameter{Tuple{N}},𝔽}) where {N,𝔽} return Euclidean(N, N; field=𝔽, parameter=:type) end function get_embedding(M::SymmetricMatrices{Tuple{Int},𝔽}) where {𝔽} @@ -166,7 +166,7 @@ function get_vector_orthonormal!(M::SymmetricMatrices{<:Any,ℂ}, Y, p, X, ::Com end ## unify within bases later. -get_n(::SymmetricMatrices{TypeParameter{n}}) where {n} = n +get_n(::SymmetricMatrices{TypeParameter{Tuple{n}}}) where {n} = n get_n(M::SymmetricMatrices{Tuple{Int}}) = get_parameter(M.size)[1] """ diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index d70b1d9282..e0c922e31a 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -232,7 +232,7 @@ function get_embedding(M::SymmetricPositiveDefinite) return Euclidean(representation_size(M)...; field=ℝ) end -get_n(::SymmetricPositiveDefinite{TypeParameter{N}}) where {N} = N +get_n(::SymmetricPositiveDefinite{TypeParameter{Tuple{N}}}) where {N} = N get_n(M::SymmetricPositiveDefinite{Tuple{Int}}) = get_parameter(M.size)[1] @doc raw""" diff --git a/src/manifolds/Unitary.jl b/src/manifolds/Unitary.jl index 07352edab1..bb5867b201 100644 --- a/src/manifolds/Unitary.jl +++ b/src/manifolds/Unitary.jl @@ -93,7 +93,7 @@ function manifold_dimension(M::UnitaryMatrices{<:Any,ℂ}) return n^2 end @doc raw""" - manifold_dimension(M::UnitaryMatrices{n,ℍ}) + manifold_dimension(M::UnitaryMatrices{<:Any,ℍ}) Return the dimension of the manifold unitary matrices. ```math @@ -183,4 +183,3 @@ function Weingarten!(::UnitaryMatrices, Y, p, X, V) end zero_vector(::UnitaryMatrices{TypeParameter{Tuple{1}},ℍ}, p) = zero(p) -zero_vector(::UnitaryMatrices{1,ℍ}, p) = zero(p) diff --git a/test/manifolds/euclidean.jl b/test/manifolds/euclidean.jl index e2daf17cdb..99c3ddecdf 100644 --- a/test/manifolds/euclidean.jl +++ b/test/manifolds/euclidean.jl @@ -395,4 +395,36 @@ using FiniteDifferences X = zeros(3) @test volume_density(E, p, X) == 1.0 end + + @testset "static parameter" begin + Ms = Euclidean(1; parameter=:type) + M0s = Euclidean(; parameter=:type) + + @test distance(Ms, 2.0, 4.0) == 2.0 + @test distance(M0s, 2.0, 4.0) == 2.0 + @test log(M0s, 2.0, 4.0) == 2.0 + @test manifold_dimension(M0s) == 1 + @test project(M0s, 2.0, 4.0) == 4.0 + @test retract(M0s, 2.0, 4.0) == 6.0 + @test retract(M0s, 2.0, 4.0, ExponentialRetraction()) == 6.0 + + @test ManifoldDiff.adjoint_Jacobi_field( + M0s, + 0.0, + 1.0, + 0.5, + 2.0, + ManifoldDiff.βdifferential_shortest_geodesic_startpoint, + ) === 2.0 + @test ManifoldDiff.diagonalizing_projectors(M0s, 0.0, 2.0) == + ((0.0, ManifoldDiff.IdentityProjector()),) + @test ManifoldDiff.jacobi_field( + M0s, + 0.0, + 1.0, + 0.5, + 2.0, + ManifoldDiff.βdifferential_shortest_geodesic_startpoint, + ) === 2.0 + end end diff --git a/test/manifolds/flag.jl b/test/manifolds/flag.jl index 6df6278aa8..61995d434f 100644 --- a/test/manifolds/flag.jl +++ b/test/manifolds/flag.jl @@ -269,4 +269,11 @@ using Random @test retract(M, p1_ortho, X1_ortho, QRRetraction()).value ≈ retract(OrthogonalMatrices(5), p1_ortho.value, X1_ortho.value, QRRetraction()) end + + @testset "static parameters" begin + M = Flag(5, 1, 2; parameter=:type) + @test Manifolds.get_n(M) == 5 + @test get_embedding(M) == Stiefel(5, 2; parameter=:type) + @test repr(M) == "Flag(5, 1, 2; parameter=:type)" + end end diff --git a/test/manifolds/sphere.jl b/test/manifolds/sphere.jl index 8a32ea0782..734641a9ea 100644 --- a/test/manifolds/sphere.jl +++ b/test/manifolds/sphere.jl @@ -282,4 +282,10 @@ using ManifoldsBase: TFVector @test manifold_volume(Sphere(3)) ≈ 2 * π * π @test volume_density(M, p, [0.0, 0.5, 0.5]) ≈ 0.9187253698655684 end + + @testset "static parameter" begin + @test repr(Sphere(2; parameter=:type)) == "Sphere(2, ℝ; parameter=:type)" + @test repr(ArraySphere(2, 3; parameter=:type)) == + "ArraySphere(2, 3; field = ℝ, parameter=:type)" + end end diff --git a/test/manifolds/unitary_matrices.jl b/test/manifolds/unitary_matrices.jl index cc443c1dfa..3345f116f3 100644 --- a/test/manifolds/unitary_matrices.jl +++ b/test/manifolds/unitary_matrices.jl @@ -5,6 +5,8 @@ using Quaternions @testset "Orthogonal Matrices" begin M = OrthogonalMatrices(3) @test repr(M) == "OrthogonalMatrices(3)" + @test repr(OrthogonalMatrices(3; parameter=:type)) == + "OrthogonalMatrices(3; parameter=:type)" @test injectivity_radius(M, PolarRetraction()) == π / sqrt(2.0) @test manifold_dimension(M) == 3 @test injectivity_radius(M) == π * sqrt(2.0) @@ -22,6 +24,7 @@ end @testset "Unitary Matrices" begin M = UnitaryMatrices(2) @test repr(M) == "UnitaryMatrices(2)" + @test repr(UnitaryMatrices(2; parameter=:type)) == "UnitaryMatrices(2; parameter=:type)" @test manifold_dimension(M) == 4 @test !is_flat(M) @test injectivity_radius(M) == π @@ -75,6 +78,7 @@ end @testset "Quaternionic Unitary Matrices" begin M = UnitaryMatrices(1, ℍ; parameter=:type) @test repr(M) == "UnitaryMatrices(1, ℍ; parameter=:type)" + @test repr(UnitaryMatrices(1, ℍ)) == "UnitaryMatrices(1, ℍ)" @test manifold_dimension(M) == 3 @test injectivity_radius(M) == π @test !is_flat(M) From 2ad761c8a34169d4fcb462ebb10539c5266f593a Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 11 Sep 2023 14:57:19 +0200 Subject: [PATCH 21/81] Update NEWS.md Co-authored-by: Ronny Bergmann --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 38a4f81df7..24619d9de9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Vector bundles are generalized to fiber bundles. -- `RotationTranslationAction`. +- `RotationTranslationAction` is introduced. - `DirectSumType` for vector bundles: `MultitangentBundle`, `MultitangentBundleFibers`, `MultitangentSpaceAtPoint`. ### Changed From 8e441e52fd26291264a9fc0be49d5b255bfcbdcb Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 12 Sep 2023 18:05:25 +0200 Subject: [PATCH 22/81] address review regarding optionally static size --- NEWS.md | 7 +- src/groups/general_linear.jl | 21 ++-- src/groups/general_unitary_groups.jl | 2 - src/groups/heisenberg.jl | 55 +++++------ src/groups/orthogonal.jl | 10 +- src/groups/special_euclidean.jl | 6 +- src/groups/special_linear.jl | 37 +++++-- src/groups/special_orthogonal.jl | 8 +- src/groups/special_unitary.jl | 12 +-- src/groups/translation_group.jl | 11 +-- src/groups/unitary.jl | 14 +-- src/manifolds/CenteredMatrices.jl | 19 ++-- src/manifolds/CholeskySpace.jl | 17 ++-- src/manifolds/Elliptope.jl | 25 +++-- src/manifolds/Euclidean.jl | 8 +- src/manifolds/FixedRankMatrices.jl | 43 ++++---- src/manifolds/Flag.jl | 21 ++-- src/manifolds/FlagOrthogonal.jl | 12 ++- src/manifolds/GeneralUnitaryMatrices.jl | 53 +++++----- src/manifolds/GeneralizedGrassmann.jl | 25 +++-- src/manifolds/GeneralizedStiefel.jl | 25 +++-- src/manifolds/Grassmann.jl | 17 ++-- src/manifolds/GrassmannProjector.jl | 16 +-- src/manifolds/GrassmannStiefel.jl | 18 ++-- src/manifolds/Hyperbolic.jl | 23 ++--- src/manifolds/HyperbolicHyperboloid.jl | 4 +- src/manifolds/HyperbolicPoincareBall.jl | 10 +- src/manifolds/HyperbolicPoincareHalfspace.jl | 10 +- src/manifolds/KendallsPreShapeSpace.jl | 17 ++-- src/manifolds/KendallsShapeSpace.jl | 27 +++--- src/manifolds/Lorentz.jl | 8 +- src/manifolds/Multinomial.jl | 33 +++---- src/manifolds/MultinomialDoublyStochastic.jl | 27 +++--- src/manifolds/MultinomialSymmetric.jl | 25 +++-- src/manifolds/Oblique.jl | 29 +++--- src/manifolds/Orthogonal.jl | 8 +- src/manifolds/ProbabilitySimplex.jl | 33 +++---- .../ProbabilitySimplexEuclideanMetric.jl | 2 +- src/manifolds/ProjectiveSpace.jl | 29 +++--- src/manifolds/Rotations.jl | 30 +++--- src/manifolds/SPDFixedDeterminant.jl | 44 ++++++--- src/manifolds/SkewHermitian.jl | 37 ++++--- src/manifolds/Spectrahedron.jl | 32 +++--- src/manifolds/Sphere.jl | 37 ++++--- src/manifolds/SphereSymmetricMatrices.jl | 21 ++-- src/manifolds/Stiefel.jl | 39 ++++---- src/manifolds/StiefelCanonicalMetric.jl | 6 +- src/manifolds/StiefelEuclideanMetric.jl | 4 +- src/manifolds/StiefelSubmersionMetric.jl | 14 +-- src/manifolds/Symmetric.jl | 27 +++--- src/manifolds/SymmetricPositiveDefinite.jl | 27 +++--- ...ymmetricPositiveDefiniteAffineInvariant.jl | 8 +- .../SymmetricPositiveDefiniteLogCholesky.jl | 10 +- .../SymmetricPositiveSemidefiniteFixedRank.jl | 35 +++---- src/manifolds/Symplectic.jl | 91 ++++++++++------- src/manifolds/SymplecticStiefel.jl | 97 ++++++++++++------- src/manifolds/Tucker.jl | 21 ++-- src/manifolds/Unitary.jl | 18 ++-- test/groups/general_linear.jl | 6 +- test/groups/general_unitary_groups.jl | 20 ++-- test/groups/power_group.jl | 2 +- test/groups/product_group.jl | 8 +- test/groups/special_euclidean.jl | 4 +- test/groups/special_orthogonal.jl | 6 +- test/groups/translation_group.jl | 12 ++- test/manifolds/centered_matrices.jl | 2 +- test/manifolds/essential_manifold.jl | 2 +- test/manifolds/euclidean.jl | 18 ++-- test/manifolds/flag.jl | 10 +- test/manifolds/hyperbolic.jl | 2 +- test/manifolds/multinomial_matrices.jl | 11 ++- test/manifolds/oblique.jl | 9 +- test/manifolds/power_manifold.jl | 3 +- test/manifolds/product_manifold.jl | 4 +- test/manifolds/projective_space.jl | 4 +- test/manifolds/skewhermitian.jl | 6 +- test/manifolds/sphere.jl | 24 ++--- test/manifolds/sphere_symmetric_matrices.jl | 7 +- test/manifolds/stiefel.jl | 8 +- test/manifolds/symmetric.jl | 6 +- test/manifolds/symmetric_positive_definite.jl | 4 + test/manifolds/symplectic.jl | 4 +- test/manifolds/symplecticstiefel.jl | 2 +- test/manifolds/unitary_matrices.jl | 14 +-- test/recipes.jl | 28 +++--- 85 files changed, 816 insertions(+), 775 deletions(-) diff --git a/NEWS.md b/NEWS.md index 24619d9de9..03bced2917 100644 --- a/NEWS.md +++ b/NEWS.md @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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 + The default is set to store the size in type parameter. For field storage, pass the `parameter=:type` keyword argument to manifold constructor. Related changes: - 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: - `CenteredMatrices{m,n}`, @@ -38,14 +38,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MultinomialSymmetric{n}`, - `Orthogonal{n}`, - `ProbabilitySimplex{n}`, + - `SPDFixedDeterminant{n}`, + - `SpecialLinear{n}`, - `SpecialOrthogonal{n}`, - `SpecialUnitary{n}`, - `SpecialEuclideanManifold{n}`, + - `Spectrahedron{n,k}`, - `SphereSymmetricMatrices{N}`, - `Stiefel{n,k}`, - `SymmetricMatrices{N}`, - `SymmetricPositiveDefinite{n}`, - `SymmetricPositiveSemidefiniteFixedRank{n,k}`, + - `Symplectic{n}`, + - `SymplecticStiefel{n,k}`, - `TranslationGroup`, - `Tucker`. diff --git a/src/groups/general_linear.jl b/src/groups/general_linear.jl index d734d5daef..62673e6519 100644 --- a/src/groups/general_linear.jl +++ b/src/groups/general_linear.jl @@ -28,7 +28,7 @@ function active_traits(f, ::GeneralLinear, args...) ) end -function GeneralLinear(n::Int, 𝔽::AbstractNumbers=ℝ; parameter::Symbol=:field) +function GeneralLinear(n::Int, 𝔽::AbstractNumbers=ℝ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return GeneralLinear{typeof(size),𝔽}(size) end @@ -128,23 +128,20 @@ function get_coordinates!( end function get_embedding(::GeneralLinear{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return Euclidean(n, n; field=𝔽, parameter=:type) + return Euclidean(n, n; field=𝔽) end function get_embedding(M::GeneralLinear{Tuple{Int},𝔽}) where {𝔽} - n = get_n(M) - return Euclidean(n, n; field=𝔽) + n = get_parameter(M.size)[1] + return Euclidean(n, n; field=𝔽, parameter=:field) end -get_n(::GeneralLinear{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::GeneralLinear{Tuple{Int}}) = get_parameter(M.size)[1] - function get_vector( M::GeneralLinear{<:Any,ℝ}, p, Xⁱ, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}, ) - n = get_n(M) + n = get_parameter(M.size)[1] return reshape(Xⁱ, n, n) end @@ -206,7 +203,7 @@ function log(M::GeneralLinear, p, q) end function log!(G::GeneralLinear{<:Any,𝔽}, X, p, q) where {𝔽} - n = get_n(G) + n = get_parameter(G.size)[1] pinvq = inverse_translate(G, p, q, LeftForwardAction()) 𝔽 === ℝ && det(pinvq) ≤ 0 && throw(OutOfInjectivityRadiusError()) if isnormal(pinvq; atol=sqrt(eps(real(eltype(pinvq))))) @@ -271,11 +268,11 @@ function Random.rand!(rng::AbstractRNG, G::GeneralLinear, pX; kwargs...) end function Base.show(io::IO, ::GeneralLinear{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return print(io, "GeneralLinear($n, $𝔽; parameter=:type)") + return print(io, "GeneralLinear($n, $𝔽)") end function Base.show(io::IO, M::GeneralLinear{Tuple{Int},𝔽}) where {𝔽} - n = get_n(M) - return print(io, "GeneralLinear($n, $𝔽)") + n = get_parameter(M.size)[1] + return print(io, "GeneralLinear($n, $𝔽; parameter=:field)") end translate_diff(::GeneralLinear, p, q, X, ::LeftForwardAction) = X diff --git a/src/groups/general_unitary_groups.jl b/src/groups/general_unitary_groups.jl index bf66c16498..64d2026f68 100644 --- a/src/groups/general_unitary_groups.jl +++ b/src/groups/general_unitary_groups.jl @@ -8,8 +8,6 @@ struct GeneralUnitaryMultiplicationGroup{T,𝔽,S} <: AbstractDecoratorManifold{ manifold::GeneralUnitaryMatrices{T,𝔽,S} end -get_n(M::GeneralUnitaryMultiplicationGroup) = get_n(M.manifold) - @inline function active_traits(f, ::GeneralUnitaryMultiplicationGroup, args...) if is_metric_function(f) #pass to Rotations by default - but keep Group Decorator for the retraction diff --git a/src/groups/heisenberg.jl b/src/groups/heisenberg.jl index 28e7945e2c..de94e14bf0 100644 --- a/src/groups/heisenberg.jl +++ b/src/groups/heisenberg.jl @@ -20,7 +20,7 @@ struct HeisenbergGroup{T} <: AbstractDecoratorManifold{ℝ} size::T end -function HeisenbergGroup(n::Int; parameter::Symbol=:field) +function HeisenbergGroup(n::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return HeisenbergGroup{typeof(size)}(size) end @@ -34,16 +34,16 @@ function active_traits(f, ::HeisenbergGroup, args...) end function _heisenberg_a_view(M::HeisenbergGroup, p) - n = get_n(M) + n = get_parameter(M.size)[1] return view(p, 1, 2:(n + 1)) end function _heisenberg_b_view(M::HeisenbergGroup, p) - n = get_n(M) + n = get_parameter(M.size)[1] return view(p, 2:(n + 1), n + 2) end function check_point(G::HeisenbergGroup, p; kwargs...) - n = get_n(G) + n = get_parameter(G.size)[1] if !isone(p[1, 1]) return DomainError( p[1, 1], @@ -79,7 +79,7 @@ function check_point(G::HeisenbergGroup, p; kwargs...) end function check_vector(G::HeisenbergGroup, p, X; kwargs...) - n = get_n(G) + n = get_parameter(G.size)[1] if !iszero(X[1, 1]) return DomainError( X[1, 1], @@ -119,12 +119,12 @@ the coordinates are concatenated vectors ``\mathbf{a}``, ``\mathbf{b}``, and num get_coordinates(::HeisenbergGroup, p, X, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) function get_coordinates_orthonormal(M::HeisenbergGroup, p, X, ::RealNumbers) - n = get_n(M) + n = get_parameter(M.size)[1] return vcat(_heisenberg_a_view(M, X), _heisenberg_b_view(M, X), X[1, n + 2]) end function get_coordinates_orthonormal!(M::HeisenbergGroup, Xⁱ, p, X, ::RealNumbers) - n = get_n(M) + n = get_parameter(M.size)[1] Xⁱ[1:n] .= _heisenberg_a_view(M, X) Xⁱ[(n + 1):(2 * n)] .= _heisenberg_b_view(M, X) Xⁱ[2 * n + 1] = X[1, n + 2] @@ -132,16 +132,13 @@ function get_coordinates_orthonormal!(M::HeisenbergGroup, Xⁱ, p, X, ::RealNumb end function get_embedding(::HeisenbergGroup{TypeParameter{Tuple{n}}}) where {n} - return Euclidean(n + 2, n + 2; parameter=:type) + return Euclidean(n + 2, n + 2) end function get_embedding(M::HeisenbergGroup{Tuple{Int}}) - n = get_n(M) - return Euclidean(n + 2, n + 2) + n = get_parameter(M.size)[1] + return Euclidean(n + 2, n + 2; parameter=:field) end -get_n(::HeisenbergGroup{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::HeisenbergGroup{Tuple{Int}}) = get_parameter(M.size)[1] - @doc raw""" get_vector(M::HeisenbergGroup, p, Xⁱ, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) @@ -156,7 +153,7 @@ Given a vector of coordinates ``\begin{bmatrix}\mathbb{a} & \mathbb{b} & c\end{b get_vector(M::HeisenbergGroup, p, c, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) function get_vector_orthonormal(M::HeisenbergGroup, p, Xⁱ, ::RealNumbers) - n = get_n(M) + n = get_parameter(M.size)[1] return [ 0 Xⁱ[1:n] Xⁱ[2 * n + 1] zeros(n, n + 1) Xⁱ[(n + 1):(2 * n)]' @@ -165,7 +162,7 @@ function get_vector_orthonormal(M::HeisenbergGroup, p, Xⁱ, ::RealNumbers) end function get_vector_orthonormal!(M::HeisenbergGroup, X, p, Xⁱ, ::RealNumbers) - n = get_n(M) + n = get_parameter(M.size)[1] fill!(X, 0) X[1, 2:(n + 1)] .= Xⁱ[1:n] X[2:(n + 1), n + 2] .= Xⁱ[(n + 1):(2 * n)] @@ -191,7 +188,7 @@ and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors. exp_lie(M::HeisenbergGroup, X) function exp_lie!(M::HeisenbergGroup, q, X) - n = get_n(M) + n = get_parameter(M.size)[1] copyto!(q, I) a_view = _heisenberg_a_view(M, X) b_view = _heisenberg_b_view(M, X) @@ -222,7 +219,7 @@ and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors. exp(M::HeisenbergGroup, p, X) function exp!(M::HeisenbergGroup, q, p, X) - n = get_n(M) + n = get_parameter(M.size)[1] copyto!(q, I) a_p_view = _heisenberg_a_view(M, p) b_p_view = _heisenberg_b_view(M, p) @@ -243,7 +240,7 @@ Return the injectivity radius on the [`HeisenbergGroup`](@ref) `M`, which is `` injectivity_radius(::HeisenbergGroup) = Inf function inner(M::HeisenbergGroup, p, X, Y) - n = get_n(M) + n = get_parameter(M.size)[1] X_a_view = _heisenberg_a_view(M, X) X_b_view = _heisenberg_b_view(M, X) Y_a_view = _heisenberg_a_view(M, Y) @@ -274,7 +271,7 @@ and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors. log(::HeisenbergGroup, p, q) function log!(M::HeisenbergGroup, X, p, q) - n = get_n(M) + n = get_parameter(M.size)[1] fill!(X, 0) a_p_view = _heisenberg_a_view(M, p) b_p_view = _heisenberg_b_view(M, p) @@ -306,7 +303,7 @@ and ``\mathbf{a}⋅\mathbf{b}`` is dot product of vectors. log_lie(M::HeisenbergGroup, p) function log_lie!(M::HeisenbergGroup, X, p) - n = get_n(M) + n = get_parameter(M.size)[1] fill!(X, 0) view_a_X = _heisenberg_a_view(M, X) view_b_X = _heisenberg_b_view(M, X) @@ -321,7 +318,7 @@ function log_lie!(::HeisenbergGroup, X, ::Identity{MultiplicationOperation}) return X end -manifold_dimension(M::HeisenbergGroup) = 2 * get_n(M) + 1 +manifold_dimension(M::HeisenbergGroup) = 2 * get_parameter(M.size)[1] + 1 parallel_transport_to(::HeisenbergGroup, p, X, q) = X @@ -335,7 +332,7 @@ Sets the diagonal elements to 1 and all non-diagonal elements except the first r last column to 0. """ function project(M::HeisenbergGroup, p) - n = get_n(M) + n = get_parameter(M.size)[1] return [ 1 p[1, 2:(n + 2)]' zeros(n, 1) Matrix(I, n, n) _heisenberg_b_view(M, p) @@ -351,7 +348,7 @@ Sets the diagonal elements to 0 and all non-diagonal elements except the first r last column to 0. """ function project(M::HeisenbergGroup, p, X) - n = get_n(M) + n = get_parameter(M.size)[1] return [ 0 X[1, 2:(n + 2)]' zeros(n, n + 1) _heisenberg_b_view(M, X) @@ -360,14 +357,14 @@ function project(M::HeisenbergGroup, p, X) end function project!(M::HeisenbergGroup, q, p) - n = get_n(M) + n = get_parameter(M.size)[1] copyto!(q, I) q[1, 2:(n + 2)] .= p[1, 2:(n + 2)] q[2:(n + 1), n + 2] .= _heisenberg_b_view(M, p) return q end function project!(M::HeisenbergGroup, Y, p, X) - n = get_n(M) + n = get_parameter(M.size)[1] fill!(Y, 0) Y[1, 2:(n + 2)] .= X[1, 2:(n + 2)] Y[2:(n + 1), n + 2] .= _heisenberg_b_view(M, X) @@ -394,7 +391,7 @@ function Random.rand!( σ::Real=one(eltype(pX)), vector_at=nothing, ) - n = get_n(M) + n = get_parameter(M.size)[1] if vector_at === nothing copyto!(pX, I) va = view(pX, 1, 2:(n + 2)) @@ -413,11 +410,11 @@ function Random.rand!( end function Base.show(io::IO, ::HeisenbergGroup{TypeParameter{Tuple{n}}}) where {n} - return print(io, "HeisenbergGroup($(n); parameter=:type)") + return print(io, "HeisenbergGroup($(n))") end function Base.show(io::IO, M::HeisenbergGroup{Tuple{Int}}) - n = get_n(M) - return print(io, "HeisenbergGroup($(n))") + n = get_parameter(M.size)[1] + return print(io, "HeisenbergGroup($(n); parameter=:field)") end translate_diff(::HeisenbergGroup, p, q, X, ::LeftForwardAction) = X diff --git a/src/groups/orthogonal.jl b/src/groups/orthogonal.jl index 9f4672d860..ed6a476b0d 100644 --- a/src/groups/orthogonal.jl +++ b/src/groups/orthogonal.jl @@ -5,18 +5,18 @@ Orthogonal group $\mathrm{O}(n)$ represented by [`OrthogonalMatrices`](@ref). # Constructor - Orthogonal(n::Int; parameter::Symbol=:field) + Orthogonal(n::Int; parameter::Symbol=:type) """ const Orthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,AbsoluteDeterminantOneMatrices} -function Orthogonal(n::Int; parameter::Symbol=:field) +function Orthogonal(n::Int; parameter::Symbol=:type) return GeneralUnitaryMultiplicationGroup(OrthogonalMatrices(n; parameter=parameter)) end function Base.show(io::IO, ::Orthogonal{TypeParameter{Tuple{n}}}) where {n} - return print(io, "Orthogonal($(n); parameter=:type)") + return print(io, "Orthogonal($(n))") end function Base.show(io::IO, M::Orthogonal{Tuple{Int}}) - n = get_n(M) - return print(io, "Orthogonal($(n))") + n = get_parameter(M.size)[1] + return print(io, "Orthogonal($(n); parameter=:field)") end diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index fad35614b1..9ce2cc1ec1 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=:field) +function SpecialEuclidean(n; parameter::Symbol=:type) Tn = TranslationGroup(n; parameter=parameter) SOn = SpecialOrthogonal(n; parameter=parameter) A = RotationAction(Tn, SOn) @@ -48,11 +48,11 @@ const SpecialEuclideanOperation{N} = SemidirectProductOperation{ const SpecialEuclideanIdentity{N} = Identity{SpecialEuclideanOperation{N}} function Base.show(io::IO, ::SpecialEuclidean{TypeParameter{Tuple{n}}}) where {n} - return print(io, "SpecialEuclidean($(n); parameter=:type)") + return print(io, "SpecialEuclidean($(n))") end function Base.show(io::IO, G::SpecialEuclidean{Tuple{Int}}) n = get_n(G) - return print(io, "SpecialEuclidean($(n))") + return print(io, "SpecialEuclidean($(n); parameter=:field)") end @inline function active_traits(f, M::SpecialEuclidean, args...) diff --git a/src/groups/special_linear.jl b/src/groups/special_linear.jl index 7d2f848d99..cc2ff3cdee 100644 --- a/src/groups/special_linear.jl +++ b/src/groups/special_linear.jl @@ -1,5 +1,5 @@ @doc raw""" - SpecialLinear{n,𝔽} <: AbstractDecoratorManifold + SpecialLinear{T,𝔽} <: AbstractDecoratorManifold The special linear group ``\mathrm{SL}(n,𝔽)`` that is, the group of all invertible matrices with unit determinant in ``𝔽^{n×n}``. @@ -15,9 +15,14 @@ metric used for [`GeneralLinear(n, 𝔽)`](@ref). The resulting geodesic on an element of ``𝔰𝔩(n, 𝔽)`` is a closed subgroup of ``\mathrm{SL}(n,𝔽)``. As a result, most metric functions forward to [`GeneralLinear`](@ref). """ -struct SpecialLinear{n,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct SpecialLinear{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end -SpecialLinear(n, 𝔽::AbstractNumbers=ℝ) = SpecialLinear{n,𝔽}() +function SpecialLinear(n, 𝔽::AbstractNumbers=ℝ; parameter::Symbol=:type) + size = wrap_type_parameter(parameter, (n,)) + return SpecialLinear{typeof(size),𝔽}(size) +end @inline function active_traits(f, ::SpecialLinear, args...) return merge_traits( @@ -28,11 +33,11 @@ SpecialLinear(n, 𝔽::AbstractNumbers=ℝ) = SpecialLinear{n,𝔽}() ) end -function allocation_promotion_function(::SpecialLinear{n,ℂ}, f, args::Tuple) where {n} +function allocation_promotion_function(::SpecialLinear{<:Any,ℂ}, f, args::Tuple) return complex end -function check_point(G::SpecialLinear{n,𝔽}, p; kwargs...) where {n,𝔽} +function check_point(G::SpecialLinear, p; kwargs...) detp = det(p) if !isapprox(detp, 1; kwargs...) return DomainError( @@ -59,7 +64,13 @@ end embed(::SpecialLinear, p) = p embed(::SpecialLinear, p, X) = X -get_embedding(::SpecialLinear{n,𝔽}) where {n,𝔽} = GeneralLinear(n, 𝔽) +function get_embedding(::SpecialLinear{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return GeneralLinear(n, 𝔽) +end +function get_embedding(M::SpecialLinear{Tuple{Int},𝔽}) where {𝔽} + n = get_parameter(M.size)[1] + return GeneralLinear(n, 𝔽; parameter=:field) +end inverse_translate_diff(::SpecialLinear, p, q, X, ::LeftForwardAction) = X inverse_translate_diff(::SpecialLinear, p, q, X, ::RightBackwardAction) = p * X / p @@ -95,7 +106,8 @@ D_{ij} = δ_{ij} \begin{cases} """ project(::SpecialLinear, p) -function project!(::SpecialLinear{n}, q, p) where {n} +function project!(M::SpecialLinear, q, p) + n = get_parameter(M.size)[1] detp = det(p) isapprox(detp, 1) && return copyto!(q, p) F = svd(p) @@ -119,14 +131,21 @@ where the last expression uses the tangent space representation as the Lie algeb """ project(::SpecialLinear, p, X) -function project!(G::SpecialLinear{n}, Y, p, X) where {n} +function project!(G::SpecialLinear, Y, p, X) + n = get_parameter(G.size)[1] inverse_translate_diff!(G, Y, p, p, X, LeftForwardAction()) Y[diagind(n, n)] .-= tr(Y) / n translate_diff!(G, Y, p, p, Y, LeftForwardAction()) return Y end -Base.show(io::IO, ::SpecialLinear{n,𝔽}) where {n,𝔽} = print(io, "SpecialLinear($n, $𝔽)") +function Base.show(io::IO, ::SpecialLinear{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return print(io, "SpecialLinear($n, $𝔽)") +end +function Base.show(io::IO, M::SpecialLinear{Tuple{Int},𝔽}) where {𝔽} + n = get_parameter(M.size)[1] + return print(io, "SpecialLinear($n, $𝔽; parameter=:field)") +end translate_diff(::SpecialLinear, p, q, X, ::LeftForwardAction) = X translate_diff(::SpecialLinear, p, q, X, ::RightBackwardAction) = p \ X * p diff --git a/src/groups/special_orthogonal.jl b/src/groups/special_orthogonal.jl index ee68f1f826..3cb7c947dc 100644 --- a/src/groups/special_orthogonal.jl +++ b/src/groups/special_orthogonal.jl @@ -8,7 +8,7 @@ Special orthogonal group ``\mathrm{SO}(n)`` represented by rotation matrices, se """ const SpecialOrthogonal{n} = GeneralUnitaryMultiplicationGroup{n,ℝ,DeterminantOneMatrices} -function SpecialOrthogonal(n; parameter::Symbol=:field) +function SpecialOrthogonal(n; parameter::Symbol=:type) return GeneralUnitaryMultiplicationGroup(Rotations(n; parameter=parameter)) end @@ -16,9 +16,9 @@ Base.inv(::SpecialOrthogonal, p) = transpose(p) Base.inv(::SpecialOrthogonal, e::Identity{MultiplicationOperation}) = e function Base.show(io::IO, ::SpecialOrthogonal{TypeParameter{Tuple{n}}}) where {n} - return print(io, "SpecialOrthogonal($(n); parameter=:type)") + return print(io, "SpecialOrthogonal($(n))") end function Base.show(io::IO, M::SpecialOrthogonal{Tuple{Int}}) - n = get_n(M) - return print(io, "SpecialOrthogonal($(n))") + n = get_parameter(M.size)[1] + return print(io, "SpecialOrthogonal($(n); parameter=:field)") end diff --git a/src/groups/special_unitary.jl b/src/groups/special_unitary.jl index c9d07ebe9d..be2d3af60d 100644 --- a/src/groups/special_unitary.jl +++ b/src/groups/special_unitary.jl @@ -20,7 +20,7 @@ Generate the Lie group of ``n×n`` unitary matrices with determinant +1. """ const SpecialUnitary{T} = GeneralUnitaryMultiplicationGroup{T,ℂ,DeterminantOneMatrices} -function SpecialUnitary(n::Int; parameter::Symbol=:field) +function SpecialUnitary(n::Int; parameter::Symbol=:type) return GeneralUnitaryMultiplicationGroup( GeneralUnitaryMatrices(n, ℂ, DeterminantOneMatrices; parameter=parameter), ) @@ -55,7 +55,7 @@ function project(G::SpecialUnitary, p, X) end function project!(G::SpecialUnitary, q, p) - n = get_n(G) + n = get_parameter(G.manifold.size)[1] F = svd(p) detUVt = det(F.U) * det(F.Vt) if !isreal(detUVt) || real(detUVt) < 0 @@ -69,7 +69,7 @@ function project!(G::SpecialUnitary, q, p) return q end function project!(G::SpecialUnitary, Y, p, X) - n = get_n(G) + n = get_parameter(G.manifold.size)[1] inverse_translate_diff!(G, Y, p, p, X, LeftForwardAction()) project!(SkewHermitianMatrices(n, ℂ), Y, Y) Y[diagind(n, n)] .-= tr(Y) / n @@ -78,9 +78,9 @@ function project!(G::SpecialUnitary, Y, p, X) end function Base.show(io::IO, ::SpecialUnitary{TypeParameter{Tuple{n}}}) where {n} - return print(io, "SpecialUnitary($(n); parameter=:type)") + return print(io, "SpecialUnitary($(n))") end function Base.show(io::IO, G::SpecialUnitary{Tuple{Int}}) - n = get_n(G) - return print(io, "SpecialUnitary($(n))") + n = get_parameter(G.manifold.size)[1] + return print(io, "SpecialUnitary($(n); parameter=:field)") end diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index 401ab27fde..8705703cf6 100644 --- a/src/groups/translation_group.jl +++ b/src/groups/translation_group.jl @@ -4,14 +4,14 @@ Translation group $\mathrm{T}(n)$ represented by translation arrays. # Constructor - TranslationGroup(n₁,...,nᵢ; field = 𝔽, parameter::Symbol=:field) + TranslationGroup(n₁,...,nᵢ; field = 𝔽, parameter::Symbol=:type) Generate the translation group on $𝔽^{n₁,…,nᵢ}$ = `Euclidean(n₁,...,nᵢ; field = 𝔽)`, which is isomorphic to the group itself. """ const TranslationGroup{T,𝔽} = GroupManifold{𝔽,Euclidean{T,𝔽},AdditionOperation} -function TranslationGroup(n::Int...; field::AbstractNumbers=ℝ, parameter::Symbol=:field) +function TranslationGroup(n::Int...; field::AbstractNumbers=ℝ, parameter::Symbol=:type) size = wrap_type_parameter(parameter, n) return TranslationGroup{typeof(size),field}( Euclidean(n...; field=field, parameter=parameter), @@ -47,12 +47,9 @@ end function Base.show(io::IO, M::TranslationGroup{N,𝔽}) where {N<:Tuple,𝔽} size = get_parameter(M.manifold.size) - return print(io, "TranslationGroup($(join(size, ", ")); field = $(𝔽))") + return print(io, "TranslationGroup($(join(size, ", ")); field=$(𝔽), parameter=:field)") end function Base.show(io::IO, M::TranslationGroup{N,𝔽}) where {N<:TypeParameter,𝔽} size = get_parameter(M.manifold.size) - return print( - io, - "TranslationGroup($(join(size, ", ")); field = $(𝔽), parameter = :type)", - ) + return print(io, "TranslationGroup($(join(size, ", ")); field=$(𝔽))") end diff --git a/src/groups/unitary.jl b/src/groups/unitary.jl index 96f20099a0..679ed39611 100644 --- a/src/groups/unitary.jl +++ b/src/groups/unitary.jl @@ -26,7 +26,7 @@ See also [`Orthogonal(n)`](@ref) for the real-valued case. """ const Unitary{n,𝔽} = GeneralUnitaryMultiplicationGroup{n,𝔽,AbsoluteDeterminantOneMatrices} -function Unitary(n, 𝔽::AbstractNumbers=ℂ; parameter::Symbol=:field) +function Unitary(n, 𝔽::AbstractNumbers=ℂ; parameter::Symbol=:type) return GeneralUnitaryMultiplicationGroup(UnitaryMatrices(n, 𝔽; parameter=parameter)) end @@ -100,16 +100,16 @@ end Base.inv(::Unitary, p) = adjoint(p) function Base.show(io::IO, ::Unitary{TypeParameter{Tuple{n}},ℂ}) where {n} - return print(io, "Unitary($(n); parameter=:type)") + return print(io, "Unitary($(n))") end function Base.show(io::IO, M::Unitary{Tuple{Int},ℂ}) - n = get_n(M) - return print(io, "Unitary($(n))") + n = get_parameter(M.size)[1] + return print(io, "Unitary($(n); parameter=:field)") end function Base.show(io::IO, ::Unitary{TypeParameter{Tuple{n}},ℍ}) where {n} - return print(io, "Unitary($(n), ℍ; parameter=:type)") + return print(io, "Unitary($(n), ℍ)") end function Base.show(io::IO, M::Unitary{Tuple{Int},ℍ}) - n = get_n(M) - return print(io, "Unitary($(n), ℍ)") + n = get_parameter(M.size)[1] + return print(io, "Unitary($(n), ℍ; parameter=:field)") end diff --git a/src/manifolds/CenteredMatrices.jl b/src/manifolds/CenteredMatrices.jl index 724f952341..d6c53fd160 100644 --- a/src/manifolds/CenteredMatrices.jl +++ b/src/manifolds/CenteredMatrices.jl @@ -8,23 +8,18 @@ The manifold of $m × n$ real-valued or complex-valued matrices whose columns su where $𝔽 ∈ \{ℝ,ℂ\}$. # Constructor - CenteredMatrices(m, n[, field=ℝ]; parameter::Symbol=:field) + CenteredMatrices(m, n[, field=ℝ]; parameter::Symbol=:type) Generate the manifold of `m`-by-`n` (`field`-valued) matrices whose columns sum to zero. `parameter`: whether a type parameter should be used to store `m` and `n`. By default size -is stored in a field. Value can either be `:field` or `:type`. +is stored in type. Value can either be `:field` or `:type`. """ struct CenteredMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T end -function CenteredMatrices( - m::Int, - n::Int, - field::AbstractNumbers=ℝ; - parameter::Symbol=:field, -) +function CenteredMatrices(m::Int, n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (m, n)) return CenteredMatrices{typeof(size),field}(size) end @@ -76,11 +71,11 @@ embed(::CenteredMatrices, p) = p embed(::CenteredMatrices, p, X) = X function get_embedding(::CenteredMatrices{TypeParameter{Tuple{m,n}},𝔽}) where {m,n,𝔽} - return Euclidean(m, n; field=𝔽, parameter=:type) + return Euclidean(m, n; field=𝔽) end function get_embedding(M::CenteredMatrices{Tuple{Int,Int},𝔽}) where {𝔽} m, n = get_mn(M) - return Euclidean(m, n; field=𝔽) + return Euclidean(m, n; field=𝔽, parameter=:field) end get_mn(::CenteredMatrices{TypeParameter{Tuple{m,n}}}) where {m,n} = (m, n) @@ -148,11 +143,11 @@ project!(::CenteredMatrices, Y, p, X) = (Y .= X .- mean(X, dims=1)) representation_size(M::CenteredMatrices) = get_mn(M) function Base.show(io::IO, ::CenteredMatrices{TypeParameter{Tuple{m,n}},𝔽}) where {m,n,𝔽} - return print(io, "CenteredMatrices($(m), $(n), $(𝔽); parameter=:type)") + return print(io, "CenteredMatrices($(m), $(n), $(𝔽))") end function Base.show(io::IO, M::CenteredMatrices{Tuple{Int,Int},𝔽}) where {𝔽} m, n = get_mn(M) - return print(io, "CenteredMatrices($(m), $(n), $(𝔽))") + return print(io, "CenteredMatrices($(m), $(n), $(𝔽); parameter=:field)") end @doc raw""" diff --git a/src/manifolds/CholeskySpace.jl b/src/manifolds/CholeskySpace.jl index 6755ab5c7a..ff3b9af276 100644 --- a/src/manifolds/CholeskySpace.jl +++ b/src/manifolds/CholeskySpace.jl @@ -7,7 +7,7 @@ are for example summarized in Table 1 of [Lin:2019](@cite). # Constructor - CholeskySpace(n; parameter::Symbol=:field) + CholeskySpace(n; parameter::Symbol=:type) Generate the manifold of $n× n$ lower triangular matrices with positive diagonal. """ @@ -15,7 +15,7 @@ struct CholeskySpace{T} <: AbstractManifold{ℝ} size::T end -function CholeskySpace(n::Int; parameter::Symbol=:field) +function CholeskySpace(n::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return CholeskySpace{typeof(size)}(size) end @@ -110,9 +110,6 @@ function exp!(::CholeskySpace, q, p, X) return q end -get_n(::CholeskySpace{TypeParameter{Tuple{N}}}) where {N} = N -get_n(M::CholeskySpace{Tuple{Int}}) = get_parameter(M.size)[1] - @doc raw""" inner(M::CholeskySpace, p, X, Y) @@ -173,7 +170,7 @@ Return the manifold dimension for the [`CholeskySpace`](@ref) `M`, i.e. ```` """ function manifold_dimension(M::CholeskySpace) - N = get_n(M) + N = get_parameter(M.size)[1] return div(N * (N + 1), 2) end @@ -183,16 +180,16 @@ end Return the representation size for the [`CholeskySpace`](@ref)`{N}` `M`, i.e. `(N,N)`. """ function representation_size(M::CholeskySpace) - N = get_n(M) + N = get_parameter(M.size)[1] return (N, N) end function Base.show(io::IO, ::CholeskySpace{TypeParameter{Tuple{n}}}) where {n} - return print(io, "CholeskySpace($(n); parameter=:type)") + return print(io, "CholeskySpace($(n))") end function Base.show(io::IO, M::CholeskySpace{Tuple{Int}}) - n = get_n(M) - return print(io, "CholeskySpace($(n))") + n = get_parameter(M.size)[1] + return print(io, "CholeskySpace($(n); parameter=:field)") end # two small helpers for strictly lower and upper triangulars diff --git a/src/manifolds/Elliptope.jl b/src/manifolds/Elliptope.jl index 6d256a03f4..18b428c3af 100644 --- a/src/manifolds/Elliptope.jl +++ b/src/manifolds/Elliptope.jl @@ -35,12 +35,12 @@ investigated in[JourneeBachAbsilSepulchre:2010](@cite). # Constructor - Elliptope(n::Int, k::Int; parameter::Symbol=:field) + Elliptope(n::Int, k::Int; parameter::Symbol=:type) generates the manifold $\mathcal E(n,k) \subset ℝ^{n × n}$. `parameter`: whether a type parameter should be used to store `n` and `k`. By default size -is stored in a field. Value can either be `:field` or `:type`. +is stored in type. Value can either be `:field` or `:type`. """ struct Elliptope{T} <: AbstractDecoratorManifold{ℝ} size::T @@ -48,7 +48,7 @@ end active_traits(f, ::Elliptope, args...) = merge_traits(IsEmbeddedManifold()) -function Elliptope(n::Int, k::Int; parameter::Symbol=:field) +function Elliptope(n::Int, k::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n, k)) return Elliptope{typeof(size)}(size) end @@ -98,16 +98,13 @@ function check_vector(M::Elliptope, q, Y; kwargs...) end function get_embedding(::Elliptope{TypeParameter{Tuple{n,k}}}) where {n,k} - return Euclidean(n, k; parameter=:type) + return Euclidean(n, k) end function get_embedding(M::Elliptope{Tuple{Int,Int}}) - n, k = get_nk(M) - return Euclidean(n, k) + n, k = get_parameter(M.size) + return Euclidean(n, k; parameter=:field) end -get_nk(::Elliptope{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) -get_nk(M::Elliptope{Tuple{Int,Int}}) = get_parameter(M.size) - """ is_flat(::Elliptope) @@ -125,7 +122,7 @@ returns the dimension of ```` """ function manifold_dimension(M::Elliptope) - N, K = get_nk(M) + N, K = get_parameter(M.size) return N * (K - 1) - div(K * (K - 1), 2) end @@ -172,14 +169,14 @@ Return the size of an array representing an element on the [`Elliptope`](@ref) manifold `M`, i.e. $n × k$, the size of such factor of $p=qq^{\mathrm{T}}$ on $\mathcal M = \mathcal E(n,k)$. """ -representation_size(M::Elliptope) = get_nk(M) +representation_size(M::Elliptope) = get_parameter(M.size) function Base.show(io::IO, ::Elliptope{TypeParameter{Tuple{n,k}}}) where {n,k} - return print(io, "Elliptope($(n), $(k); parameter=:type)") + return print(io, "Elliptope($(n), $(k))") end function Base.show(io::IO, M::Elliptope{Tuple{Int,Int}}) - n, k = get_nk(M) - return print(io, "Elliptope($(n), $(k))") + n, k = get_parameter(M.size) + return print(io, "Elliptope($(n), $(k); parameter=:field)") end """ diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 082c0b97a4..3b78200001 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -21,7 +21,7 @@ The dimension of this space is ``k \dim_ℝ 𝔽``, where ``\dim_ℝ 𝔽`` is t [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of the field ``𝔽``. `parameter`: whether a type parameter should be used to store `n`. By default size -is stored in a field. Value can either be `:field` or `:type`. +is stored in type. Value can either be `:field` or `:type`. Euclidean(; field=ℝ) @@ -35,7 +35,7 @@ end function Euclidean( n::Vararg{Int,I}; field::AbstractNumbers=ℝ, - parameter::Symbol=:field, + parameter::Symbol=:type, ) where {I} size = wrap_type_parameter(parameter, n) return Euclidean{typeof(size),field}(size) @@ -700,11 +700,11 @@ end function Base.show(io::IO, M::Euclidean{N,𝔽}) where {N<:Tuple,𝔽} size = get_parameter(M.size) - return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽))") + return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽), parameter = :field)") end function Base.show(io::IO, M::Euclidean{N,𝔽}) where {N<:TypeParameter,𝔽} size = get_parameter(M.size) - return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽), parameter = :type)") + return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽))") end # # Vector Transport diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 2778fb493d..0e8091c86e 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -45,7 +45,7 @@ function FixedRankMatrices( n::Int, k::Int, field::AbstractNumbers=ℝ; - parameter::Symbol=:field, + parameter::Symbol=:type, ) size = wrap_type_parameter(parameter, (m, n, k)) return FixedRankMatrices{typeof(size),field}(size) @@ -128,7 +128,7 @@ function allocate(X::UMVTVector, ::Type{T}) where {T} end function allocate_result(M::FixedRankMatrices, ::typeof(project), X, p, vals...) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) # vals are p and X, so we can use their fields to set up those of the UMVTVector return UMVTVector(allocate(p.U, m, k), allocate(p.S, k, k), allocate(p.Vt, k, n)) end @@ -198,7 +198,7 @@ shape, i.e. `p.U` and `p.Vt` have to be unitary. The keyword arguments are passe `rank` function that verifies the rank of `p`. """ function check_point(M::FixedRankMatrices, p; kwargs...) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) r = rank(p; kwargs...) s = "The point $(p) does not lie on $(M), " if r > k @@ -207,7 +207,7 @@ function check_point(M::FixedRankMatrices, p; kwargs...) return nothing end function check_point(M::FixedRankMatrices, p::SVDMPoint; kwargs...) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) s = "The point $(p) does not lie on $(M), " if !isapprox(p.U' * p.U, one(zeros(k, k)); kwargs...) return DomainError( @@ -225,7 +225,7 @@ function check_point(M::FixedRankMatrices, p::SVDMPoint; kwargs...) end function check_size(M::FixedRankMatrices, p::SVDMPoint) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) if (size(p.U) != (m, k)) || (length(p.S) != k) || (size(p.Vt) != (k, n)) return DomainError( [size(p.U)..., length(p.S), size(p.Vt)...], @@ -234,7 +234,7 @@ function check_size(M::FixedRankMatrices, p::SVDMPoint) end end function check_size(M::FixedRankMatrices, p) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) pS = svd(p) if (size(pS.U) != (m, k)) || (length(pS.S) != k) || (size(pS.Vt) != (k, n)) return DomainError( @@ -244,7 +244,7 @@ function check_size(M::FixedRankMatrices, p) end end function check_size(M::FixedRankMatrices, p, X::UMVTVector) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) if (size(X.U) != (m, k)) || (size(X.Vt) != (k, n)) || (size(X.M) != (k, k)) return DomainError( cat(size(X.U), size(X.M), size(X.Vt), dims=1), @@ -261,7 +261,7 @@ Check whether the tangent [`UMVTVector`](@ref) `X` is from the tangent space of respectively, and its dimensions are consistent with `p` and `X.M`, i.e. correspond to `m`-by-`n` matrices of rank `k`. """ function check_vector(M::FixedRankMatrices, p::SVDMPoint, X::UMVTVector; kwargs...) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) if !isapprox(X.U' * p.U, zeros(k, k); kwargs...) return DomainError( norm(X.U' * p.U - zeros(k, k)), @@ -351,16 +351,13 @@ function embed!(::FixedRankMatrices, Y, p::SVDMPoint, X::UMVTVector) end function get_embedding(::FixedRankMatrices{TypeParameter{Tuple{m,n,k}},𝔽}) where {m,n,k,𝔽} - return Euclidean(m, n; field=𝔽, parameter=:type) + return Euclidean(m, n; field=𝔽) end function get_embedding(M::FixedRankMatrices{Tuple{Int,Int,Int},𝔽}) where {𝔽} - m, n, k = get_mnk(M) - return Euclidean(m, n; field=𝔽) + m, n, k = get_parameter(M.size) + return Euclidean(m, n; field=𝔽, parameter=:field) end -get_mnk(::FixedRankMatrices{TypeParameter{Tuple{m,n,k}}}) where {m,n,k} = (m, n, k) -get_mnk(M::FixedRankMatrices{Tuple{Int,Int,Int}}) = get_parameter(M.size) - """ injectivity_radius(::FixedRankMatrices) @@ -425,7 +422,7 @@ of dimension `m`x`n` of rank `k`, namely where ``\dim_ℝ 𝔽`` is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ function manifold_dimension(M::FixedRankMatrices{<:Any,𝔽}) where {𝔽} - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) return (m + n - k) * k * real_dimension(𝔽) end @@ -468,7 +465,7 @@ function Random.rand(M::FixedRankMatrices; vector_at=nothing, kwargs...) return rand(Random.default_rng(), M; vector_at=vector_at, kwargs...) end function Random.rand(rng::AbstractRNG, M::FixedRankMatrices; vector_at=nothing, kwargs...) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) if vector_at === nothing p = SVDMPoint( Matrix{Float64}(undef, m, k), @@ -493,7 +490,7 @@ function Random.rand!( vector_at=nothing, kwargs..., ) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) if vector_at === nothing U = rand(rng, Stiefel(m, k); kwargs...) S = sort(rand(rng, k); rev=true) @@ -522,7 +519,7 @@ Return the element size of a point on the [`FixedRankMatrices`](@ref) `M`, i.e. the size of matrices on this manifold ``(m,n)``. """ function representation_size(M::FixedRankMatrices) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) return (m, n) end @@ -546,7 +543,7 @@ function retract_polar!( X::UMVTVector, t::Number, ) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) tX = t * X QU, RU = qr([p.U tX.U]) QV, RV = qr([p.Vt' tX.Vt']) @@ -597,11 +594,11 @@ function Base.show( io::IO, ::FixedRankMatrices{TypeParameter{Tuple{m,n,k}},𝔽}, ) where {m,n,k,𝔽} - return print(io, "FixedRankMatrices($(m), $(n), $(k), $(𝔽); parameter=:type)") + return print(io, "FixedRankMatrices($(m), $(n), $(k), $(𝔽))") end function Base.show(io::IO, M::FixedRankMatrices{Tuple{Int,Int,Int},𝔽}) where {𝔽} - m, n, k = get_mnk(M) - return print(io, "FixedRankMatrices($(m), $(n), $(k), $(𝔽))") + m, n, k = get_parameter(M.size) + return print(io, "FixedRankMatrices($(m), $(n), $(k), $(𝔽); parameter=:field)") end function Base.show(io::IO, ::MIME"text/plain", p::SVDMPoint) pre = " " @@ -657,7 +654,7 @@ Return a [`UMVTVector`](@ref) representing the zero tangent vector in the tangen structure are zero matrices. """ function zero_vector(M::FixedRankMatrices, p::SVDMPoint) - m, n, k = get_mnk(M) + m, n, k = get_parameter(M.size) v = UMVTVector( zeros(eltype(p.U), m, k), zeros(eltype(p.S), k, k), diff --git a/src/manifolds/Flag.jl b/src/manifolds/Flag.jl index afa9971566..542d9bb5b2 100644 --- a/src/manifolds/Flag.jl +++ b/src/manifolds/Flag.jl @@ -56,7 +56,7 @@ Tangent space is represented in the block-skew-symmetric form. # Constructor - Flag(N, n1, n2, ..., nd; parameter::Symbol=:field) + Flag(N, n1, n2, ..., nd; parameter::Symbol=:type) Generate the manifold ``\operatorname{Flag}(n_1, n_2, ..., n_d; N)`` of subspaces ```math @@ -66,14 +66,14 @@ where ``𝕍_i`` for ``i ∈ 1, 2, …, d`` are subspaces of ``ℝ^N`` of dimens ``\operatorname{dim} 𝕍_i = n_i``. `parameter`: whether a type parameter should be used to store `n`. By default size -is stored in a field. Value can either be `:field` or `:type`. +is stored in type. Value can either be `:field` or `:type`. """ struct Flag{T,dp1} <: AbstractDecoratorManifold{ℝ} subspace_dimensions::ZeroTuple{NTuple{dp1,Int}} size::T end -function Flag(N::Int, ns::Vararg{Int,I}; parameter::Symbol=:field) where {I} +function Flag(N::Int, ns::Vararg{Int,I}; parameter::Symbol=:type) where {I} if ns[1] <= 0 error( "First dimension in the sequence ns must be strictly positive, but is $(ns[1]).", @@ -103,15 +103,12 @@ end Get the embedding of the [`Flag`](@ref) manifold `M`, i.e. the [`Stiefel`](@ref) manifold. """ function get_embedding(M::Flag{Tuple{Int},dp1}) where {dp1} - return Stiefel(M.size[1], M.subspace_dimensions[dp1 - 1]) + return Stiefel(M.size[1], M.subspace_dimensions[dp1 - 1]; parameter=:field) end function get_embedding(M::Flag{TypeParameter{Tuple{N}},dp1}) where {N,dp1} - return Stiefel(N, M.subspace_dimensions[dp1 - 1]; parameter=:type) + return Stiefel(N, M.subspace_dimensions[dp1 - 1]) end -get_n(::Flag{TypeParameter{Tuple{N}}}) where {N} = N -get_n(M::Flag{Tuple{Int}}) = get_parameter(M.size)[1] - @doc raw""" injectivity_radius(M::Flag) injectivity_radius(M::Flag, p) @@ -138,7 +135,7 @@ Return dimension of flag manifold ``\operatorname{Flag}(n_1, n_2, ..., n_d; N)`` The formula reads ``\sum_{i=1}^d (n_i-n_{i-1})(N-n_i)``. """ function manifold_dimension(M::Flag{<:Any,dp1}) where {dp1} - N = get_n(M) + N = get_parameter(M.size)[1] dim = 0 for i in 1:(dp1 - 1) dim += @@ -153,15 +150,15 @@ function Base.show(io::IO, M::Flag{TypeParameter{Tuple{N}}}) where {N} for d_i in M.subspace_dimensions.x[1:(end - 1)] print(io, ", $d_i") end - return print(io, "; parameter=:type)") + return print(io, ")") end function Base.show(io::IO, M::Flag{Tuple{Int}}) - N = get_n(M) + N = get_parameter(M.size)[1] print(io, "Flag($(N)") for d_i in M.subspace_dimensions.x[1:(end - 1)] print(io, ", $d_i") end - return print(io, ")") + return print(io, "; parameter=:field)") end """ diff --git a/src/manifolds/FlagOrthogonal.jl b/src/manifolds/FlagOrthogonal.jl index 8795933c73..c6d897b70a 100644 --- a/src/manifolds/FlagOrthogonal.jl +++ b/src/manifolds/FlagOrthogonal.jl @@ -60,9 +60,11 @@ end Get embedding of [`Flag`](@ref) manifold `M`, i.e. the manifold [`OrthogonalMatrices`](@ref). """ function get_embedding(::Flag{TypeParameter{Tuple{N}}}, p::OrthogonalPoint) where {N} - return OrthogonalMatrices(N; parameter=:type) + return OrthogonalMatrices(N) +end +function get_embedding(M::Flag{Tuple{Int}}, p::OrthogonalPoint) + return OrthogonalMatrices(M.size[1]; parameter=:field) end -get_embedding(M::Flag{Tuple{Int}}, p::OrthogonalPoint) = OrthogonalMatrices(M.size[1]) function _extract_flag(M::Flag, p::AbstractMatrix, i::Int) range = (M.subspace_dimensions[i - 1] + 1):M.subspace_dimensions[i] @@ -85,7 +87,7 @@ function project!( ::OrthogonalPoint, X::OrthogonalTVector, ) where {dp1} - N = get_n(M) + N = get_parameter(M.size)[1] project!(SkewHermitianMatrices(N), Y.value, X.value) for i in 1:dp1 Bi = _extract_flag(M, Y.value, i) @@ -112,7 +114,7 @@ X = \begin{bmatrix} where ``B_{i,j} ∈ ℝ^{(n_i - n_{i-1}) × (n_j - n_{j-1})}``, for ``1 ≤ i < j ≤ d+1``. """ function project(M::Flag{<:Any,dp1}, ::OrthogonalPoint, X::OrthogonalTVector) where {dp1} - N = get_n(M) + N = get_parameter(M.size)[1] Y = project(SkewHermitianMatrices(N), X.value) for i in 1:dp1 Bi = _extract_flag(M, Y, i) @@ -128,7 +130,7 @@ function Random.rand!( vector_at=nothing, ) where {dp1} if vector_at === nothing - N = get_n(M) + N = get_parameter(M.size)[1] RN = Rotations(N) rand!(rng, RN, pX.value) else diff --git a/src/manifolds/GeneralUnitaryMatrices.jl b/src/manifolds/GeneralUnitaryMatrices.jl index 8b8a349b15..8f4441befe 100644 --- a/src/manifolds/GeneralUnitaryMatrices.jl +++ b/src/manifolds/GeneralUnitaryMatrices.jl @@ -35,15 +35,12 @@ function GeneralUnitaryMatrices( n::Int, field, matrix_type::Type{<:AbstractMatrixType}; - parameter::Symbol=:field, + parameter::Symbol=:type, ) size = wrap_type_parameter(parameter, (n,)) return GeneralUnitaryMatrices{typeof(size),field,matrix_type}(size) end -get_n(::GeneralUnitaryMatrices{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::GeneralUnitaryMatrices{Tuple{Int}}) = get_parameter(M.size)[1] - function active_traits(f, ::GeneralUnitaryMatrices, args...) return merge_traits(IsEmbeddedManifold(), IsDefaultMetric(EuclideanMetric())) end @@ -104,7 +101,7 @@ function check_point( end function check_size(M::GeneralUnitaryMatrices, p) - n = get_n(M) + n = get_parameter(M.size)[1] m = size(p) if length(m) != 2 return DomainError( @@ -121,7 +118,7 @@ function check_size(M::GeneralUnitaryMatrices, p) return nothing end function check_size(M::GeneralUnitaryMatrices, p, X) - n = get_n(M) + n = get_parameter(M.size)[1] m = size(X) if length(size(X)) != 2 return DomainError( @@ -151,7 +148,7 @@ and orthogonal to `p`. The tolerance for the last test can be set using the `kwargs...`. """ function check_vector(M::GeneralUnitaryMatrices{<:Any,𝔽}, p, X; kwargs...) where {𝔽} - n = get_n(M) + n = get_parameter(M.size)[1] return check_point(SkewHermitianMatrices(n, 𝔽), X; kwargs...) end @@ -404,7 +401,7 @@ function get_coordinates_orthogonal!( X, ::RealNumbers, ) - n = get_n(M) + n = get_parameter(M.size)[1] @assert length(Xⁱ) == manifold_dimension(M) @assert size(X) == (n, n) if n == 2 @@ -438,18 +435,18 @@ function get_coordinates_orthonormal!( end @doc raw""" - get_embedding(M::OrthogonalMatrices{n}) - get_embedding(M::Rotations{n}) - get_embedding(M::UnitaryMatrices{n}) + get_embedding(M::OrthogonalMatrices) + get_embedding(M::Rotations) + get_embedding(M::UnitaryMatrices) Return the embedding, i.e. The ``\mathbb F^{n×n}``, where ``\mathbb F = \mathbb R`` for the first two and ``\mathbb F = \mathbb C`` for the unitary matrices. """ function get_embedding(::GeneralUnitaryMatrices{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return Euclidean(n, n; field=𝔽, parameter=:type) + return Euclidean(n, n; field=𝔽) end function get_embedding(M::GeneralUnitaryMatrices{Tuple{Int},𝔽}) where {𝔽} - n = get_n(M) + n = get_parameter(M.size)[1] return Euclidean(n, n; field=𝔽, parameter=:field) end @@ -470,7 +467,7 @@ function get_vector_orthogonal(M::GeneralUnitaryMatrices{<:Any,ℝ}, p, c, N::Re end function get_vector_orthogonal( - ::GeneralUnitaryMatrices{TypeParameter{2},ℝ}, + ::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, p::SMatrix, Xⁱ, ::RealNumbers, @@ -479,7 +476,7 @@ function get_vector_orthogonal( end function get_vector_orthogonal!( - ::GeneralUnitaryMatrices{TypeParameter{1},ℝ}, + ::GeneralUnitaryMatrices{TypeParameter{Tuple{1}},ℝ}, X, p, Xⁱ, @@ -551,7 +548,7 @@ function get_vector_orthogonal!( Xⁱ, ::RealNumbers, ) - n = get_n(M) + n = get_parameter(M.size)[1] @assert size(X) == (n, n) @assert length(Xⁱ) == manifold_dimension(M) if n == 1 @@ -645,18 +642,18 @@ function injectivity_radius(::GeneralUnitaryMatrices{TypeParameter{Tuple{n}},ℝ return π * sqrt(2.0) end function injectivity_radius(M::GeneralUnitaryMatrices{Tuple{Int},ℝ}) - n = get_n(M) + n = get_parameter(M.size)[1] return n == 1 ? 0.0 : π * sqrt(2.0) end injectivity_radius(::GeneralUnitaryMatrices{TypeParameter{Tuple{1}},ℝ}) = 0.0 # Resolve ambiguity on Rotations and Orthogonal function _injectivity_radius(M::GeneralUnitaryMatrices{<:Any,ℝ}, ::ExponentialRetraction) - n = get_n(M) + n = get_parameter(M.size)[1] return n == 1 ? 0.0 : π * sqrt(2.0) end function _injectivity_radius(M::GeneralUnitaryMatrices{<:Any,ℝ}, ::PolarRetraction) - n = get_n(M) + n = get_parameter(M.size)[1] return n == 1 ? 0.0 : π / sqrt(2.0) end @@ -717,7 +714,7 @@ end function log!(M::GeneralUnitaryMatrices{<:Any,ℝ}, X, p, q) U = transpose(p) * q X .= real(log_safe(U)) - n = get_n(M) + n = get_parameter(M.size)[1] return project!(SkewSymmetricMatrices(n), X, p, X) end function log!(M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, X, p, q) @@ -767,7 +764,7 @@ end function log!(M::GeneralUnitaryMatrices{<:Any,𝔽}, X, p, q) where {𝔽} log_safe!(X, adjoint(p) * q) - n = get_n(M) + n = get_parameter(M.size)[1] project!(SkewHermitianMatrices(n, 𝔽), X, X) return X end @@ -784,7 +781,7 @@ Return the dimension of the manifold orthogonal matrices and of the manifold of ``` """ function manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℝ}) - n = get_n(M) + n = get_parameter(M.size)[1] return div(n * (n - 1), 2) end @doc raw""" @@ -796,7 +793,7 @@ Return the dimension of the manifold of special unitary matrices. ``` """ function manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices}) - n = get_n(M) + n = get_parameter(M.size)[1] return n^2 - 1 end @@ -814,7 +811,7 @@ formula reads [BoyaSudarshanTilma:2003](@cite): ``` """ function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℝ,AbsoluteDeterminantOneMatrices}) - n = get_n(M) + n = get_parameter(M.size)[1] return 2 * manifold_volume(GeneralUnitaryMatrices(n, ℝ, DeterminantOneMatrices)) end @doc raw""" @@ -835,7 +832,7 @@ It differs from the paper by a factor of `sqrt(2)` due to a different choice of normalization. """ function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℝ,DeterminantOneMatrices}) - n = get_n(M) + n = get_parameter(M.size)[1] vol = 1.0 if n % 2 == 0 k = div(n, 2) @@ -866,7 +863,7 @@ formula reads [BoyaSudarshanTilma:2003](@cite) ``` """ function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℂ,AbsoluteDeterminantOneMatrices}) - n = get_n(M) + n = get_parameter(M.size)[1] vol = sqrt(n * 2^(n + 1)) * π^(((n + 1) * n) // 2) kf = 1 for k in 1:(n - 1) @@ -886,7 +883,7 @@ reads [BoyaSudarshanTilma:2003](@cite) ``` """ function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices}) - n = get_n(M) + n = get_parameter(M.size)[1] vol = sqrt(n * 2^(n - 1)) * π^(((n - 1) * (n + 2)) // 2) kf = 1 for k in 1:(n - 1) @@ -951,7 +948,7 @@ and change the representer to use the corresponding Lie algebra, i.e. we compute project(::GeneralUnitaryMatrices, p, X) function project!(M::GeneralUnitaryMatrices{<:Any,𝔽}, Y, p, X) where {𝔽} - n = get_n(M) + n = get_parameter(M.size)[1] project!(SkewHermitianMatrices(n, 𝔽), Y, p \ X) return Y end diff --git a/src/manifolds/GeneralizedGrassmann.jl b/src/manifolds/GeneralizedGrassmann.jl index 5b38027eaf..2fe79dd922 100644 --- a/src/manifolds/GeneralizedGrassmann.jl +++ b/src/manifolds/GeneralizedGrassmann.jl @@ -53,7 +53,7 @@ function GeneralizedGrassmann( k::Int, B::AbstractMatrix=Matrix{Float64}(I, n, n), 𝔽::AbstractNumbers=ℝ; - parameter::Symbol=:field, + parameter::Symbol=:type, ) size = wrap_type_parameter(parameter, (n, k)) return GeneralizedGrassmann{typeof(size),𝔽,typeof(B)}(size, B) @@ -191,17 +191,14 @@ Return true if [`GeneralizedGrassmann`](@ref) `M` is one-dimensional. """ is_flat(M::GeneralizedGrassmann) = manifold_dimension(M) == 1 -function get_embedding(::GeneralizedGrassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} - return GeneralizedStiefel(n, k, M.B, 𝔽; parameter=:type) +function get_embedding(M::GeneralizedGrassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return GeneralizedStiefel(n, k, M.B, 𝔽) end function get_embedding(M::GeneralizedGrassmann{Tuple{Int,Int},𝔽}) where {𝔽} - n, k = get_nk(M) - return GeneralizedStiefel(n, k, M.B, 𝔽) + n, k = get_parameter(M.size) + return GeneralizedStiefel(n, k, M.B, 𝔽; parameter=:field) end -get_nk(::GeneralizedGrassmann{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) -get_nk(M::GeneralizedGrassmann{Tuple{Int,Int}}) = get_parameter(M.size) - @doc raw""" inner(M::GeneralizedGrassmann, p, X, Y) @@ -265,7 +262,7 @@ Return the dimension of the [`GeneralizedGrassmann(n,k,𝔽)`](@ref) manifold `M where $\dim_ℝ 𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ function manifold_dimension(M::GeneralizedGrassmann{<:Any,𝔽}) where {𝔽} - n, k = get_nk(M) + n, k = get_parameter(M.size) return k * (n - k) * real_dimension(𝔽) end @@ -347,7 +344,7 @@ function Random.rand!( vector_at=nothing, σ::Real=one(real(eltype(pX))), ) - n, k = get_nk(M) + n, k = get_parameter(M.size) if vector_at === nothing A = σ * randn(rng, eltype(pX), n, k) project!(M, pX, Matrix(qr(A).Q)) @@ -365,7 +362,7 @@ end Return the represenation size or matrix dimension of a point on the [`GeneralizedGrassmann`](@ref) `M`, i.e. $(n,k)$ for both the real-valued and the complex value case. """ -representation_size(M::GeneralizedGrassmann) = get_nk(M) +representation_size(M::GeneralizedGrassmann) = get_parameter(M.size) @doc raw""" retract(M::GeneralizedGrassmann, p, X, ::PolarRetraction) @@ -391,11 +388,11 @@ function Base.show( io::IO, M::GeneralizedGrassmann{TypeParameter{Tuple{n,k}},𝔽}, ) where {n,k,𝔽} - return print(io, "GeneralizedGrassmann($(n), $(k), $(M.B), $(𝔽); parameter=:type)") + return print(io, "GeneralizedGrassmann($(n), $(k), $(M.B), $(𝔽))") end function Base.show(io::IO, M::GeneralizedGrassmann{Tuple{Int,Int},𝔽}) where {𝔽} - n, k = get_nk(M) - return print(io, "GeneralizedGrassmann($(n), $(k), $(M.B), $(𝔽))") + n, k = get_parameter(M.size) + return print(io, "GeneralizedGrassmann($(n), $(k), $(M.B), $(𝔽); parameter=:field)") end @doc raw""" diff --git a/src/manifolds/GeneralizedStiefel.jl b/src/manifolds/GeneralizedStiefel.jl index 3f66415f2a..5510abe875 100644 --- a/src/manifolds/GeneralizedStiefel.jl +++ b/src/manifolds/GeneralizedStiefel.jl @@ -45,7 +45,7 @@ function GeneralizedStiefel( k::Int, B::AbstractMatrix=Matrix{Float64}(I, n, n), 𝔽::AbstractNumbers=ℝ; - parameter::Symbol=:field, + parameter::Symbol=:type, ) size = wrap_type_parameter(parameter, (n, k)) return GeneralizedStiefel{typeof(size),𝔽,typeof(B)}(size, B) @@ -100,16 +100,13 @@ function check_vector(M::GeneralizedStiefel, p, X; kwargs...) end function get_embedding(::GeneralizedStiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} - return Euclidean(n, k; field=𝔽, parameter=:type) + return Euclidean(n, k; field=𝔽) end function get_embedding(M::GeneralizedStiefel{Tuple{Int,Int},𝔽}) where {𝔽} - n, k = get_nk(M) - return Euclidean(n, k; field=𝔽) + n, k = get_parameter(M.size) + return Euclidean(n, k; field=𝔽, parameter=:field) end -get_nk(::GeneralizedStiefel{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) -get_nk(M::GeneralizedStiefel{Tuple{Int,Int}}) = get_parameter(M.size) - @doc raw""" inner(M::GeneralizedStiefel, p, X, Y) @@ -146,15 +143,15 @@ The dimension is given by ```` """ function manifold_dimension(M::GeneralizedStiefel{<:Any,ℝ}) - n, k = get_nk(M) + n, k = get_parameter(M.size) return n * k - div(k * (k + 1), 2) end function manifold_dimension(M::GeneralizedStiefel{<:Any,ℂ}) - n, k = get_nk(M) + n, k = get_parameter(M.size) return 2 * n * k - k * k end function manifold_dimension(M::GeneralizedStiefel{<:Any,ℍ}) - n, k = get_nk(M) + n, k = get_parameter(M.size) return 4 * n * k - k * (2k - 1) end @@ -218,7 +215,7 @@ function Random.rand!( vector_at=nothing, σ::Real=one(real(eltype(pX))), ) - n, k = get_nk(M) + n, k = get_parameter(M.size) if vector_at === nothing A = σ * randn(rng, eltype(pX), n, k) project!(M, pX, Matrix(qr(A).Q)) @@ -258,9 +255,9 @@ function retract_project!(M::GeneralizedStiefel, q, p, X, t::Number) end function Base.show(io::IO, M::GeneralizedStiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} - return print(io, "GeneralizedStiefel($(n), $(k), $(M.B), $(𝔽); parameter=:type)") + return print(io, "GeneralizedStiefel($(n), $(k), $(M.B), $(𝔽))") end function Base.show(io::IO, M::GeneralizedStiefel{Tuple{Int,Int},𝔽}) where {𝔽} - n, k = get_nk(M) - return print(io, "GeneralizedStiefel($(n), $(k), $(M.B), $(𝔽))") + n, k = get_parameter(M.size) + return print(io, "GeneralizedStiefel($(n), $(k), $(M.B), $(𝔽); parameter=:field)") end diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index f989907758..5ef3e7b214 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -65,7 +65,7 @@ A good overview can be found in[BendokatZimmermannAbsil:2020](@cite). # Constructor - Grassmann(n, k, field=ℝ, parameter::Symbol=:field) + Grassmann(n, k, field=ℝ, parameter::Symbol=:type) Generate the Grassmann manifold $\operatorname{Gr}(n,k)$, where the real-valued case `field = ℝ` is the default. @@ -77,7 +77,7 @@ end # # Generic functions independent of the representation of points # -function Grassmann(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) +function Grassmann(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n, k)) return Grassmann{typeof(size),field}(size) end @@ -116,9 +116,6 @@ function change_metric!(::Grassmann, Y, ::EuclideanMetric, p, X) return Y end -get_nk(::Grassmann{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) -get_nk(M::Grassmann{Tuple{Int,Int}}) = get_parameter(M.size) - @doc raw""" injectivity_radius(M::Grassmann) injectivity_radius(M::Grassmann, p) @@ -156,7 +153,7 @@ Return the dimension of the [`Grassmann(n,k,𝔽)`](@ref) manifold `M`, i.e. where $\dim_ℝ 𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ function manifold_dimension(M::Grassmann{<:Any,𝔽}) where {𝔽} - n, k = get_nk(M) + n, k = get_parameter(M.size) return k * (n - k) * real_dimension(𝔽) end @@ -179,7 +176,7 @@ function default_estimation_method(::Grassmann, ::typeof(mean)) end function get_orbit_action(M::Grassmann{<:Any,ℝ}) - n, k = get_nk(M) + n, k = get_parameter(M.size) return RowwiseMultiplicationAction(M, Orthogonal(k)) end @@ -190,11 +187,11 @@ Return the total space of the [`Grassmann`](@ref) manifold, which is the corresp independent of whether the points are represented already in the total space or as [`ProjectorPoint`](@ref)s. """ function get_total_space(::Grassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} - return Stiefel(n, k, 𝔽; parameter=:type) + return Stiefel(n, k, 𝔽) end function get_total_space(M::Grassmann{Tuple{Int,Int},𝔽}) where {𝔽} - n, k = get_nk(M) - return Stiefel(n, k, 𝔽) + n, k = get_parameter(M.size) + return Stiefel(n, k, 𝔽; parameter=:field) end # diff --git a/src/manifolds/GrassmannProjector.jl b/src/manifolds/GrassmannProjector.jl index 47071d2ea7..459cb718ba 100644 --- a/src/manifolds/GrassmannProjector.jl +++ b/src/manifolds/GrassmannProjector.jl @@ -28,7 +28,7 @@ i.e. the [`ProjectorPoint`](@ref) ``p ∈ \mathbb F^{n×n}``, ``\mathbb F ∈ \{ has to fulfill ``p^{\mathrm{T}} = p``, ``p^2=p``, and ``\operatorname{rank} p = k`. """ function check_point(M::Grassmann, p::ProjectorPoint; kwargs...) - n, k = get_nk(M) + n, k = get_parameter(M.size) c = p.value * p.value if !isapprox(c, p.value; kwargs...) return DomainError( @@ -106,11 +106,11 @@ function get_embedding( ::Grassmann{TypeParameter{Tuple{n,k}},𝔽}, ::ProjectorPoint, ) where {n,k,𝔽} - return Euclidean(n, n; field=𝔽, parameter=:type) + return Euclidean(n, n; field=𝔽) end function get_embedding(M::Grassmann{Tuple{Int,Int},𝔽}, ::ProjectorPoint) where {𝔽} - n, k = get_nk(M) - return Euclidean(n, n; field=𝔽, parameter=:type) + n, k = get_parameter(M.size) + return Euclidean(n, n; field=𝔽, parameter=:field) end @doc raw""" @@ -120,7 +120,7 @@ Return the represenation size or matrix dimension of a point on the [`Grassmann` `M` when using [`ProjectorPoint`](@ref)s, i.e. ``(n,n)``. """ function representation_size(M::Grassmann, p::ProjectorPoint) - n, k = get_nk(M) + n, k = get_parameter(M.size) return (n, n) end @@ -142,7 +142,7 @@ function canonical_project!(M::Grassmann, q::ProjectorPoint, p::StiefelPoint) return canonical_project!(M, q, p.value) end function allocate_result(M::Grassmann, ::typeof(canonical_project), p::StiefelPoint) - n, k = get_nk(M) + n, k = get_parameter(M.size) return ProjectorPoint(allocate(p.value, (n, n))) end @@ -176,11 +176,11 @@ function allocate_result( p::StiefelPoint, X::StiefelTVector, ) - n, k = get_nk(M) + n, k = get_parameter(M.size) return ProjectorTVector(allocate(p.value, (n, n))) end function allocate_result(M::Grassmann, ::typeof(differential_canonical_project), p, X) - n, k = get_nk(M) + n, k = get_parameter(M.size) return ProjectorTVector(allocate(p, (n, n))) end diff --git a/src/manifolds/GrassmannStiefel.jl b/src/manifolds/GrassmannStiefel.jl index 035ad99cd3..5fa2382b61 100644 --- a/src/manifolds/GrassmannStiefel.jl +++ b/src/manifolds/GrassmannStiefel.jl @@ -94,11 +94,11 @@ function exp!(M::Grassmann, q, p, X) end function get_embedding(::Grassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} - return Stiefel(n, k, 𝔽; parameter=:type) + return Stiefel(n, k, 𝔽) end function get_embedding(M::Grassmann{Tuple{Int,Int},𝔽}) where {𝔽} - n, k = get_nk(M) - return Stiefel(n, k, 𝔽) + n, k = get_parameter(M.size) + return Stiefel(n, k, 𝔽; parameter=:field) end @doc raw""" @@ -238,7 +238,7 @@ function Random.rand!( vector_at=nothing, ) where {𝔽} if vector_at === nothing - n, k = get_nk(M) + n, k = get_parameter(M.size) V = σ * randn(rng, 𝔽 === ℝ ? Float64 : ComplexF64, (n, k)) pX .= qr(V).Q[:, 1:k] else @@ -255,7 +255,7 @@ end Return the representation size or matrix dimension of a point on the [`Grassmann`](@ref) `M`, i.e. $(n,k)$ for both the real-valued and the complex value case. """ -representation_size(M::Grassmann) = get_nk(M) +representation_size(M::Grassmann) = get_parameter(M.size) @doc raw""" retract(M::Grassmann, p, X, ::PolarRetraction) @@ -343,11 +343,11 @@ function riemann_tensor!(::Grassmann{<:Any,ℝ}, Xresult, p, X, Y, Z) end function Base.show(io::IO, ::Grassmann{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} - return print(io, "Grassmann($(n), $(k), $(𝔽); parameter=:type)") + return print(io, "Grassmann($(n), $(k), $(𝔽))") end function Base.show(io::IO, M::Grassmann{Tuple{Int,Int},𝔽}) where {𝔽} - n, k = get_nk(M) - return print(io, "Grassmann($(n), $(k), $(𝔽))") + n, k = get_parameter(M.size) + return print(io, "Grassmann($(n), $(k), $(𝔽); parameter=:field)") end Base.show(io::IO, p::StiefelPoint) = print(io, "StiefelPoint($(p.value))") Base.show(io::IO, X::StiefelTVector) = print(io, "StiefelTVector($(X.value))") @@ -363,7 +363,7 @@ The implementation is based on Section 2.5.1 in [Chikuse:2003](@cite); see also Theorem 2.2.2(iii) in [Chikuse:2003](@cite). """ function uniform_distribution(M::Grassmann{<:Any,ℝ}, p) - n, k = get_nk(M) + n, k = get_parameter(M.size) μ = Distributions.Zeros(n, k) σ = one(eltype(p)) Σ1 = Distributions.PDMats.ScalMat(n, σ) diff --git a/src/manifolds/Hyperbolic.jl b/src/manifolds/Hyperbolic.jl index bf40bee973..a151a4d2e4 100644 --- a/src/manifolds/Hyperbolic.jl +++ b/src/manifolds/Hyperbolic.jl @@ -29,7 +29,7 @@ and the Poincaré half space model, see [`PoincareHalfSpacePoint`](@ref) and [`P # Constructor - Hyperbolic(n::Int; parameter::Symbol=:field) + Hyperbolic(n::Int; parameter::Symbol=:type) Generate the Hyperbolic manifold of dimension `n`. """ @@ -37,7 +37,7 @@ struct Hyperbolic{T} <: AbstractDecoratorManifold{ℝ} size::T end -function Hyperbolic(n::Int; parameter::Symbol=:field) +function Hyperbolic(n::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return Hyperbolic{typeof(size)}(size) end @@ -176,7 +176,7 @@ function check_vector( X::Union{PoincareBallTVector,PoincareHalfSpaceTVector}; kwargs..., ) - n = get_n(M) + n = get_parameter(M.size)[1] return check_point(Euclidean(n), X.value; kwargs...) end @@ -202,16 +202,13 @@ function diagonalizing_projectors(M::Hyperbolic, p, X) end function get_embedding(::Hyperbolic{TypeParameter{Tuple{n}}}) where {n} - return Lorentz(n + 1, MinkowskiMetric(); parameter=:type) + return Lorentz(n + 1, MinkowskiMetric()) end function get_embedding(M::Hyperbolic{Tuple{Int}}) - n = get_n(M) - return Lorentz(n + 1, MinkowskiMetric()) + n = get_parameter(M.size)[1] + return Lorentz(n + 1, MinkowskiMetric(); parameter=:field) end -get_n(::Hyperbolic{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::Hyperbolic{Tuple{Int}}) = get_parameter(M.size)[1] - embed(::Hyperbolic, p::AbstractArray) = p embed(::Hyperbolic, p::AbstractArray, X::AbstractArray) = X @@ -312,7 +309,7 @@ end Return the dimension of the hyperbolic space manifold $\mathcal H^n$, i.e. $\dim(\mathcal H^n) = n$. """ -manifold_dimension(M::Hyperbolic) = get_n(M) +manifold_dimension(M::Hyperbolic) = get_parameter(M.size)[1] @doc raw""" manifold_dimension(M::Hyperbolic) @@ -358,11 +355,11 @@ the [`Lorentz`](@ref)ian manifold. project(::Hyperbolic, ::Any, ::Any) function Base.show(io::IO, ::Hyperbolic{TypeParameter{Tuple{n}}}) where {n} - return print(io, "Hyperbolic($(n); parameter=:type)") + return print(io, "Hyperbolic($(n))") end function Base.show(io::IO, M::Hyperbolic{Tuple{Int}}) - n = get_n(M) - return print(io, "Hyperbolic($(n))") + n = get_parameter(M.size)[1] + return print(io, "Hyperbolic($(n); parameter=:field)") end for T in _HyperbolicTypes @eval Base.show(io::IO, p::$T) = print(io, "$($T)($(p.value))") diff --git a/src/manifolds/HyperbolicHyperboloid.jl b/src/manifolds/HyperbolicHyperboloid.jl index e465bf35bc..f0b15489b8 100644 --- a/src/manifolds/HyperbolicHyperboloid.jl +++ b/src/manifolds/HyperbolicHyperboloid.jl @@ -263,7 +263,7 @@ function _get_basis( end function get_basis_orthonormal(M::Hyperbolic, p, r::RealNumbers) - n = get_n(M) + n = get_parameter(M.size)[1] V = [ _hyperbolize(M, p, [i == k ? one(eltype(p)) : zero(eltype(p)) for k in 1:n]) for i in 1:n @@ -415,7 +415,7 @@ function Random.rand!( vector_at=nothing, σ::Real=one(eltype(pX)), ) - N = get_n(M) + N = get_parameter(M.size)[1] if vector_at === nothing a = randn(rng, N) f = 1 + σ * abs(randn(rng)) diff --git a/src/manifolds/HyperbolicPoincareBall.jl b/src/manifolds/HyperbolicPoincareBall.jl index 624ff1a7d1..81726e55da 100644 --- a/src/manifolds/HyperbolicPoincareBall.jl +++ b/src/manifolds/HyperbolicPoincareBall.jl @@ -53,7 +53,7 @@ function check_point(M::Hyperbolic, p::PoincareBallPoint; kwargs...) end function check_size(M::Hyperbolic, p::PoincareBallPoint) - N = get_n(M) + N = get_parameter(M.size)[1] if size(p.value, 1) != N !(norm(p.value) < 1) return DomainError( @@ -64,7 +64,7 @@ function check_size(M::Hyperbolic, p::PoincareBallPoint) end function check_size(M::Hyperbolic, p::PoincareBallPoint, X::PoincareBallTVector; kwargs...) - N = get_n(M) + N = get_parameter(M.size)[1] if size(X.value, 1) != N return DomainError( size(X.value, 1), @@ -272,11 +272,11 @@ embed(::Hyperbolic, p::PoincareBallPoint, X::PoincareBallTVector) = X.value embed!(::Hyperbolic, Y, p::PoincareBallPoint, X::PoincareBallTVector) = copyto!(Y, X.value) function get_embedding(::Hyperbolic{TypeParameter{Tuple{n}}}, ::PoincareBallPoint) where {n} - return Euclidean(n; parameter=:type) + return Euclidean(n) end function get_embedding(M::Hyperbolic{Tuple{Int}}, ::PoincareBallPoint) - n = get_n(M) - return Euclidean(n) + n = get_parameter(M.size)[1] + return Euclidean(n; parameter=:field) end @doc raw""" diff --git a/src/manifolds/HyperbolicPoincareHalfspace.jl b/src/manifolds/HyperbolicPoincareHalfspace.jl index 64ad2ac394..0bc0c14457 100644 --- a/src/manifolds/HyperbolicPoincareHalfspace.jl +++ b/src/manifolds/HyperbolicPoincareHalfspace.jl @@ -8,7 +8,7 @@ function check_point(M::Hyperbolic, p::PoincareHalfSpacePoint; kwargs...) end function check_size(M::Hyperbolic, p::PoincareHalfSpacePoint) - N = get_n(M) + N = get_parameter(M.size)[1] if size(p.value, 1) != N !(norm(p.value) < 1) return DomainError( @@ -24,7 +24,7 @@ function check_size( X::PoincareHalfSpaceTVector; kwargs..., ) - N = get_n(M) + N = get_parameter(M.size)[1] if size(X.value, 1) != N return DomainError( size(X.value, 1), @@ -217,11 +217,11 @@ function get_embedding( ::Hyperbolic{TypeParameter{Tuple{n}}}, ::PoincareHalfSpacePoint, ) where {n} - return Euclidean(n; parameter=:type) + return Euclidean(n) end function get_embedding(M::Hyperbolic{Tuple{Int}}, ::PoincareHalfSpacePoint) - n = get_n(M) - return Euclidean(n) + n = get_parameter(M.size)[1] + return Euclidean(n; parameter=:field) end @doc raw""" diff --git a/src/manifolds/KendallsPreShapeSpace.jl b/src/manifolds/KendallsPreShapeSpace.jl index 28127dfded..fe8de11a2d 100644 --- a/src/manifolds/KendallsPreShapeSpace.jl +++ b/src/manifolds/KendallsPreShapeSpace.jl @@ -11,7 +11,7 @@ translation and scaling of all points, so this can be thought of as a quotient m # Constructor - KendallsPreShapeSpace(n::Int, k::Int; parameter::Symbol=:field) + KendallsPreShapeSpace(n::Int, k::Int; parameter::Symbol=:type) # See also [`KendallsShapeSpace`](@ref), esp. for the references @@ -20,7 +20,7 @@ struct KendallsPreShapeSpace{T} <: AbstractSphere{ℝ} size::T end -function KendallsPreShapeSpace(n::Int, k::Int; parameter::Symbol=:field) +function KendallsPreShapeSpace(n::Int, k::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n, k)) return KendallsPreShapeSpace{typeof(size)}(size) end @@ -29,7 +29,7 @@ function active_traits(f, ::KendallsPreShapeSpace, args...) return merge_traits(IsEmbeddedSubmanifold()) end -representation_size(M::KendallsPreShapeSpace) = get_nk(M) +representation_size(M::KendallsPreShapeSpace) = get_parameter(M.size) """ check_point(M::KendallsPreShapeSpace, p; atol=sqrt(max_eps(X, Y)), kwargs...) @@ -79,16 +79,13 @@ of matrices of the same shape. get_embedding(::KendallsPreShapeSpace) function get_embedding(::KendallsPreShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} - return ArraySphere(n, k, parameter=:type) + return ArraySphere(n, k) end function get_embedding(M::KendallsPreShapeSpace{Tuple{Int,Int}}) - n, k = get_nk(M) - return ArraySphere(n, k) + n, k = get_parameter(M.size) + return ArraySphere(n, k; parameter=:field) end -get_nk(::KendallsPreShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) -get_nk(M::KendallsPreShapeSpace{Tuple{Int,Int}}) = get_parameter(M.size) - @doc raw""" manifold_dimension(M::KendallsPreShapeSpace) @@ -96,7 +93,7 @@ Return the dimension of the [`KendallsPreShapeSpace`](@ref) manifold `M`. The di given by ``n(k - 1) - 1``. """ function manifold_dimension(M::KendallsPreShapeSpace) - n, k = get_nk(M) + n, k = get_parameter(M.size) return n * (k - 1) - 1 end diff --git a/src/manifolds/KendallsShapeSpace.jl b/src/manifolds/KendallsShapeSpace.jl index 50f6213992..ff023efba6 100644 --- a/src/manifolds/KendallsShapeSpace.jl +++ b/src/manifolds/KendallsShapeSpace.jl @@ -12,7 +12,7 @@ This manifold possesses the [`IsQuotientManifold`](@ref) trait. # Constructor - KendallsShapeSpace(n::Int, k::Int; parameter::Symbol=:field) + KendallsShapeSpace(n::Int, k::Int; parameter::Symbol=:type) # References """ @@ -20,7 +20,7 @@ struct KendallsShapeSpace{T} <: AbstractDecoratorManifold{ℝ} size::T end -function KendallsShapeSpace(n::Int, k::Int; parameter::Symbol=:field) +function KendallsShapeSpace(n::Int, k::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n, k)) return KendallsShapeSpace{typeof(size)}(size) end @@ -30,11 +30,11 @@ function active_traits(f, ::KendallsShapeSpace, args...) end function get_orbit_action(M::KendallsShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} - return ColumnwiseMultiplicationAction(M, SpecialOrthogonal(n; parameter=:type)) + return ColumnwiseMultiplicationAction(M, SpecialOrthogonal(n)) end function get_orbit_action(M::KendallsShapeSpace{Tuple{Int,Int}}) - n, k = get_nk(M) - return ColumnwiseMultiplicationAction(M, SpecialOrthogonal(n)) + n, k = get_parameter(M.size) + return ColumnwiseMultiplicationAction(M, SpecialOrthogonal(n; parameter=:field)) end @doc raw""" @@ -45,11 +45,11 @@ Return the total space of the [`KendallsShapeSpace`](@ref) manifold, which is th """ get_total_space(::KendallsShapeSpace) function get_total_space(::KendallsShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} - return KendallsPreShapeSpace(n, k, parameter=:type) + return KendallsPreShapeSpace(n, k) end function get_total_space(M::KendallsShapeSpace{Tuple{Int,Int}}) - n, k = get_nk(M) - return KendallsPreShapeSpace(n, k) + n, k = get_parameter(M.size) + return KendallsPreShapeSpace(n, k; parameter=:field) end function distance(M::KendallsShapeSpace, p, q) @@ -85,16 +85,13 @@ Get the manifold in which [`KendallsShapeSpace`](@ref) `M` is embedded, i.e. get_embedding(::KendallsShapeSpace) function get_embedding(::KendallsShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} - return KendallsPreShapeSpace(n, k, parameter=:type) + return KendallsPreShapeSpace(n, k) end function get_embedding(M::KendallsShapeSpace{Tuple{Int,Int}}) - n, k = get_nk(M) - return KendallsPreShapeSpace(n, k) + n, k = get_parameter(M.size) + return KendallsPreShapeSpace(n, k; parameter=:field) end -get_nk(::KendallsShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) -get_nk(M::KendallsShapeSpace{Tuple{Int,Int}}) = get_parameter(M.size) - """ horizontal_component(::KendallsShapeSpace, p, X) @@ -155,7 +152,7 @@ given by ``n(k - 1) - 1 - n(n - 1)/2`` in the typical case where ``k \geq n+1``, is 0. See [Kendall:1984](@cite) for a discussion of the over-dimensioned case. """ function manifold_dimension(M::KendallsShapeSpace) - n, k = get_nk(M) + n, k = get_parameter(M.size) if k < n + 1 # over-dimensioned case if k == 1 return 0 diff --git a/src/manifolds/Lorentz.jl b/src/manifolds/Lorentz.jl index 95d1d78bb1..438eaa068f 100644 --- a/src/manifolds/Lorentz.jl +++ b/src/manifolds/Lorentz.jl @@ -29,7 +29,7 @@ which is by default set to the [`MinkowskiMetric`](@ref). """ const Lorentz = MetricManifold{ℝ,Euclidean{T,ℝ},<:LorentzMetric} where {T} -function Lorentz(n::Int, m::LorentzMetric=MinkowskiMetric(); parameter::Symbol=:field) +function Lorentz(n::Int, m::LorentzMetric=MinkowskiMetric(); parameter::Symbol=:type) E = Euclidean(n; parameter=parameter) return Lorentz(E, m) end @@ -37,8 +37,8 @@ function Lorentz(E::Euclidean{T}, m::LorentzMetric=MinkowskiMetric()) where {T} return Lorentz{T,typeof(m)}(E, m) end -function local_metric(M::Lorentz{Tuple{Int},MinkowskiMetric}, p) - n = M.manifold.size[1] +function local_metric(M::Lorentz{<:Any,MinkowskiMetric}, p) + n = get_parameter(M.manifold.size)[1] return Diagonal([ones(n - 1)..., -1]) end @@ -46,7 +46,7 @@ function inner(::Lorentz{<:Any,MinkowskiMetric}, p, X, Y) return minkowski_metric(X, Y) end @doc raw""" - minkowski_metric(a,b) + minkowski_metric(a, b) Compute the minkowski metric on $\mathbb R^n$ is given by ````math diff --git a/src/manifolds/Multinomial.jl b/src/manifolds/Multinomial.jl index d7468ba52b..e01f2b763d 100644 --- a/src/manifolds/Multinomial.jl +++ b/src/manifolds/Multinomial.jl @@ -18,13 +18,13 @@ The [`ProbabilitySimplex`](@ref) is stored internally within `M.manifold`, such # Constructor - MultinomialMatrices(n::Int, m::Int; parameter::Symbol=:field) + MultinomialMatrices(n::Int, m::Int; parameter::Symbol=:type) Generate the manifold of matrices $\mathbb R^{n×m}$ such that the $m$ columns are discrete probability distributions, i.e. sum up to one. `parameter`: whether a type parameter should be used to store `n` and `m`. By default size -is stored in a field. Value can either be `:field` or `:type`. +is stored in type. Value can either be `:field` or `:type`. """ struct MultinomialMatrices{T,TPM<:ProbabilitySimplex} <: AbstractPowerManifold{ℝ,TPM,ArrayPowerRepresentation} @@ -32,18 +32,18 @@ struct MultinomialMatrices{T,TPM<:ProbabilitySimplex} <: manifold::TPM end -function MultinomialMatrices(n::Int, m::Int; parameter::Symbol=:field) +function MultinomialMatrices(n::Int, m::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n, m)) MPS = ProbabilitySimplex(n - 1; parameter=parameter) return MultinomialMatrices{typeof(size),typeof(MPS)}(size, MPS) end function Base.:^(::ProbabilitySimplex{TypeParameter{Tuple{N}}}, m::Int) where {N} - return MultinomialMatrices(N, m; parameter=:type) + return MultinomialMatrices(N + 1, m) end function Base.:^(M::ProbabilitySimplex{Tuple{Int}}, m::Int) - n = get_n(M) - return MultinomialMatrices(n + 1, m) + n = get_parameter(M.size)[1] + return MultinomialMatrices(n + 1, m; parameter=:field) end @doc raw""" @@ -55,7 +55,7 @@ of `m` discrete probability distributions as columns from $\mathbb R^{n}$, i.e. """ check_point(::MultinomialMatrices, ::Any) function check_point(M::MultinomialMatrices, p; kwargs...) - n, m = get_nm(M) + n, m = get_parameter(M.size) return check_point(PowerManifold(M.manifold, m), p; kwargs...) end @@ -67,33 +67,30 @@ This means, that `p` is valid, that `X` is of correct dimension and columnswise a tangent vector to the columns of `p` on the [`ProbabilitySimplex`](@ref). """ function check_vector(M::MultinomialMatrices, p, X; kwargs...) - n, m = get_nm(M) + n, m = get_parameter(M.size) return check_vector(PowerManifold(M.manifold, m), p, X; kwargs...) end -get_nm(::MultinomialMatrices{TypeParameter{Tuple{n,m}}}) where {n,m} = (n, m) -get_nm(M::MultinomialMatrices{Tuple{Int,Int}}) = get_parameter(M.size) - function get_iterator(M::MultinomialMatrices) - n, m = get_nm(M) + n, m = get_parameter(M.size) return Base.OneTo(m) end function manifold_dimension(M::MultinomialMatrices) - n, m = get_nm(M) + n, m = get_parameter(M.size) return (n - 1) * m end function power_dimensions(M::MultinomialMatrices) - n, m = get_nm(M) + n, m = get_parameter(M.size) return (m,) end -representation_size(M::MultinomialMatrices) = get_nm(M) +representation_size(M::MultinomialMatrices) = get_parameter(M.size) function Base.show(io::IO, ::MultinomialMatrices{TypeParameter{Tuple{n,m}}}) where {n,m} - return print(io, "MultinomialMatrices($(n), $(m); parameter=:type)") + return print(io, "MultinomialMatrices($(n), $(m))") end function Base.show(io::IO, M::MultinomialMatrices{Tuple{Int,Int}}) - n, m = get_nm(M) - return print(io, "MultinomialMatrices($(n), $(m))") + n, m = get_parameter(M.size) + return print(io, "MultinomialMatrices($(n), $(m); parameter=:field)") end diff --git a/src/manifolds/MultinomialDoublyStochastic.jl b/src/manifolds/MultinomialDoublyStochastic.jl index c2b408a0e7..d8dfa1dcac 100644 --- a/src/manifolds/MultinomialDoublyStochastic.jl +++ b/src/manifolds/MultinomialDoublyStochastic.jl @@ -41,7 +41,7 @@ More details can be found in Section III [DouikHassibi:2019](@cite). # Constructor - MultinomialDoubleStochastic(n::Int; parameter::Symbol=:field) + MultinomialDoubleStochastic(n::Int; parameter::Symbol=:type) Generate the manifold of matrices $\mathbb R^{n×n}$ that are doubly stochastic and symmetric. """ @@ -49,7 +49,7 @@ struct MultinomialDoubleStochastic{T} <: AbstractMultinomialDoublyStochastic size::T end -function MultinomialDoubleStochastic(n::Int; parameter::Symbol=:field) +function MultinomialDoubleStochastic(n::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return MultinomialDoubleStochastic{typeof(size)}(size) end @@ -61,7 +61,7 @@ Checks whether `p` is a valid point on the [`MultinomialDoubleStochastic`](@ref) i.e. is a matrix with positive entries whose rows and columns sum to one. """ function check_point(M::MultinomialDoubleStochastic, p; kwargs...) - n = get_n(M) + n = get_parameter(M.size)[1] r = sum(p, dims=2) if !isapprox(norm(r - ones(n, 1)), 0.0; kwargs...) return DomainError( @@ -90,16 +90,13 @@ function check_vector(M::MultinomialDoubleStochastic, p, X; kwargs...) end function get_embedding(::MultinomialDoubleStochastic{TypeParameter{Tuple{n}}}) where {n} - return MultinomialMatrices(n, n; parameter=:type) + return MultinomialMatrices(n, n) end function get_embedding(M::MultinomialDoubleStochastic{Tuple{Int}}) - n = get_n(M) - return MultinomialMatrices(n, n) + n = get_parameter(M.size)[1] + return MultinomialMatrices(n, n; parameter=:field) end -get_n(::MultinomialDoubleStochastic{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::MultinomialDoubleStochastic{Tuple{Int}}) = get_parameter(M.size)[1] - """ is_flat(::MultinomialDoubleStochastic) @@ -117,7 +114,7 @@ namely ```` """ function manifold_dimension(M::MultinomialDoubleStochastic) - n = get_n(M) + n = get_parameter(M.size)[1] return (n - 1)^2 end @@ -143,7 +140,7 @@ where $I_n$ is the $n×n$ unit matrix and $\mathbf{1}_n$ is the vector of length project(::MultinomialDoubleStochastic, ::Any, ::Any) function project!(M::MultinomialDoubleStochastic, X, p, Y) - n = get_n(M) + n = get_parameter(M.size)[1] ζ = [I p; p I] \ [sum(Y, dims=2); sum(Y, dims=1)'] # Formula (25) from 1802.02628 return X .= Y .- (repeat(ζ[1:n], 1, 3) .+ repeat(ζ[(n + 1):end]', 3, 1)) .* p end @@ -194,7 +191,7 @@ function project!( end function representation_size(M::MultinomialDoubleStochastic) - n = get_n(M) + n = get_parameter(M.size)[1] return (n, n) end @@ -212,9 +209,9 @@ function retract_project!(M::MultinomialDoubleStochastic, q, p, X, t::Number) end function Base.show(io::IO, ::MultinomialDoubleStochastic{TypeParameter{Tuple{n}}}) where {n} - return print(io, "MultinomialDoubleStochastic($(n); parameter=:type)") + return print(io, "MultinomialDoubleStochastic($(n))") end function Base.show(io::IO, M::MultinomialDoubleStochastic{Tuple{Int}}) - n = get_n(M) - return print(io, "MultinomialDoubleStochastic($(n))") + n = get_parameter(M.size)[1] + return print(io, "MultinomialDoubleStochastic($(n); parameter=:field)") end diff --git a/src/manifolds/MultinomialSymmetric.jl b/src/manifolds/MultinomialSymmetric.jl index 756a15a774..23b178b368 100644 --- a/src/manifolds/MultinomialSymmetric.jl +++ b/src/manifolds/MultinomialSymmetric.jl @@ -43,7 +43,7 @@ struct MultinomialSymmetric{T} <: AbstractMultinomialDoublyStochastic size::T end -function MultinomialSymmetric(n::Int; parameter::Symbol=:field) +function MultinomialSymmetric(n::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return MultinomialSymmetric{typeof(size)}(size) end @@ -55,7 +55,7 @@ Checks whether `p` is a valid point on the [`MultinomialSymmetric`](@ref)`(m,n)` i.e. is a symmetric matrix with positive entries whose rows sum to one. """ function check_point(M::MultinomialSymmetric, p; kwargs...) - n = get_n(M) + n = get_parameter(M.size)[1] return check_point(SymmetricMatrices(n, ℝ), p) end @doc raw""" @@ -66,7 +66,7 @@ This means, that `p` is valid, that `X` is of correct dimension, symmetric, and along any row. """ function check_vector(M::MultinomialSymmetric, p, X; kwargs...) - n = get_n(M) + n = get_parameter(M.size)[1] return check_vector(SymmetricMatrices(n, ℝ), p, X; kwargs...) end @@ -74,16 +74,13 @@ embed!(::MultinomialSymmetric, q, p) = copyto!(q, p) embed!(::MultinomialSymmetric, Y, ::Any, X) = copyto!(Y, X) function get_embedding(::MultinomialSymmetric{TypeParameter{Tuple{n}}}) where {n} - return MultinomialMatrices(n, n; parameter=:type) + return MultinomialMatrices(n, n) end function get_embedding(M::MultinomialSymmetric{Tuple{Int}}) - n = get_n(M) - return MultinomialMatrices(n, n) + n = get_parameter(M.size)[1] + return MultinomialMatrices(n, n; parameter=:field) end -get_n(::MultinomialSymmetric{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::MultinomialSymmetric{Tuple{Int}}) = get_parameter(M.size)[1] - """ is_flat(::MultinomialSymmetric) @@ -101,7 +98,7 @@ namely ```` """ function manifold_dimension(M::MultinomialSymmetric) - n = get_n(M) + n = get_parameter(M.size)[1] return div(n * (n - 1), 2) end @@ -129,7 +126,7 @@ function project!(::MultinomialSymmetric, X, p, Y) end function representation_size(M::MultinomialSymmetric) - n = get_n(M) + n = get_parameter(M.size)[1] return (n, n) end @@ -147,9 +144,9 @@ function retract_project!(M::MultinomialSymmetric, q, p, X, t::Number) end function Base.show(io::IO, ::MultinomialSymmetric{TypeParameter{Tuple{n}}}) where {n} - return print(io, "MultinomialSymmetric($(n); parameter=:type)") + return print(io, "MultinomialSymmetric($(n))") end function Base.show(io::IO, M::MultinomialSymmetric{Tuple{Int}}) - n = get_n(M) - return print(io, "MultinomialSymmetric($(n))") + n = get_parameter(M.size)[1] + return print(io, "MultinomialSymmetric($(n); parameter=:field)") end diff --git a/src/manifolds/Oblique.jl b/src/manifolds/Oblique.jl index 3871d969fa..9663f92633 100644 --- a/src/manifolds/Oblique.jl +++ b/src/manifolds/Oblique.jl @@ -11,7 +11,7 @@ The [`Sphere`](@ref) is stored internally within `M.manifold`, such that all fun # Constructor - Oblique(n::Int, m::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) + Oblique(n::Int, m::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) Generate the manifold of matrices $\mathbb R^{n × m}$ such that the $m$ columns are unit vectors, i.e. from the [`Sphere`](@ref)`(n-1)`. @@ -21,18 +21,18 @@ struct Oblique{T,𝔽,S} <: AbstractPowerManifold{𝔽,Sphere{S,𝔽},ArrayPower manifold::Sphere{S,𝔽} end -function Oblique(n::Int, m::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) +function Oblique(n::Int, m::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) sphere = Sphere(n - 1, field; parameter=parameter) size = wrap_type_parameter(parameter, (n, m)) return Oblique{typeof(size),field,typeof(sphere).parameters[1]}(size, sphere) end function Base.:^(::Sphere{TypeParameter{Tuple{N}},𝔽}, m::Int) where {N,𝔽} - return Oblique(N + 1, m, 𝔽; parameter=:type) + return Oblique(N + 1, m, 𝔽) end function Base.:^(M::Sphere{Tuple{Int},𝔽}, m::Int) where {𝔽} N = M.size[1] - return Oblique(N + 1, m, 𝔽) + return Oblique(N + 1, m, 𝔽; parameter=:field) end @doc raw""" @@ -44,7 +44,7 @@ of `m` unit columns from $\mathbb R^{n}$, i.e. each column is a point from """ check_point(::Oblique, ::Any) function check_point(M::Oblique, p; kwargs...) - n, m = get_nm(M) + n, m = get_parameter(M.size) return check_point(PowerManifold(M.manifold, m), p; kwargs...) end @doc raw""" @@ -55,22 +55,19 @@ This means, that `p` is valid, that `X` is of correct dimension and columnswise a tangent vector to the columns of `p` on the [`Sphere`](@ref). """ function check_vector(M::Oblique, p, X; kwargs...) - n, m = get_nm(M) + n, m = get_parameter(M.size) return check_vector(PowerManifold(M.manifold, m), p, X; kwargs...) end -get_iterator(M::Oblique) = Base.OneTo(get_nm(M)[2]) - -get_nm(::Oblique{TypeParameter{Tuple{n,m}}}) where {n,m} = (n, m) -get_nm(M::Oblique{Tuple{Int,Int}}) = get_parameter(M.size) +get_iterator(M::Oblique) = Base.OneTo(get_parameter(M.size)[2]) function manifold_dimension(M::Oblique{<:Any,𝔽}) where {𝔽} - n, m = get_nm(M) + n, m = get_parameter(M.size) return (n * real_dimension(𝔽) - 1) * m end -power_dimensions(M::Oblique) = get_nm(M)[2] +power_dimensions(M::Oblique) = get_parameter(M.size)[2] -representation_size(M::Oblique) = get_nm(M) +representation_size(M::Oblique) = get_parameter(M.size) @doc raw""" parallel_transport_to(M::Oblique, p, X, q) @@ -82,9 +79,9 @@ doing a column wise parallel transport on the [`Sphere`](@ref) parallel_transport_to(::Oblique, p, X, q) function Base.show(io::IO, ::Oblique{TypeParameter{Tuple{n,m}},𝔽}) where {n,m,𝔽} - return print(io, "Oblique($(n), $(m); field = $(𝔽), parameter=:type)") + return print(io, "Oblique($(n), $(m); field=$(𝔽))") end function Base.show(io::IO, M::Oblique{Tuple{Int,Int},𝔽}) where {𝔽} - n, m = get_nm(M) - return print(io, "Oblique($(n), $(m); field = $(𝔽))") + n, m = get_parameter(M.size) + return print(io, "Oblique($(n), $(m); field=$(𝔽), parameter=:field)") end diff --git a/src/manifolds/Orthogonal.jl b/src/manifolds/Orthogonal.jl index b5e1b24c0c..b1db00d891 100644 --- a/src/manifolds/Orthogonal.jl +++ b/src/manifolds/Orthogonal.jl @@ -7,7 +7,7 @@ The manifold of (real) orthogonal matrices ``\mathrm{O}(n)``. """ const OrthogonalMatrices{n} = GeneralUnitaryMatrices{n,ℝ,AbsoluteDeterminantOneMatrices} -function OrthogonalMatrices(n::Int; parameter::Symbol=:field) +function OrthogonalMatrices(n::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return OrthogonalMatrices{typeof(size)}(size) end @@ -37,9 +37,9 @@ function Random.rand!( end function Base.show(io::IO, ::OrthogonalMatrices{TypeParameter{Tuple{n}}}) where {n} - return print(io, "OrthogonalMatrices($(n); parameter=:type)") + return print(io, "OrthogonalMatrices($(n))") end function Base.show(io::IO, M::OrthogonalMatrices{Tuple{Int}}) - n = get_n(M) - return print(io, "OrthogonalMatrices($n)") + n = get_parameter(M.size)[1] + return print(io, "OrthogonalMatrices($n; parameter=:field)") end diff --git a/src/manifolds/ProbabilitySimplex.jl b/src/manifolds/ProbabilitySimplex.jl index a235c05075..8a00af96c2 100644 --- a/src/manifolds/ProbabilitySimplex.jl +++ b/src/manifolds/ProbabilitySimplex.jl @@ -38,7 +38,7 @@ struct ProbabilitySimplex{T,boundary} <: AbstractDecoratorManifold{ℝ} size::T end -function ProbabilitySimplex(n::Int; boundary::Symbol=:open, parameter::Symbol=:field) +function ProbabilitySimplex(n::Int; boundary::Symbol=:open, parameter::Symbol=:type) if boundary !== :open && boundary !== :closed throw( ArgumentError( @@ -184,7 +184,7 @@ function exp!(::ProbabilitySimplex, q, p, X) end function get_coordinates_orthonormal!(M::ProbabilitySimplex, Xc, p, X, R::RealNumbers) - n = get_n(M) + n = get_parameter(M.size)[1] get_coordinates_orthonormal!( Sphere(n), Xc, @@ -196,18 +196,15 @@ function get_coordinates_orthonormal!(M::ProbabilitySimplex, Xc, p, X, R::RealNu end function get_embedding(::ProbabilitySimplex{TypeParameter{Tuple{n}}}) where {n} - return Euclidean(n + 1; parameter=:type) + return Euclidean(n + 1) end function get_embedding(M::ProbabilitySimplex{Tuple{Int}}) - n = get_n(M) - return Euclidean(n + 1) + n = get_parameter(M.size)[1] + return Euclidean(n + 1; parameter=:field) end -get_n(::ProbabilitySimplex{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::ProbabilitySimplex{Tuple{Int}}) = get_parameter(M.size)[1] - function get_vector_orthonormal!(M::ProbabilitySimplex, Y, p, Xc, R::RealNumbers) - n = get_n(M) + n = get_parameter(M.size)[1] ps = simplex_to_amplitude(M, p) X = get_vector_orthonormal(Sphere(n), ps, Xc, R) return amplitude_to_simplex_diff!(M, Y, ps, X) @@ -315,7 +312,7 @@ Returns the manifold dimension of the probability simplex in $ℝ^{n+1}$, i.e. \dim_{Δ^n} = n. ```` """ -manifold_dimension(M::ProbabilitySimplex) = get_n(M) +manifold_dimension(M::ProbabilitySimplex) = get_parameter(M.size)[1] @doc raw""" manifold_volume(::ProbabilitySimplex) @@ -325,7 +322,7 @@ Return the volume of the [`ProbabilitySimplex`](@ref), i.e. volume of the `n`-di orthant. """ function manifold_volume(M::ProbabilitySimplex) - n = get_n(M) + n = get_parameter(M.size)[1] return manifold_volume(Sphere(n)) / 2^(n + 1) end @@ -346,7 +343,7 @@ mean(::ProbabilitySimplex, ::Any...) default_estimation_method(::ProbabilitySimplex, ::typeof(mean)) = GeodesicInterpolation() function parallel_transport_to!(M::ProbabilitySimplex, Y, p, X, q) - n = get_n(M) + n = get_parameter(M.size)[1] q_s = simplex_to_amplitude(M, q) Ys = parallel_transport_to( Sphere(n), @@ -442,7 +439,7 @@ Return the representation size of points in the $n$-dimensional probability simp i.e. an array size of `(n+1,)`. """ function representation_size(M::ProbabilitySimplex) - n = get_n(M) + n = get_parameter(M.size)[1] return (n + 1,) end @@ -500,7 +497,7 @@ It is computed using isometry with positive orthant of a sphere. riemann_tensor(::ProbabilitySimplex, p, X, Y, Z) function riemann_tensor!(M::ProbabilitySimplex, Xresult, p, X, Y, Z) - n = get_n(M) + n = get_parameter(M.size)[1] pe = simplex_to_amplitude(M, p) Xrs = riemann_tensor( Sphere(n), @@ -517,11 +514,11 @@ function Base.show( io::IO, ::ProbabilitySimplex{TypeParameter{Tuple{n}},boundary}, ) where {n,boundary} - return print(io, "ProbabilitySimplex($(n); boundary=:$boundary, parameter=:type)") + return print(io, "ProbabilitySimplex($(n); boundary=:$boundary)") end function Base.show(io::IO, M::ProbabilitySimplex{Tuple{Int},boundary}) where {boundary} - n = get_n(M) - return print(io, "ProbabilitySimplex($(n); boundary=:$boundary)") + n = get_parameter(M.size)[1] + return print(io, "ProbabilitySimplex($(n); boundary=:$boundary, parameter=:field)") end @doc raw""" @@ -531,7 +528,7 @@ Compute the volume density at point `p` on [`ProbabilitySimplex`](@ref) `M` for vector `X`. It is computed using isometry with positive orthant of a sphere. """ function volume_density(M::ProbabilitySimplex, p, X) - n = get_n(M) + n = get_parameter(M.size)[1] pe = simplex_to_amplitude(M, p) return volume_density(Sphere(n), pe, simplex_to_amplitude_diff(M, p, X)) end diff --git a/src/manifolds/ProbabilitySimplexEuclideanMetric.jl b/src/manifolds/ProbabilitySimplexEuclideanMetric.jl index 9deb63f70f..9ea96ab8e2 100644 --- a/src/manifolds/ProbabilitySimplexEuclideanMetric.jl +++ b/src/manifolds/ProbabilitySimplexEuclideanMetric.jl @@ -16,7 +16,7 @@ Return the volume of the [`ProbabilitySimplex`](@ref) with the Euclidean metric. The formula reads ``\frac{\sqrt{n+1}}{n!}`` """ function manifold_volume(M::MetricManifold{ℝ,<:ProbabilitySimplex,<:EuclideanMetric}) - n = get_n(M.manifold) + n = get_parameter(M.manifold.size)[1] return sqrt(n + 1) / factorial(n) end diff --git a/src/manifolds/ProjectiveSpace.jl b/src/manifolds/ProjectiveSpace.jl index 4d23e24bef..29b63729af 100644 --- a/src/manifolds/ProjectiveSpace.jl +++ b/src/manifolds/ProjectiveSpace.jl @@ -40,7 +40,7 @@ projective spaces. struct ProjectiveSpace{T,𝔽} <: AbstractProjectiveSpace{𝔽} size::T end -function ProjectiveSpace(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) +function ProjectiveSpace(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return ProjectiveSpace{typeof(size),field}(size) end @@ -91,7 +91,7 @@ end function ArrayProjectiveSpace( n::Vararg{Int,I}; field::AbstractNumbers=ℝ, - parameter::Symbol=:field, + parameter::Symbol=:type, ) where {I} size = wrap_type_parameter(parameter, n) return ArrayProjectiveSpace{typeof(size),field}(size) @@ -177,7 +177,7 @@ function exp!(M::AbstractProjectiveSpace, q, p, X) end function get_basis(M::ProjectiveSpace{<:Any,ℝ}, p, B::DiagonalizingOrthonormalBasis{ℝ}) - n = get_n(M) + n = get_parameter(M.size)[1] return get_basis(Sphere(n), p, B) end @@ -251,9 +251,6 @@ function get_vector_orthonormal!( return Y end -get_n(::ProjectiveSpace{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::ProjectiveSpace{Tuple{Int}}) = get_parameter(M.size)[1] - injectivity_radius(::AbstractProjectiveSpace) = π / 2 injectivity_radius(::AbstractProjectiveSpace, p) = π / 2 injectivity_radius(::AbstractProjectiveSpace, ::AbstractRetractionMethod) = π / 2 @@ -445,7 +442,7 @@ function representation_size(M::ArrayProjectiveSpace) return get_parameter(M.size) end function representation_size(M::ProjectiveSpace) - n = get_n(M) + n = get_parameter(M.size)[1] return (n + 1,) end @@ -486,21 +483,21 @@ function retract_qr!(M::AbstractProjectiveSpace, q, p, X, t::Number) end function Base.show(io::IO, ::ProjectiveSpace{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return print(io, "ProjectiveSpace($(n), $(𝔽); parameter=:type)") + return print(io, "ProjectiveSpace($(n), $(𝔽))") end function Base.show(io::IO, M::ProjectiveSpace{Tuple{Int},𝔽}) where {𝔽} - n = get_n(M) - return print(io, "ProjectiveSpace($(n), $(𝔽))") + n = get_parameter(M.size)[1] + return print(io, "ProjectiveSpace($(n), $(𝔽); parameter=:field)") end -function Base.show(io::IO, ::ArrayProjectiveSpace{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return print( - io, - "ArrayProjectiveSpace($(join(n.parameters, ", ")); field = $(𝔽), parameter=:type)", - ) +function Base.show(io::IO, ::ArrayProjectiveSpace{TypeParameter{tn},𝔽}) where {tn<:Tuple,𝔽} + return print(io, "ArrayProjectiveSpace($(join(tn.parameters, ", ")); field = $(𝔽))") end function Base.show(io::IO, M::ArrayProjectiveSpace{<:Tuple,𝔽}) where {𝔽} n = M.size - return print(io, "ArrayProjectiveSpace($(join(n, ", ")); field = $(𝔽))") + return print( + io, + "ArrayProjectiveSpace($(join(n, ", ")); field = $(𝔽), parameter=:field)", + ) end """ diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 4dee6fb28e..9f786450ed 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -1,18 +1,18 @@ @doc raw""" - Rotations{n} <: AbstractManifold{ℝ} + Rotations{T} <: AbstractManifold{ℝ} The manifold of rotation matrices of size ``n × n``, i.e. real-valued orthogonal matrices with determinant ``+1``. # Constructor - Rotations(n) + Rotations(n::Int; parameter::Symbol=:type) Generate the manifold of ``n × n`` rotation matrices. """ -const Rotations{n} = GeneralUnitaryMatrices{n,ℝ,DeterminantOneMatrices} +const Rotations{T} = GeneralUnitaryMatrices{T,ℝ,DeterminantOneMatrices} -function Rotations(n::Int; parameter::Symbol=:field) +function Rotations(n::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return Rotations{typeof(size)}(size) end @@ -125,7 +125,7 @@ function _ev_zero(tridiagonal_elements, unitary, evec, evals, fill_at; i) end function get_basis_diagonalizing(M::Rotations, p, B::DiagonalizingOrthonormalBasis{ℝ}) - n = get_n(M) + n = get_parameter(M.size)[1] decomp = schur(B.frame_direction) decomp = ordschur(decomp, map(v -> norm(v) > eps(eltype(p)), decomp.values)) @@ -162,7 +162,7 @@ Return the radius of injectivity for the [`PolarRetraction`](https://juliamanifo """ injectivity_radius(::Rotations, ::PolarRetraction) function _injectivity_radius(M::Rotations, ::PolarRetraction) - n = get_n(M) + n = get_parameter(M.size)[1] return n == 1 ? 0.0 : π / sqrt(2.0) end @@ -196,7 +196,7 @@ Compute a vector from the tangent space $T_p\mathrm{SO}(n)$ of the point `p` on inverse_retract(::Rotations, ::Any, ::Any, ::QRInverseRetraction) function inverse_retract_polar!(M::Rotations, X, p, q) - n = get_n(M) + n = get_parameter(M.size)[1] A = transpose(p) * q Amat = A isa StaticMatrix ? A : convert(Matrix, A) H = copyto!(allocate(Amat), -2I) @@ -213,7 +213,7 @@ function inverse_retract_polar!(M::Rotations, X, p, q) return project!(SkewSymmetricMatrices(n), X, p, X) end function inverse_retract_qr!(M::Rotations, X, p, q) - n = get_n(M) + n = get_parameter(M.size)[1] A = transpose(p) * q R = zero(X) for i in 1:n @@ -249,7 +249,7 @@ and second columns are swapped. The argument `p` is used to determine the type of returned points. """ function normal_rotation_distribution(M::Rotations, p, σ::Real) - n = get_n(M) + n = get_parameter(M.size)[1] d = Distributions.MvNormal(zeros(n * n), σ) return NormalRotationDistribution(M, d, p) end @@ -274,7 +274,7 @@ check with `check_det = false`. project(::Rotations, ::Any) function project!(M::Rotations, q, p; check_det::Bool=true) - n = get_n(M) + n = get_parameter(M.size)[1] F = svd(p) mul!(q, F.U, F.Vt) if check_det && det(q) < 0 @@ -290,7 +290,7 @@ function Random.rand( rng::AbstractRNG, d::NormalRotationDistribution{TResult,<:Rotations}, ) where {TResult} - n = get_n(d.manifold) + n = get_parameter(d.manifold.size)[1] return if n == 1 convert(TResult, ones(1, 1)) else @@ -390,11 +390,11 @@ end parallel_transport_to(::Rotations{TypeParameter{Tuple{2}}}, p, X, q) = X function Base.show(io::IO, ::Rotations{TypeParameter{Tuple{n}}}) where {n} - return print(io, "Rotations($(n); parameter=:type)") + return print(io, "Rotations($(n))") end function Base.show(io::IO, M::Rotations{Tuple{Int}}) - n = get_n(M) - return print(io, "Rotations($n)") + n = get_parameter(M.size)[1] + return print(io, "Rotations($n; parameter=:field)") end @doc raw""" @@ -409,7 +409,7 @@ to map it into the Lie algebra. """ riemannian_Hessian(M::Rotations, p, G, H, X) function riemannian_Hessian!(M::Rotations, Y, p, G, H, X) - N = get_n(M) + N = get_parameter(M.size)[1] symmetrize!(Y, G' * p) project!(SkewSymmetricMatrices(N), Y, p' * H - X * Y) return Y diff --git a/src/manifolds/SPDFixedDeterminant.jl b/src/manifolds/SPDFixedDeterminant.jl index 07b65b461c..5efae753c0 100644 --- a/src/manifolds/SPDFixedDeterminant.jl +++ b/src/manifolds/SPDFixedDeterminant.jl @@ -1,5 +1,5 @@ @doc raw""" - SPDFixedDeterminant{N,D} <: AbstractDecoratorManifold{ℝ} + SPDFixedDeterminant{T,D} <: AbstractDecoratorManifold{ℝ} The manifold of symmetric positive definite matrices of fixed determinant ``d > 0``, i.e. @@ -29,18 +29,23 @@ Additionally we store the tangent vectors as `X=p^{-1}Z`, i.e. symmetric matrice # Constructor - SPDFixedDeterminant(n::Int, d::Real=1.0) + SPDFixedDeterminant(n::Int, d::Real=1.0; parameter::Symbol=:type) -generates the manifold $\mathcal P_d(n) \subset \mathcal P(n)$ of determinant ``d``, +Generate the manifold $\mathcal P_d(n) \subset \mathcal P(n)$ of determinant ``d``, which defaults to 1. + +`parameter`: whether a type parameter should be used to store `n`. By default size +is stored in type. Value can either be `:field` or `:type`. """ -struct SPDFixedDeterminant{N,TD<:Real} <: AbstractDecoratorManifold{ℝ} +struct SPDFixedDeterminant{T,TD<:Real} <: AbstractDecoratorManifold{ℝ} + size::T d::TD end -function SPDFixedDeterminant(n::Int, d::F=1.0) where {F<:Real} +function SPDFixedDeterminant(n::Int, d::F=1.0; parameter::Symbol=:type) where {F<:Real} @assert d > 0 "The determinant has to be positive but was provided as $d." - return SPDFixedDeterminant{n,F}(d) + size = wrap_type_parameter(parameter, (n,)) + return SPDFixedDeterminant{typeof(size),F}(size, d) end function active_traits(f, ::SPDFixedDeterminant, args...) @@ -48,7 +53,7 @@ function active_traits(f, ::SPDFixedDeterminant, args...) end @doc raw""" - check_point(M::SPDFixedDeterminant{n}, p; kwargs...) + check_point(M::SPDFixedDeterminant, p; kwargs...) Check whether `p` is a valid manifold point on the [`SPDFixedDeterminant`](@ref)`(n,d)` `M`, i.e. whether `p` is a [`SymmetricPositiveDefinite`](@ref) matrix of size `(n, n)` @@ -57,7 +62,7 @@ with determinant ``\det(p) = ```M.d`. The tolerance for the determinant of `p` can be set using `kwargs...`. """ -function check_point(M::SPDFixedDeterminant{n}, p; kwargs...) where {n} +function check_point(M::SPDFixedDeterminant, p; kwargs...) if det(p) ≉ M.d return DomainError( det(p), @@ -92,9 +97,13 @@ embed(M::SPDFixedDeterminant, p, X) = copy(M, X) embed!(M::SPDFixedDeterminant, q, p) = copyto!(M, q, p) embed!(M::SPDFixedDeterminant, Y, p, X) = copyto!(M, Y, p, X) -function get_embedding(::SPDFixedDeterminant{n}) where {n} +function get_embedding(::SPDFixedDeterminant{TypeParameter{Tuple{n}}}) where {n} return SymmetricPositiveDefinite(n) end +function get_embedding(M::SPDFixedDeterminant{Tuple{Int}}) + n = get_parameter(M.size)[1] + return SymmetricPositiveDefinite(n; parameter=:field) +end @doc raw""" manifold_dimension(M::SPDFixedDeterminant) @@ -112,8 +121,8 @@ function manifold_dimension(M::SPDFixedDeterminant) end @doc raw""" - q = project(M::SPDFixedDeterminant{n}, p) - project!(M::SPDFixedDeterminant{n}, q, p) + q = project(M::SPDFixedDeterminant, p) + project!(M::SPDFixedDeterminant, q, p) Project the symmetric positive definite (s.p.d.) matrix `p` from the embedding onto the (sub-)manifold of s.p.d. matrices of determinant `M.d` (in place of `q`). @@ -126,14 +135,15 @@ q = \Bigl(\frac{d}{\det(p)}\Bigr)^{\frac{1}{n}}p """ project(M::SPDFixedDeterminant, p) -function project!(M::SPDFixedDeterminant{n}, q, p) where {n} +function project!(M::SPDFixedDeterminant, q, p) + n = get_parameter(M.size)[1] q .= (M.d / det(p))^(1 / n) .* p return end @doc raw""" - Y = project(M::SPDFixedDeterminant{n}, p, X) - project!(M::SPDFixedDeterminant{n}, Y, p, X) + Y = project(M::SPDFixedDeterminant, p, X) + project!(M::SPDFixedDeterminant, Y, p, X) Project the symmetric matrix `X` onto the tangent space at `p` of the (sub-)manifold of s.p.d. matrices of determinant `M.d` (in place of `Y`), @@ -148,6 +158,10 @@ function project!(M::SPDFixedDeterminant, Y, p, X) return Y end -function Base.show(io::IO, M::SPDFixedDeterminant{n}) where {n} +function Base.show(io::IO, M::SPDFixedDeterminant{TypeParameter{Tuple{n}}}) where {n} return print(io, "SPDFixedDeterminant($n, $(M.d))") end +function Base.show(io::IO, M::SPDFixedDeterminant{Tuple{Int}}) + n = get_parameter(M.size)[1] + return print(io, "SPDFixedDeterminant($n, $(M.d); parameter=:field)") +end diff --git a/src/manifolds/SkewHermitian.jl b/src/manifolds/SkewHermitian.jl index 7319a84aba..8fdc87c757 100644 --- a/src/manifolds/SkewHermitian.jl +++ b/src/manifolds/SkewHermitian.jl @@ -26,7 +26,7 @@ struct SkewHermitianMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T end -function SkewHermitianMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) +function SkewHermitianMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return SkewHermitianMatrices{typeof(size),field}(size) end @@ -43,7 +43,7 @@ This is equivalent to [`SkewHermitianMatrices(n, ℝ)`](@ref). """ const SkewSymmetricMatrices{T} = SkewHermitianMatrices{T,ℝ} -function SkewSymmetricMatrices(n::Int; parameter::Symbol=:field) +function SkewSymmetricMatrices(n::Int; parameter::Symbol=:type) return SkewHermitianMatrices(n; parameter=parameter) end @@ -97,7 +97,7 @@ function get_basis(M::SkewHermitianMatrices, p, B::DiagonalizingOrthonormalBasis end function get_coordinates_orthonormal!(M::SkewSymmetricMatrices, Y, p, X, ::RealNumbers) - N = get_n(M) + N = get_parameter(M.size)[1] dim = manifold_dimension(M) @assert size(Y) == (dim,) @assert size(X) == (N, N) @@ -116,7 +116,7 @@ function get_coordinates_orthonormal!( X, ::ComplexNumbers, ) - N = get_n(M) + N = get_parameter(M.size)[1] dim = manifold_dimension(M) @assert size(Y) == (dim,) @assert size(X) == (N, N) @@ -136,15 +136,15 @@ function get_coordinates_orthonormal!( end function get_embedding(::SkewHermitianMatrices{TypeParameter{Tuple{N}},𝔽}) where {N,𝔽} - return Euclidean(N, N; field=𝔽, parameter=:type) + return Euclidean(N, N; field=𝔽) end function get_embedding(M::SkewHermitianMatrices{Tuple{Int},𝔽}) where {𝔽} - N = get_n(M) - return Euclidean(N, N; field=𝔽) + N = get_parameter(M.size)[1] + return Euclidean(N, N; field=𝔽, parameter=:field) end function get_vector_orthonormal!(M::SkewSymmetricMatrices, Y, p, X, ::RealNumbers) - N = get_n(M) + N = get_parameter(M.size)[1] dim = manifold_dimension(M) @assert size(X) == (dim,) @assert size(Y) == (N, N) @@ -166,7 +166,7 @@ function get_vector_orthonormal!( X, ::ComplexNumbers, ) - N = get_n(M) + N = get_parameter(M.size)[1] dim = manifold_dimension(M) @assert size(X) == (dim,) @assert size(Y) == (N, N) @@ -184,9 +184,6 @@ function get_vector_orthonormal!( return Y end -get_n(::SkewHermitianMatrices{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::SkewHermitianMatrices{Tuple{Int}}) = get_parameter(M.size)[1] - """ is_flat(::SkewHermitianMatrices) @@ -209,7 +206,7 @@ only the upper triangular elements of the matrix being unique, and the second te corresponds to the constraint that the real part of the diagonal be zero. """ function manifold_dimension(M::SkewHermitianMatrices{<:Any,𝔽}) where {𝔽} - N = get_n(M) + N = get_parameter(M.size)[1] return div(N * (N + 1), 2) * real_dimension(𝔽) - N end @@ -251,23 +248,23 @@ project(::SkewHermitianMatrices, ::Any, ::Any) project!(M::SkewHermitianMatrices, Y, p, X) = project!(M, Y, X) function representation_size(M::SkewHermitianMatrices) - N = get_n(M) + N = get_parameter(M.size)[1] return (N, N) end function Base.show(io::IO, ::SkewHermitianMatrices{TypeParameter{Tuple{n}},F}) where {n,F} - return print(io, "SkewHermitianMatrices($(n), $(F); parameter=:type)") + return print(io, "SkewHermitianMatrices($(n), $(F))") end function Base.show(io::IO, ::SkewSymmetricMatrices{TypeParameter{Tuple{n}}}) where {n} - return print(io, "SkewSymmetricMatrices($(n); parameter=:type)") + return print(io, "SkewSymmetricMatrices($(n))") end function Base.show(io::IO, M::SkewHermitianMatrices{Tuple{Int},F}) where {F} - n = get_n(M) - return print(io, "SkewHermitianMatrices($(n), $(F))") + n = get_parameter(M.size)[1] + return print(io, "SkewHermitianMatrices($(n), $(F); parameter=:field)") end function Base.show(io::IO, M::SkewSymmetricMatrices{Tuple{Int}}) - n = get_n(M) - return print(io, "SkewSymmetricMatrices($(n))") + n = get_parameter(M.size)[1] + return print(io, "SkewSymmetricMatrices($(n); parameter=:field)") end @doc raw""" diff --git a/src/manifolds/Spectrahedron.jl b/src/manifolds/Spectrahedron.jl index 28b883538f..e36a63f5f0 100644 --- a/src/manifolds/Spectrahedron.jl +++ b/src/manifolds/Spectrahedron.jl @@ -1,5 +1,5 @@ @doc raw""" - Spectrahedron{N,K} <: AbstractDecoratorManifold{ℝ} + Spectrahedron{T} <: AbstractDecoratorManifold{ℝ} The Spectrahedron manifold, also known as the set of correlation matrices (symmetric positive semidefinite matrices) of rank $k$ with unit trace. @@ -38,13 +38,18 @@ investigated in [JourneeBachAbsilSepulchre:2010](@cite). # Constructor - Spectrahedron(n,k) + Spectrahedron(n::Int, k::Int; parameter::Symbol=:type) generates the manifold $\mathcal S(n,k) \subset ℝ^{n × n}$. """ -struct Spectrahedron{N,K} <: AbstractDecoratorManifold{ℝ} end +struct Spectrahedron{T} <: AbstractDecoratorManifold{ℝ} + size::T +end -Spectrahedron(n::Int, k::Int) = Spectrahedron{n,k}() +function Spectrahedron(n::Int, k::Int; parameter::Symbol=:type) + size = wrap_type_parameter(parameter, (n, k)) + return Spectrahedron{typeof(size)}(size) +end active_traits(f, ::Spectrahedron, args...) = merge_traits(IsIsometricEmbeddedManifold()) @@ -59,7 +64,7 @@ Since by construction $p$ is symmetric, this is not explicitly checked. Since $p$ is by construction positive semidefinite, this is not checked. The tolerances for positive semidefiniteness and unit trace can be set using the `kwargs...`. """ -function check_point(M::Spectrahedron{N,K}, q; kwargs...) where {N,K} +function check_point(M::Spectrahedron, q; kwargs...) fro_n = norm(q) if !isapprox(fro_n, 1.0; kwargs...) return DomainError( @@ -80,7 +85,7 @@ and a $X$ has to be a symmetric matrix with trace. The tolerance for the base point check and zero diagonal can be set using the `kwargs...`. Note that symmetry of $X$ holds by construction and is not explicitly checked. """ -function check_vector(M::Spectrahedron{N,K}, q, Y; kwargs...) where {N,K} +function check_vector(M::Spectrahedron, q, Y; kwargs...) X = q * Y' + Y * q' n = tr(X) if !isapprox(n, 0.0; kwargs...) @@ -112,7 +117,8 @@ returns the dimension of \dim \mathcal S(n,k) = nk - 1 - \frac{k(k-1)}{2}. ```` """ -@generated function manifold_dimension(::Spectrahedron{N,K}) where {N,K} +function manifold_dimension(M::Spectrahedron) + N, K = get_parameter(M.size) return N * K - 1 - div(K * (K - 1), 2) end @@ -155,10 +161,14 @@ Return the size of an array representing an element on the [`Spectrahedron`](@ref) manifold `M`, i.e. $n × k$, the size of such factor of $p=qq^{\mathrm{T}}$ on $\mathcal M = \mathcal S(n,k)$. """ -@generated representation_size(::Spectrahedron{N,K}) where {N,K} = (N, K) +representation_size(M::Spectrahedron) = get_parameter(M.size) -function Base.show(io::IO, ::Spectrahedron{N,K}) where {N,K} - return print(io, "Spectrahedron($(N), $(K))") +function Base.show(io::IO, M::Spectrahedron{TypeParameter{Tuple{n,k}}}) where {n,k} + return print(io, "Spectrahedron($n, $k)") +end +function Base.show(io::IO, M::Spectrahedron{Tuple{Int,Int}}) + n, k = get_parameter(M.size) + return print(io, "Spectrahedron($n, $k; parameter=:field)") end """ @@ -182,4 +192,4 @@ definite matrix `p` on the [`Spectrahedron`](@ref) manifold `M`. """ zero_vector(::Spectrahedron, ::Any...) -zero_vector!(::Spectrahedron{N,K}, v, ::Any) where {N,K} = fill!(v, 0) +zero_vector!(::Spectrahedron, X, ::Any) = fill!(X, 0) diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 46e4d68e4d..4c6deaa8ec 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -52,7 +52,7 @@ generate the complex- and quaternionic-valued sphere. struct Sphere{T,𝔽} <: AbstractSphere{𝔽} size::T end -function Sphere(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) +function Sphere(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return Sphere{typeof(size),field}(size) end @@ -89,7 +89,7 @@ several functions like the [`inner`](@ref inner(::Euclidean, ::Any...)) product # Constructor - ArraySphere(n₁,n₂,...,nᵢ; field=ℝ) + ArraySphere(n₁,n₂,...,nᵢ; field=ℝ, parameter::Symbol=:type) Generate sphere in $𝔽^{n_1, n_2, …, n_i}$, where $𝔽$ defaults to the real-valued case $ℝ$. """ @@ -99,7 +99,7 @@ end function ArraySphere( n::Vararg{Int,I}; field::AbstractNumbers=ℝ, - parameter::Symbol=:field, + parameter::Symbol=:type, ) where {I} size = wrap_type_parameter(parameter, n) return ArraySphere{typeof(size),field}(size) @@ -198,7 +198,7 @@ function exp!(M::AbstractSphere, q, p, X, t::Number) end function get_basis_diagonalizing(M::Sphere{<:Any,ℝ}, p, B::DiagonalizingOrthonormalBasis{ℝ}) - n = get_n(M) + n = get_parameter(M.size)[1] A = zeros(n + 1, n + 1) A[1, :] = transpose(p) A[2, :] = transpose(B.frame_direction) @@ -243,6 +243,9 @@ end function get_embedding(M::AbstractSphere{𝔽}) where {𝔽} return Euclidean(representation_size(M)...; field=𝔽) end +function get_embedding(M::Sphere{<:Tuple,𝔽}) where {𝔽} + return Euclidean(representation_size(M)...; field=𝔽, parameter=:field) +end @doc raw""" get_vector(M::AbstractSphere{ℝ}, p, X, B::DefaultOrthonormalBasis) @@ -273,9 +276,6 @@ function get_vector_orthonormal!(M::AbstractSphere{ℝ}, Y, p, X, ::RealNumbers) return Y end -get_n(::Sphere{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::Sphere{Tuple{Int}}) = get_parameter(M.size)[1] - @doc raw""" injectivity_radius(M::AbstractSphere[, p]) @@ -330,7 +330,7 @@ the diagonal matrix of size ``n×n`` with ones on the diagonal, since the metric from the embedding by restriction to the tangent space ``T_p\mathcal M`` at ``p``. """ function local_metric(M::Sphere{Tuple{Int},ℝ}, p, ::DefaultOrthonormalBasis) - n = get_n(M) + n = get_parameter(M.size)[1] return Diagonal(ones(eltype(p), n)) end function local_metric( @@ -477,7 +477,7 @@ function representation_size(M::ArraySphere) return get_parameter(M.size) end function representation_size(M::Sphere) - n = get_n(M) + n = get_parameter(M.size)[1] return (n + 1,) end @@ -498,21 +498,18 @@ function retract_project!(M::AbstractSphere, q, p, X, t::Number) end function Base.show(io::IO, ::Sphere{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return print(io, "Sphere($(n), $(𝔽); parameter=:type)") + return print(io, "Sphere($(n), $(𝔽))") end function Base.show(io::IO, M::Sphere{Tuple{Int},𝔽}) where {𝔽} - n = get_n(M) - return print(io, "Sphere($(n), $(𝔽))") + n = get_parameter(M.size)[1] + return print(io, "Sphere($(n), $(𝔽); parameter=:field)") end function Base.show(io::IO, ::ArraySphere{TypeParameter{tn},𝔽}) where {tn,𝔽} - return print( - io, - "ArraySphere($(join(tn.parameters, ", ")); field = $(𝔽), parameter=:type)", - ) + return print(io, "ArraySphere($(join(tn.parameters, ", ")); field=$(𝔽))") end function Base.show(io::IO, M::ArraySphere{<:Tuple,𝔽}) where {𝔽} n = M.size - return print(io, "ArraySphere($(join(n, ", ")); field = $(𝔽))") + return print(io, "ArraySphere($(join(n, ", ")); field=$(𝔽), parameter=:field)") end """ @@ -522,7 +519,7 @@ Uniform distribution on given [`Sphere`](@ref) `M`. Generated points will be of similar type as `p`. """ function uniform_distribution(M::Sphere{<:Any,ℝ}, p) - n = get_n(M) + n = get_parameter(M.size)[1] d = Distributions.MvNormal(zero(p), 1.0) return ProjectedPointDistribution(M, d, project!, p) end @@ -644,7 +641,7 @@ function get_coordinates_induced_basis!( X, B::InducedBasis{ℝ,TangentSpaceType,<:StereographicAtlas}, ) - n = get_n(M) + n = get_parameter(M.size)[1] if B.i === :north for i in 1:n Y[i] = X[i + 1] / (1 + p[1]) - X[1] * p[i + 1] / (1 + p[1])^2 @@ -664,7 +661,7 @@ function get_vector_induced_basis!( X, B::InducedBasis{ℝ,TangentSpaceType,<:StereographicAtlas}, ) - n = get_n(M) + n = get_parameter(M.size)[1] a = get_parameters(M, B.A, B.i, p) mult = inv(1 + dot(a, a))^2 diff --git a/src/manifolds/SphereSymmetricMatrices.jl b/src/manifolds/SphereSymmetricMatrices.jl index c730d53c9b..cdb97c05e5 100644 --- a/src/manifolds/SphereSymmetricMatrices.jl +++ b/src/manifolds/SphereSymmetricMatrices.jl @@ -18,7 +18,7 @@ struct SphereSymmetricMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T end -function SphereSymmetricMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) +function SphereSymmetricMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return SphereSymmetricMatrices{typeof(size),field}(size) end @@ -68,16 +68,13 @@ embed(::SphereSymmetricMatrices, p) = p embed(::SphereSymmetricMatrices, p, X) = X function get_embedding(::SphereSymmetricMatrices{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return ArraySphere(n, n; field=𝔽, parameter=:type) + return ArraySphere(n, n; field=𝔽) end function get_embedding(M::SphereSymmetricMatrices{Tuple{Int},𝔽}) where {𝔽} - n = get_n(M) - return ArraySphere(n, n; field=𝔽) + n = get_parameter(M.size)[1] + return ArraySphere(n, n; field=𝔽, parameter=:field) end -get_n(::SphereSymmetricMatrices{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::SphereSymmetricMatrices{Tuple{Int}}) = get_parameter(M.size)[1] - """ is_flat(::SphereSymmetricMatrices) @@ -99,7 +96,7 @@ Frobenius norm over the number system `𝔽`, i.e. ```` """ function manifold_dimension(M::SphereSymmetricMatrices{<:Any,𝔽}) where {𝔽} - n = get_n(M) + n = get_parameter(M.size)[1] return div(n * (n + 1), 2) * real_dimension(𝔽) - (𝔽 === ℂ ? n : 0) - 1 end @@ -136,14 +133,14 @@ function project!(M::SphereSymmetricMatrices, Y, p, X) end function representation_size(M::SphereSymmetricMatrices) - n = get_n(M) + n = get_parameter(M.size)[1] return (n, n) end function Base.show(io::IO, ::SphereSymmetricMatrices{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} - return print(io, "SphereSymmetricMatrices($(n), $(𝔽); parameter=:type)") + return print(io, "SphereSymmetricMatrices($(n), $(𝔽))") end function Base.show(io::IO, M::SphereSymmetricMatrices{Tuple{Int},𝔽}) where {𝔽} - n = get_n(M) - return print(io, "SphereSymmetricMatrices($(n), $(𝔽))") + n = get_parameter(M.size)[1] + return print(io, "SphereSymmetricMatrices($(n), $(𝔽); parameter=:field)") end diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 81db0a3ce0..454871a9ef 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -27,7 +27,7 @@ The manifold is named after [Eduard L. Stiefel](https://en.wikipedia.org/wiki/Eduard_Stiefel) (1909–1978). # Constructor - Stiefel(n, k, field = ℝ; parameter::Symbol=:field) + Stiefel(n, k, field = ℝ; parameter::Symbol=:type) Generate the (real-valued) Stiefel manifold of $n × k$ dimensional orthonormal matrices. """ @@ -35,7 +35,7 @@ struct Stiefel{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T end -function Stiefel(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) +function Stiefel(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n, k)) return Stiefel{typeof(size),field}(size) end @@ -104,7 +104,7 @@ where $\cdot^{\mathrm{H}}$ denotes the Hermitian and $\overline{\cdot}$ the (ele The settings for approximately can be set with `kwargs...`. """ function check_vector(M::Stiefel, p, X; kwargs...) - n, k = get_nk(M) + n, k = get_parameter(M.size) cks = check_size(M, p, X) cks === nothing || return cks if !isapprox(p' * X, -conj(X' * p); kwargs...) @@ -145,16 +145,13 @@ embed(::Stiefel, p) = p embed(::Stiefel, p, X) = X function get_embedding(::Stiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} - return Euclidean(n, k; field=𝔽, parameter=:type) + return Euclidean(n, k; field=𝔽) end function get_embedding(M::Stiefel{Tuple{Int,Int},𝔽}) where {𝔽} - n, k = get_nk(M) - return Euclidean(n, k; field=𝔽) + n, k = get_parameter(M.size) + return Euclidean(n, k; field=𝔽, parameter=:field) end -get_nk(::Stiefel{TypeParameter{Tuple{n,k}}}) where {n,k} = (n, k) -get_nk(M::Stiefel{Tuple{Int,Int}}) = get_parameter(M.size) - @doc raw""" inverse_retract(M::Stiefel, p, q, ::PolarInverseRetraction) @@ -190,7 +187,7 @@ in [KanekoFioriTanaka:2013](@cite). inverse_retract(::Stiefel, ::Any, ::Any, ::QRInverseRetraction) function _stiefel_inv_retr_qr_mul_by_r_generic!(M::Stiefel, X, q, R, A) - n, k = get_nk(M) + n, k = get_parameter(M.size) @inbounds for i in 1:k b = zeros(eltype(R), i) b[i] = 1 @@ -284,7 +281,7 @@ function _stiefel_inv_retr_qr_mul_by_r!( return _stiefel_inv_retr_qr_mul_by_r_generic!(M, X, q, R, A) end function _stiefel_inv_retr_qr_mul_by_r!(M::Stiefel, X, q, A, ::Type{ElT}) where {ElT} - n, k = get_nk(M) + n, k = get_parameter(M.size) R = zeros(ElT, k, k) return _stiefel_inv_retr_qr_mul_by_r_generic!(M, X, q, R, A) end @@ -298,7 +295,7 @@ function inverse_retract_polar!(::Stiefel, X, p, q) return X end function inverse_retract_qr!(M::Stiefel, X, p, q) - n, k = get_nk(M) + n, k = get_parameter(M.size) A = p' * q @boundscheck size(A) === (k, k) ElT = typeof(one(eltype(p)) * one(eltype(q))) @@ -333,15 +330,15 @@ The dimension is given by ```` """ function manifold_dimension(M::Stiefel{<:Any,ℝ}) - n, k = get_nk(M) + n, k = get_parameter(M.size) return n * k - div(k * (k + 1), 2) end function manifold_dimension(M::Stiefel{<:Any,ℂ}) - n, k = get_nk(M) + n, k = get_parameter(M.size) return 2 * n * k - k * k end function manifold_dimension(M::Stiefel{<:Any,ℍ}) - n, k = get_nk(M) + n, k = get_parameter(M.size) return 4 * n * k - k * (2k - 1) end @@ -366,7 +363,7 @@ function Random.rand!( vector_at=nothing, σ::Real=one(real(eltype(pX))), ) where {𝔽} - n, k = get_nk(M) + n, k = get_parameter(M.size) if vector_at === nothing A = σ * randn(rng, 𝔽 === ℝ ? Float64 : ComplexF64, n, k) pX .= Matrix(qr(A).Q) @@ -515,14 +512,14 @@ end Returns the representation size of the [`Stiefel`](@ref) `M`=$\operatorname{St}(n,k)$, i.e. `(n,k)`, which is the matrix dimensions. """ -representation_size(M::Stiefel) = get_nk(M) +representation_size(M::Stiefel) = get_parameter(M.size) function Base.show(io::IO, ::Stiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} - return print(io, "Stiefel($(n), $(k), $(𝔽); parameter=:type)") + return print(io, "Stiefel($(n), $(k), $(𝔽))") end function Base.show(io::IO, M::Stiefel{Tuple{Int,Int},𝔽}) where {𝔽} - n, k = get_nk(M) - return print(io, "Stiefel($(n), $(k), $(𝔽))") + n, k = get_parameter(M.size) + return print(io, "Stiefel($(n), $(k), $(𝔽); parameter=:field)") end """ @@ -536,7 +533,7 @@ The implementation is based on Section 2.5.1 in [Chikuse:2003](@cite); see also Theorem 2.2.1(iii) in [Chikuse:2003](@cite). """ function uniform_distribution(M::Stiefel{<:Any,ℝ}, p) - n, k = get_nk(M) + n, k = get_parameter(M.size) μ = Distributions.Zeros(n, k) σ = one(eltype(p)) Σ1 = Distributions.PDMats.ScalMat(n, σ) diff --git a/src/manifolds/StiefelCanonicalMetric.jl b/src/manifolds/StiefelCanonicalMetric.jl index c0e05069db..e60a31a924 100644 --- a/src/manifolds/StiefelCanonicalMetric.jl +++ b/src/manifolds/StiefelCanonicalMetric.jl @@ -67,7 +67,7 @@ For more details, see [EdelmanAriasSmith:1998](@cite)[Zimmermann:2017](@cite). exp(::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, ::Any...) function exp!(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, q, p, X) - n, k = get_nk(M.manifold) + n, k = get_parameter(M.manifold.size) A = p' * X n == k && return mul!(q, p, exp(A)) QR = qr(X - p * A) @@ -90,7 +90,7 @@ g_p(X,Y) = \operatorname{tr}\bigl( X^{\mathrm{T}}(I_n - \frac{1}{2}pp^{\mathrm{T ``` """ function inner(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, p, X, Y) - n, k = get_nk(M.manifold) + n, k = get_parameter(M.manifold.size) T = Base.promote_eltype(p, X, Y) if n == k return T(dot(X, Y)) / 2 @@ -148,7 +148,7 @@ function inverse_retract!( q, a::ApproximateLogarithmicMap, ) - n, k = get_nk(M.manifold) + n, k = get_parameter(M.manifold.size) qfact = stiefel_factorization(p, q) V = allocate(qfact.Z, Size(2k, 2k)) LV = allocate(V) diff --git a/src/manifolds/StiefelEuclideanMetric.jl b/src/manifolds/StiefelEuclideanMetric.jl index 50a9c451a4..f886d22c67 100644 --- a/src/manifolds/StiefelEuclideanMetric.jl +++ b/src/manifolds/StiefelEuclideanMetric.jl @@ -24,7 +24,7 @@ $0_k$ are the identity matrix and the zero matrix of dimension $k × k$, respect exp(::Stiefel, ::Any...) function exp!(M::Stiefel, q, p, X) - n, k = get_nk(M) + n, k = get_parameter(M.size) A = p' * X B = exp([A -X'*X; I A]) @views begin @@ -89,7 +89,7 @@ function get_vector_orthonormal!(M::Stiefel{<:Any,ℝ}, X, p, c, N::RealNumbers) end function get_vectors(M::Stiefel{<:Any,ℝ}, p, ::DefaultOrthonormalBasis{ℝ,TangentSpaceType}) - n, k = get_nk(M) + n, k = get_parameter(M.size) p⊥ = nullspace([p zeros(n, n - k)]) an = div(k * (k - 1), 2) bn = (n - k) * k diff --git a/src/manifolds/StiefelSubmersionMetric.jl b/src/manifolds/StiefelSubmersionMetric.jl index 982c7f385f..8dbe89d8e2 100644 --- a/src/manifolds/StiefelSubmersionMetric.jl +++ b/src/manifolds/StiefelSubmersionMetric.jl @@ -43,7 +43,7 @@ For ``k < \frac{n}{2}`` the exponential is computed more efficiently using exp(::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, ::Any...) function exp!(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, q, p, X) - n, k = get_nk(M.manifold) + n, k = get_parameter(M.manifold.size) α = metric(M).α T = Base.promote_eltype(q, p, X) if k ≤ div(n, 2) @@ -88,7 +88,7 @@ g_p(X,Y) = \operatorname{tr}\bigl( X^{\mathrm{T}}(I_n - \frac{2α+1}{2(α+1)}pp^ where ``α`` is the parameter of the metric. """ function inner(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetric}, p, X, Y) - n, k = get_nk(M.manifold) + n, k = get_parameter(M.manifold.size) α = metric(M).α T = typeof(one(Base.promote_eltype(p, X, Y, α))) if n == k @@ -155,7 +155,7 @@ function inverse_retract_shooting!( <:Union{ProjectionTransport,ScaledVectorTransport{ProjectionTransport}}, }, ) - n, k = get_nk(M.manifold) + n, k = get_parameter(M.manifold.size) if k > div(n, 2) # fall back to default method invoke( @@ -410,7 +410,7 @@ function Base.copyto!( return dest end function project!(M::Stiefel{<:Any,ℝ}, q::StiefelFactorization, p::StiefelFactorization) - n, k = get_nk(M) + n, k = get_parameter(M.size) project!(Stiefel(2k, k), q.Z, p.Z) return q end @@ -420,7 +420,7 @@ function project!( p::StiefelFactorization, X::StiefelFactorization, ) - n, k = get_nk(M) + n, k = get_parameter(M.size) project!(Stiefel(2k, k), Y.Z, p.Z, X.Z) return Y end @@ -430,7 +430,7 @@ function inner( X::StiefelFactorization, Y::StiefelFactorization, ) - n, k = get_nk(M.manifold) + n, k = get_parameter(M.manifold.size) Msub = MetricManifold(Stiefel(2k, k), metric(M)) return inner(Msub, p.Z, X.Z, Y.Z) end @@ -440,7 +440,7 @@ function exp!( p::StiefelFactorization, X::StiefelFactorization, ) - n, k = get_nk(M.manifold) + n, k = get_parameter(M.manifold.size) α = metric(M).α @views begin ZM = X.Z[1:k, 1:k] diff --git a/src/manifolds/Symmetric.jl b/src/manifolds/Symmetric.jl index 902948ec03..3d5a7832bd 100644 --- a/src/manifolds/Symmetric.jl +++ b/src/manifolds/Symmetric.jl @@ -25,7 +25,7 @@ struct SymmetricMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T end -function SymmetricMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) +function SymmetricMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return SymmetricMatrices{typeof(size),field}(size) end @@ -90,7 +90,7 @@ function get_basis(M::SymmetricMatrices, p, B::DiagonalizingOrthonormalBasis) end function get_coordinates_orthonormal!(M::SymmetricMatrices{<:Any,ℝ}, Y, p, X, ::RealNumbers) - N = get_n(M) + N = get_parameter(M.size)[1] dim = manifold_dimension(M) @assert size(Y) == (dim,) @assert size(X) == (N, N) @@ -110,7 +110,7 @@ function get_coordinates_orthonormal!( X, ::ComplexNumbers, ) - N = get_n(M) + N = get_parameter(M.size)[1] dim = manifold_dimension(M) @assert size(Y) == (dim,) @assert size(X) == (N, N) @@ -129,15 +129,15 @@ function get_coordinates_orthonormal!( end function get_embedding(::SymmetricMatrices{TypeParameter{Tuple{N}},𝔽}) where {N,𝔽} - return Euclidean(N, N; field=𝔽, parameter=:type) + return Euclidean(N, N; field=𝔽) end function get_embedding(M::SymmetricMatrices{Tuple{Int},𝔽}) where {𝔽} - N = get_n(M) - return Euclidean(N, N; field=𝔽) + N = get_parameter(M.size)[1] + return Euclidean(N, N; field=𝔽, parameter=:field) end function get_vector_orthonormal!(M::SymmetricMatrices{<:Any,ℝ}, Y, p, X, ::RealNumbers) - N = get_n(M) + N = get_parameter(M.size)[1] dim = manifold_dimension(M) @assert size(X) == (dim,) @assert size(Y) == (N, N) @@ -151,7 +151,7 @@ function get_vector_orthonormal!(M::SymmetricMatrices{<:Any,ℝ}, Y, p, X, ::Rea return Y end function get_vector_orthonormal!(M::SymmetricMatrices{<:Any,ℂ}, Y, p, X, ::ComplexNumbers) - N = get_n(M) + N = get_parameter(M.size)[1] dim = manifold_dimension(M) @assert size(X) == (dim,) @assert size(Y) == (N, N) @@ -166,9 +166,6 @@ function get_vector_orthonormal!(M::SymmetricMatrices{<:Any,ℂ}, Y, p, X, ::Com end ## unify within bases later. -get_n(::SymmetricMatrices{TypeParameter{Tuple{n}}}) where {n} = n -get_n(M::SymmetricMatrices{Tuple{Int}}) = get_parameter(M.size)[1] - """ is_flat(::SymmetricMatrices) @@ -192,7 +189,7 @@ Return the dimension of the [`SymmetricMatrices`](@ref) matrix `M` over the numb where the last $-n$ is due to the zero imaginary part for Hermitian matrices """ function manifold_dimension(M::SymmetricMatrices{<:Any,𝔽}) where {𝔽} - N = get_n(M) + N = get_parameter(M.size)[1] return div(N * (N + 1), 2) * real_dimension(𝔽) - (𝔽 === ℂ ? N : 0) end @@ -230,11 +227,11 @@ project(::SymmetricMatrices, ::Any, ::Any) project!(M::SymmetricMatrices, Y, p, X) = (Y .= (X .+ transpose(X)) ./ 2) function Base.show(io::IO, ::SymmetricMatrices{TypeParameter{Tuple{n}},F}) where {n,F} - return print(io, "SymmetricMatrices($(n), $(F); parameter=:type)") + return print(io, "SymmetricMatrices($(n), $(F))") end function Base.show(io::IO, M::SymmetricMatrices{Tuple{Int},F}) where {F} - n = get_n(M) - return print(io, "SymmetricMatrices($(n), $(F))") + n = get_parameter(M.size)[1] + return print(io, "SymmetricMatrices($(n), $(F); parameter=:field)") end @doc raw""" diff --git a/src/manifolds/SymmetricPositiveDefinite.jl b/src/manifolds/SymmetricPositiveDefinite.jl index e0c922e31a..27d7cb43b8 100644 --- a/src/manifolds/SymmetricPositiveDefinite.jl +++ b/src/manifolds/SymmetricPositiveDefinite.jl @@ -22,7 +22,7 @@ i.e. the set of symmetric matrices, # Constructor - SymmetricPositiveDefinite(n; parameter::Symbol=:field) + SymmetricPositiveDefinite(n; parameter::Symbol=:type) generates the manifold $\mathcal P(n) \subset ℝ^{n × n}$ """ @@ -30,7 +30,7 @@ struct SymmetricPositiveDefinite{T} <: AbstractDecoratorManifold{ℝ} size::T end -function SymmetricPositiveDefinite(n::Int; parameter::Symbol=:field) +function SymmetricPositiveDefinite(n::Int; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return SymmetricPositiveDefinite{typeof(size)}(size) end @@ -228,12 +228,13 @@ embed(::SymmetricPositiveDefinite, p) = p embed(::SymmetricPositiveDefinite, p::SPDPoint) = convert(AbstractMatrix, p) embed(::SymmetricPositiveDefinite, p, X) = X -function get_embedding(M::SymmetricPositiveDefinite) - return Euclidean(representation_size(M)...; field=ℝ) +function get_embedding(::SymmetricPositiveDefinite{TypeParameter{Tuple{n}}}) where {n} + return Euclidean(n, n; field=ℝ) +end +function get_embedding(M::SymmetricPositiveDefinite{Tuple{Int}}) + n = get_parameter(M.size)[1] + return Euclidean(n, n; field=ℝ, parameter=:field) end - -get_n(::SymmetricPositiveDefinite{TypeParameter{Tuple{N}}}) where {N} = N -get_n(M::SymmetricPositiveDefinite{Tuple{Int}}) = get_parameter(M.size)[1] @doc raw""" injectivity_radius(M::SymmetricPositiveDefinite[, p]) @@ -270,7 +271,7 @@ returns the dimension of ```` """ function manifold_dimension(M::SymmetricPositiveDefinite) - n = get_n(M) + n = get_parameter(M.size)[1] return div(n * (n + 1), 2) end @@ -421,7 +422,7 @@ function Random.rand!( (vector_at === nothing ? 1 : norm(convert(AbstractMatrix, vector_at))), tangent_distr=:Gaussian, ) - N = get_n(M) + N = get_parameter(M.size)[1] if vector_at === nothing D = Diagonal(1 .+ rand(rng, N)) # random diagonal matrix s = qr(σ * randn(rng, N, N)) # random q @@ -465,16 +466,16 @@ Return the size of an array representing an element on the symmetric positive definite matrix on $\mathcal M = \mathcal P(n)$. """ function representation_size(M::SymmetricPositiveDefinite) - N = get_n(M) + N = get_parameter(M.size)[1] return (N, N) end function Base.show(io::IO, ::SymmetricPositiveDefinite{TypeParameter{Tuple{n}}}) where {n} - return print(io, "SymmetricPositiveDefinite($(n); parameter=:type)") + return print(io, "SymmetricPositiveDefinite($(n))") end function Base.show(io::IO, M::SymmetricPositiveDefinite{Tuple{Int}}) - n = get_n(M) - return print(io, "SymmetricPositiveDefinite($(n))") + n = get_parameter(M.size)[1] + return print(io, "SymmetricPositiveDefinite($(n); parameter=:field)") end function Base.show(io::IO, ::MIME"text/plain", p::SPDPoint) diff --git a/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl b/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl index c41acc107c..126b40c1e2 100644 --- a/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl +++ b/src/manifolds/SymmetricPositiveDefiniteAffineInvariant.jl @@ -162,7 +162,7 @@ function get_basis_diagonalizing( p, B::DiagonalizingOrthonormalBasis, ) - N = get_n(M) + N = get_parameter(M.size)[1] (p_sqrt, p_sqrt_inv) = spd_sqrt_and_sqrt_inv(p) eigv = eigen(Symmetric(p_sqrt_inv * B.frame_direction * p_sqrt_inv)) V = eigv.vectors @@ -210,7 +210,7 @@ We then form the ONB by get_basis(::SymmetricPositiveDefinite, p, B::DefaultOrthonormalBasis) function get_basis_orthonormal(M::SymmetricPositiveDefinite, p, Ns::RealNumbers) - N = get_n(M) + N = get_parameter(M.size)[1] p_sqrt = spd_sqrt(p) Ξ = [similar(convert(AbstractMatrix, p)) for _ in 1:manifold_dimension(M)] k = 1 @@ -239,7 +239,7 @@ where $k$ is trhe linearized index of the $i=1,\ldots,n, j=i,\ldots,n$. get_coordinates(::SymmetricPositiveDefinite, c, p, X, ::DefaultOrthonormalBasis) function get_coordinates_orthonormal!(M::SymmetricPositiveDefinite, c, p, X, ::RealNumbers) - N = get_n(M) + N = get_parameter(M.size)[1] dim = manifold_dimension(M) @assert size(c) == (dim,) @assert size(X) == (N, N) @@ -277,7 +277,7 @@ where $k$ is the linearized index of the $i=1,\ldots,n, j=i,\ldots,n$. get_vector(::SymmetricPositiveDefinite, X, p, c, ::DefaultOrthonormalBasis) function get_vector_orthonormal!(M::SymmetricPositiveDefinite, X, p, c, ::RealNumbers) - N = get_n(M) + N = get_parameter(M.size)[1] @assert size(c) == (div(N * (N + 1), 2),) @assert size(X) == (N, N) p_sqrt = spd_sqrt(p) diff --git a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl index 6ac02ede73..25c7d22d42 100644 --- a/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl +++ b/src/manifolds/SymmetricPositiveDefiniteLogCholesky.jl @@ -36,7 +36,7 @@ $⌊\cdot⌋$ denbotes the strictly lower triangular matrix of its argument, and $\lVert\cdot\rVert_{\mathrm{F}}$ the Frobenius norm. """ function distance(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, p, q) - N = get_n(M.manifold) + N = get_parameter(M.manifold.size)[1] return distance(CholeskySpace(N), cholesky(p).L, cholesky(q).L) end @@ -58,7 +58,7 @@ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2} exp(::MetricManifold{ℝ,SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any...) function exp!(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, q, p, X) - N = get_n(M.manifold) + N = get_parameter(M.manifold.size)[1] (y, W) = spd_to_cholesky(p, X) z = exp(CholeskySpace(N), y, W) return copyto!(q, z * z') @@ -90,7 +90,7 @@ $a_z(W) = z (z^{-1}Wz^{-\mathrm{T}})_{\frac{1}{2}}$, and $(\cdot)_\frac{1}{2}$ denotes the lower triangular matrix with the diagonal multiplied by $\frac{1}{2}$ """ function inner(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, p, X, Y) - N = get_n(M.manifold) + N = get_parameter(M.manifold.size)[1] (z, Xz) = spd_to_cholesky(p, X) (z, Yz) = spd_to_cholesky(p, z, Y) return inner(CholeskySpace(N), z, Xz, Yz) @@ -119,7 +119,7 @@ of $q$ and the just mentioned logarithmic map is the one on [`CholeskySpace`](@r log(::MetricManifold{ℝ,SymmetricPositiveDefinite,LogCholeskyMetric}, ::Any...) function log!(M::MetricManifold{ℝ,<:SymmetricPositiveDefinite,LogCholeskyMetric}, X, p, q) - N = get_n(M.manifold) + N = get_parameter(M.manifold.size)[1] x = cholesky(p).L y = cholesky(q).L log!(CholeskySpace(N), X, x, y) @@ -161,7 +161,7 @@ function parallel_transport_to!( X, q, ) - N = get_n(M.manifold) + N = get_parameter(M.manifold.size)[1] y = cholesky(q).L (x, W) = spd_to_cholesky(p, X) parallel_transport_to!(CholeskySpace(N), Y, x, W, y) diff --git a/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl b/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl index c9e6532820..9be8593caf 100644 --- a/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl +++ b/src/manifolds/SymmetricPositiveSemidefiniteFixedRank.jl @@ -35,7 +35,7 @@ The metric was used in [JourneeBachAbsilSepulchre:2010](@cite)[MassartAbsil:2020 # Constructor - SymmetricPositiveSemidefiniteFixedRank(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:field) + SymmetricPositiveSemidefiniteFixedRank(n::Int, k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) Generate the manifold of $n × n$ symmetric positive semidefinite matrices of rank $k$ over the `field` of real numbers `ℝ` or complex numbers `ℂ`. @@ -48,7 +48,7 @@ function SymmetricPositiveSemidefiniteFixedRank( n::Int, k::Int, field::AbstractNumbers=ℝ; - parameter::Symbol=:field, + parameter::Symbol=:type, ) size = wrap_type_parameter(parameter, (n, k)) return SymmetricPositiveSemidefiniteFixedRank{typeof(size),field}(size) @@ -68,7 +68,7 @@ The symmetry of `p` is not explicitly checked since by using `q` p is symmetric The tolerance for the symmetry of `p` can and the rank of `q*q'` be set using `kwargs...`. """ function check_point(M::SymmetricPositiveSemidefiniteFixedRank, q; kwargs...) - n, k = get_nk(M) + n, k = get_parameter(M.size) p = q * q' r = rank(p * p'; kwargs...) if r < k @@ -94,21 +94,14 @@ check_vector(M::SymmetricPositiveSemidefiniteFixedRank, q, Y; kwargs...) function get_embedding( ::SymmetricPositiveSemidefiniteFixedRank{TypeParameter{Tuple{n,k}},𝔽}, ) where {n,k,𝔽} - return Euclidean(n, k; field=𝔽, parameter=:type) + return Euclidean(n, k; field=𝔽) end function get_embedding( M::SymmetricPositiveSemidefiniteFixedRank{Tuple{Int,Int},𝔽}, ) where {𝔽} - n, k = get_nk(M) - return Euclidean(n, k; field=𝔽) -end - -function get_nk( - ::SymmetricPositiveSemidefiniteFixedRank{TypeParameter{Tuple{n,k}}}, -) where {n,k} - return (n, k) + n, k = get_parameter(M.size) + return Euclidean(n, k; field=𝔽, parameter=:field) end -get_nk(M::SymmetricPositiveSemidefiniteFixedRank{Tuple{Int,Int}}) = get_parameter(M.size) @doc raw""" distance(M::SymmetricPositiveSemidefiniteFixedRank, p, q) @@ -209,11 +202,11 @@ where the last $k^2$ is due to the zero imaginary part for Hermitian matrices di manifold_dimension(::SymmetricPositiveSemidefiniteFixedRank) function manifold_dimension(M::SymmetricPositiveSemidefiniteFixedRank{<:Any,ℝ}) - n, k = get_nk(M) + n, k = get_parameter(M.size) return k * n - div(k * (k - 1), 2) end function manifold_dimension(M::SymmetricPositiveSemidefiniteFixedRank{<:Any,ℂ}) - n, k = get_nk(M) + n, k = get_parameter(M.size) return 2 * k * n - k * k end @@ -226,17 +219,17 @@ function Base.show( io::IO, ::SymmetricPositiveSemidefiniteFixedRank{TypeParameter{Tuple{n,k}},𝔽}, ) where {n,k,𝔽} - return print( - io, - "SymmetricPositiveSemidefiniteFixedRank($(n), $(k), $(𝔽); parameter=:type)", - ) + return print(io, "SymmetricPositiveSemidefiniteFixedRank($(n), $(k), $(𝔽))") end function Base.show( io::IO, M::SymmetricPositiveSemidefiniteFixedRank{Tuple{Int,Int},𝔽}, ) where {𝔽} - n, k = get_nk(M) - return print(io, "SymmetricPositiveSemidefiniteFixedRank($(n), $(k), $(𝔽))") + n, k = get_parameter(M.size) + return print( + io, + "SymmetricPositiveSemidefiniteFixedRank($(n), $(k), $(𝔽); parameter=:field)", + ) end """ diff --git a/src/manifolds/Symplectic.jl b/src/manifolds/Symplectic.jl index 7de358593d..23cfeca206 100644 --- a/src/manifolds/Symplectic.jl +++ b/src/manifolds/Symplectic.jl @@ -1,5 +1,5 @@ @doc raw""" - Symplectic{n, 𝔽} <: AbstractEmbeddedManifold{𝔽, DefaultIsometricEmbeddingType} + Symplectic{T, 𝔽} <: AbstractEmbeddedManifold{𝔽, DefaultIsometricEmbeddingType} The symplectic manifold consists of all ``2n \times 2n`` matrices which preserve the canonical symplectic form over ``𝔽^{2n × 2n} \times 𝔽^{2n × 2n}``, @@ -32,22 +32,26 @@ The tangent space at a point ``p`` is given by [BendokatZimmermann:2021](@cite) ```` # Constructor - Symplectic(2n, field=ℝ) -> Symplectic{div(2n, 2), field}() + + Symplectic(2n, field=ℝ; parameter::Symbol=:type) Generate the (real-valued) symplectic manifold of ``2n \times 2n`` symplectic matrices. The constructor for the [`Symplectic`](@ref) manifold accepts the even column/row embedding dimension ``2n`` for the real symplectic manifold, ``ℝ^{2n × 2n}``. """ -struct Symplectic{n,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct Symplectic{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end function active_traits(f, ::Symplectic, args...) return merge_traits(IsEmbeddedManifold(), IsDefaultMetric(RealSymplecticMetric())) end -function Symplectic(n::Int, field::AbstractNumbers=ℝ) +function Symplectic(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) n % 2 == 0 || throw(ArgumentError("The dimension of the symplectic manifold embedding space must be even. Was odd, n % 2 == $(n % 2).")) - return Symplectic{div(n, 2),field}() + size = wrap_type_parameter(parameter, (div(n, 2),)) + return Symplectic{typeof(size),field}(size) end @doc raw""" @@ -148,9 +152,9 @@ function change_representer!(::Symplectic, Y, ::EuclideanMetric, p, X) end @doc raw""" - change_representer(MetMan::MetricManifold{𝔽, Euclidean{Tuple{m, n}, 𝔽}, ExtendedSymplecticMetric}, + change_representer(MetMan::MetricManifold{<:Any, <:Euclidean, ExtendedSymplecticMetric}, EucMet::EuclideanMetric, p, X) - change_representer!(MetMan::MetricManifold{𝔽, Euclidean{Tuple{m, n}, 𝔽}, ExtendedSymplecticMetric}, + change_representer!(MetMan::MetricManifold{<:Any, <:Euclidean, ExtendedSymplecticMetric}, Y, EucMet::EuclideanMetric, p, X) Change the representation of a matrix ``ξ ∈ \mathbb{R}^{2n \times 2n}`` @@ -174,21 +178,21 @@ In this case, we compute the mapping ```` """ function change_representer( - ::MetricManifold{𝔽,Euclidean{Tuple{m,n},𝔽},ExtendedSymplecticMetric}, + ::MetricManifold{<:Any,<:Euclidean,ExtendedSymplecticMetric}, ::EuclideanMetric, p, X, -) where {𝔽,m,n} +) return p * p' * X end function change_representer!( - ::MetricManifold{𝔽,Euclidean{Tuple{m,n},𝔽},ExtendedSymplecticMetric}, + ::MetricManifold{<:Any,<:Euclidean,ExtendedSymplecticMetric}, Y, ::EuclideanMetric, p, X, -) where {𝔽,m,n} +) Y .= p * p' * X return Y end @@ -208,7 +212,7 @@ Q_{2n} = ```` The tolerance can be set with `kwargs...` (e.g. `atol = 1.0e-14`). """ -function check_point(M::Symplectic{n,ℝ}, p; kwargs...) where {n,ℝ} +function check_point(M::Symplectic, p; kwargs...) # Perform check that the matrix lives on the real symplectic manifold: expected_zero = norm(inv(M, p) * p - LinearAlgebra.I) if !isapprox(expected_zero, zero(eltype(p)); kwargs...) @@ -241,7 +245,7 @@ The tolerance can be set with `kwargs...` (e.g. `atol = 1.0e-14`). """ check_vector(::Symplectic, ::Any...) -function check_vector(M::Symplectic{n}, p, X; kwargs...) where {n} +function check_vector(M::Symplectic, p, X; kwargs...) Q = SymplecticMatrix(p, X) tangent_requirement_norm = norm(X' * Q * p + p' * Q * X, 2) if !isapprox(tangent_requirement_norm, 0.0; kwargs...) @@ -302,7 +306,7 @@ we see that \end{align*} ```` """ -function distance(M::Symplectic{n}, p, q) where {n} +function distance(M::Symplectic, p, q) return norm(log(symplectic_inverse_times(M, p, q))) end @@ -332,7 +336,13 @@ function exp!(M::Symplectic, q, p, X) return q end -get_embedding(::Symplectic{n,ℝ}) where {n} = Euclidean(2n, 2n; field=ℝ) +function get_embedding(::Symplectic{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return Euclidean(2 * n, 2 * n; field=𝔽) +end +function get_embedding(M::Symplectic{Tuple{Int},𝔽}) where {𝔽} + n = get_parameter(M.size)[1] + return Euclidean(2 * n, 2 * n; field=𝔽, parameter=:field) +end @doc raw""" gradient(M::Symplectic, f, p, backend::RiemannianProjectionBackend; @@ -398,7 +408,7 @@ function ManifoldDiff.gradient!( end @doc raw""" - inner(::Symplectic{n, ℝ}, p, X, Y) + inner(::Symplectic{<:Any,ℝ}, p, X, Y) Compute the canonical Riemannian inner product [`RealSymplecticMetric`](@ref) ````math @@ -406,7 +416,7 @@ Compute the canonical Riemannian inner product [`RealSymplecticMetric`](@ref) ```` between the two tangent vectors ``X, Y \in T_p\operatorname{Sp}(2n)``. """ -function inner(M::Symplectic{n,ℝ}, p, X, Y) where {n} +function inner(M::Symplectic{<:Any,ℝ}, p, X, Y) p_star = inv(M, p) return dot((p_star * X), (p_star * Y)) end @@ -445,7 +455,8 @@ A^{+} = \end{bmatrix}. ```` """ -function Base.inv(::Symplectic{n,ℝ}, A) where {n} +function Base.inv(M::Symplectic{<:Any,ℝ}, A) + n = get_parameter(M.size)[1] Ai = similar(A) checkbounds(A, 1:(2n), 1:(2n)) @inbounds for i in 1:n, j in 1:n @@ -463,7 +474,8 @@ function Base.inv(::Symplectic{n,ℝ}, A) where {n} return Ai end -function inv!(::Symplectic{n,ℝ}, A) where {n} +function inv!(M::Symplectic{<:Any,ℝ}, A) + n = get_parameter(M.size)[1] checkbounds(A, 1:(2n), 1:(2n)) @inbounds for i in 1:n, j in 1:n A[i, j], A[j + n, i + n] = A[j + n, i + n], A[i, j] @@ -537,7 +549,7 @@ Return false. [`Symplectic`](@ref) is not a flat manifold. is_flat(M::Symplectic) = false @doc raw""" - manifold_dimension(::Symplectic{n}) + manifold_dimension(::Symplectic) Returns the dimension of the symplectic manifold embedded in ``ℝ^{2n \times 2n}``, i.e. @@ -545,7 +557,10 @@ embedded in ``ℝ^{2n \times 2n}``, i.e. \operatorname{dim}(\operatorname{Sp}(2n)) = (2n + 1)n. ```` """ -manifold_dimension(::Symplectic{n}) where {n} = (2n + 1) * n +function manifold_dimension(M::Symplectic) + n = get_parameter(M.size)[1] + return (2n + 1) * n +end @doc raw""" project(::Symplectic, p, A) @@ -586,7 +601,7 @@ function project!(::Symplectic, Y, p, A) end @doc raw""" - project!(::MetricManifold{𝔽,Euclidean,ExtendedSymplecticMetric}, Y, p, X) where {𝔽} + project!(::MetricManifold{𝔽,<:Euclidean,ExtendedSymplecticMetric}, Y, p, X) where {𝔽} Compute the projection of ``X ∈ R^{2n × 2n}`` onto ``T_p\operatorname{Sp}(2n, ℝ)`` w.r.t. the Riemannian metric ``g`` [`RealSymplecticMetric`](@ref). @@ -599,12 +614,7 @@ The closed form projection mapping is given by [GaoSonAbsilStykel:2021](@cite) where ``\operatorname{sym}(A) = \frac{1}{2}(A + A^{\mathrm{T}})``. This function is not exported. """ -function project!( - ::MetricManifold{𝔽,Euclidean{Tuple{m,n},𝔽},ExtendedSymplecticMetric}, - Y, - p, - X, -) where {m,n,𝔽} +function project!(::MetricManifold{<:Any,<:Euclidean,ExtendedSymplecticMetric}, Y, p, X) Q = SymplecticMatrix(p, X) pT_QT_X = p' * Q' * X @@ -615,7 +625,7 @@ function project!( end @doc raw""" - project_normal!(::MetricManifold{𝔽,Euclidean,ExtendedSymplecticMetric}, Y, p, X) + project_normal!(::MetricManifold{𝔽,<:Euclidean,ExtendedSymplecticMetric}, Y, p, X) Project onto the normal of the tangent space ``(T_p\operatorname{Sp}(2n))^{\perp_g}`` at a point ``p ∈ \operatorname{Sp}(2n)``, relative to the riemannian metric @@ -637,11 +647,11 @@ where ``\operatorname{skew}(A) = \frac{1}{2}(A - A^{\mathrm{T}})``. This function is not exported. """ function project_normal!( - ::MetricManifold{𝔽,Euclidean{Tuple{m,n},𝔽},ExtendedSymplecticMetric}, + M::MetricManifold{𝔽,<:Euclidean,ExtendedSymplecticMetric}, Y, p, X, -) where {m,n,𝔽} +) where {𝔽} Q = SymplecticMatrix(p, X) pT_QT_X = p' * Q' * X @@ -686,7 +696,8 @@ function Random.rand( end end -function random_vector(::Symplectic{n}, p::AbstractMatrix; symmetric_norm=1.0) where {n} +function random_vector(M::Symplectic, p::AbstractMatrix; symmetric_norm=1.0) + n = get_parameter(M.size)[1] # Generate random symmetric matrix: S = randn(2n, 2n) S .= (S + S') @@ -696,7 +707,8 @@ function random_vector(::Symplectic{n}, p::AbstractMatrix; symmetric_norm=1.0) w return p * S end -function rand_hamiltonian(::Symplectic{n}; frobenius_norm=1.0) where {n} +function rand_hamiltonian(M::Symplectic; frobenius_norm=1.0) + n = get_parameter(M.size)[1] A = randn(n, n) B = randn(n, n) C = randn(n, n) @@ -750,7 +762,13 @@ function retract_cayley!(M::Symplectic, q, p, X, t::Number) return q end -Base.show(io::IO, ::Symplectic{n,𝔽}) where {n,𝔽} = print(io, "Symplectic{$(2n), $(𝔽)}()") +function Base.show(io::IO, ::Symplectic{TypeParameter{Tuple{n}},𝔽}) where {n,𝔽} + return print(io, "Symplectic($(2n), $(𝔽))") +end +function Base.show(io::IO, M::Symplectic{Tuple{Int},𝔽}) where {𝔽} + n = get_parameter(M.size)[1] + return print(io, "Symplectic($(2n), $(𝔽); parameter=:field)") +end @doc raw""" symplectic_inverse_times(::Symplectic, p, q) @@ -763,12 +781,13 @@ That is, this function efficiently computes where ``Q_{2n}`` is the [`SymplecticMatrix`](@ref) of size ``2n \times 2n``. """ -function symplectic_inverse_times(M::Symplectic{n}, p, q) where {n} +function symplectic_inverse_times(M::Symplectic, p, q) A = similar(p) return symplectic_inverse_times!(M, A, p, q) end -function symplectic_inverse_times!(::Symplectic{n}, A, p, q) where {n} +function symplectic_inverse_times!(M::Symplectic, A, p, q) + n = get_parameter(M.size)[1] # we write p = [p1 p2; p3 p4] (and q, too), then p1 = @view(p[1:n, 1:n]) p2 = @view(p[1:n, (n + 1):(2n)]) diff --git a/src/manifolds/SymplecticStiefel.jl b/src/manifolds/SymplecticStiefel.jl index b9e47f487c..3a4c1c6ebb 100644 --- a/src/manifolds/SymplecticStiefel.jl +++ b/src/manifolds/SymplecticStiefel.jl @@ -1,5 +1,5 @@ @doc raw""" - SymplecticStiefel{n, k, 𝔽} <: AbstractEmbeddedManifold{𝔽, DefaultIsometricEmbeddingType} + SymplecticStiefel{T,𝔽} <: AbstractEmbeddedManifold{𝔽, DefaultIsometricEmbeddingType} The symplectic Stiefel manifold consists of all $2n × 2k, \; n \geq k$ matrices satisfying the requirement @@ -30,8 +30,7 @@ where ``Ω \in \mathfrak{sp}(2n,F)`` is Hamiltonian and ``p^s`` means the symplectic complement of ``p`` s.t. ``p^{+}p^{s} = 0``. # Constructor - SymplecticStiefel(2n::Int, 2k::Int, field::AbstractNumbers=ℝ) - -> SymplecticStiefel{div(2n, 2), div(2k, 2), field}() + SymplecticStiefel(2n::Int, 2k::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) Generate the (real-valued) symplectic Stiefel manifold of ``2n \times 2k`` matrices which span a ``2k`` dimensional symplectic subspace of ``ℝ^{2n \times 2n}``. @@ -39,10 +38,18 @@ The constructor for the [`SymplecticStiefel`](@ref) manifold accepts the even co dimension ``2n`` and an even number of columns ``2k`` for the real symplectic Stiefel manifold with elements ``p \in ℝ^{2n × 2k}``. """ -struct SymplecticStiefel{n,k,𝔽} <: AbstractDecoratorManifold{𝔽} end +struct SymplecticStiefel{T,𝔽} <: AbstractDecoratorManifold{𝔽} + size::T +end -function SymplecticStiefel(two_n::Int, two_k::Int, field::AbstractNumbers=ℝ) - return SymplecticStiefel{div(two_n, 2),div(two_k, 2),field}() +function SymplecticStiefel( + two_n::Int, + two_k::Int, + field::AbstractNumbers=ℝ; + parameter::Symbol=:type, +) + size = wrap_type_parameter(parameter, (div(two_n, 2), div(two_k, 2))) + return SymplecticStiefel{typeof(size),field}(size) end function active_traits(f, ::SymplecticStiefel, args...) @@ -57,7 +64,7 @@ ManifoldsBase.default_retraction_method(::SymplecticStiefel) = CayleyRetraction( @doc raw""" canonical_project(::SymplecticStiefel, p_Sp) - canonical_project!(::SymplecticStiefel{n,k}, p, p_Sp) + canonical_project!(::SymplecticStiefel, p, p_Sp) Define the canonical projection from ``\operatorname{Sp}(2n, 2n)`` onto ``\operatorname{SpSt}(2n, 2k)``, by projecting onto the first ``k`` columns @@ -65,12 +72,14 @@ and the ``n + 1``'th onto the ``n + k``'th columns [BendokatZimmermann:2021](@ci It is assumed that the point ``p`` is on ``\operatorname{Sp}(2n, 2n)``. """ -function canonical_project(M::SymplecticStiefel{n,k}, p_Sp) where {n,k} +function canonical_project(M::SymplecticStiefel, p_Sp) + n, k = get_parameter(M.size) p_SpSt = similar(p_Sp, (2n, 2k)) return canonical_project!(M, p_SpSt, p_Sp) end -function canonical_project!(::SymplecticStiefel{n,k}, p, p_Sp) where {n,k} +function canonical_project!(M::SymplecticStiefel, p, p_Sp) + n, k = get_parameter(M.size) p[:, (1:k)] .= p_Sp[:, (1:k)] p[:, ((k + 1):(2k))] .= p_Sp[:, ((n + 1):(n + k))] return p @@ -94,7 +103,7 @@ Q_{2n} = ```` The tolerance can be set with `kwargs...` (e.g. `atol = 1.0e-14`). """ -function check_point(M::SymplecticStiefel{n,k}, p; kwargs...) where {n,k} +function check_point(M::SymplecticStiefel, p; kwargs...) # Perform check that the matrix lives on the real symplectic manifold: expected_zero = norm(inv(M, p) * p - I) if !isapprox(expected_zero, 0; kwargs...) @@ -133,7 +142,8 @@ The tolerance can be set with `kwargs...` (e.g. `atol = 1.0e-14`). """ check_vector(::SymplecticStiefel, ::Any...) -function check_vector(M::SymplecticStiefel{n,k,field}, p, X; kwargs...) where {n,k,field} +function check_vector(M::SymplecticStiefel{<:Any,field}, p, X; kwargs...) where {field} + n, k = get_parameter(M.size) # From Bendokat-Zimmermann: T_pSpSt(2n, 2k) = \{p*H | H^{+} = -H \} H = inv(M, p) * X # ∈ ℝ^{2k × 2k}, should be Hamiltonian. H_star = inv(Symplectic(2k, field), H) @@ -229,7 +239,8 @@ which only requires computing the matrix exponentials of """ exp(::SymplecticStiefel, p, X) -function exp!(M::SymplecticStiefel{n,k}, q, p, X) where {n,k} +function exp!(M::SymplecticStiefel, q, p, X) + n, k = get_parameter(M.size) Q = SymplecticMatrix(p, X) pT_p = lu(p' * p) # ∈ ℝ^{2k × 2k} @@ -276,17 +287,29 @@ function exp!(M::SymplecticStiefel{n,k}, q, p, X) where {n,k} return q end -get_embedding(::SymplecticStiefel{n,k,ℝ}) where {n,k} = Euclidean(2n, 2k; field=ℝ) +function get_embedding(::SymplecticStiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return Euclidean(2 * n, 2 * k; field=𝔽) +end +function get_embedding(M::SymplecticStiefel{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_parameter(M.size) + return Euclidean(2 * n, 2 * k; field=𝔽, parameter=:field) +end @doc raw""" get_total_space(::SymplecticStiefel) Return the total space of the [`SymplecticStiefel`](@ref) manifold, which is the corresponding [`Symplectic`](@ref) manifold. """ -get_total_space(::SymplecticStiefel{n,k,𝔽}) where {n,k,𝔽} = Symplectic{n,𝔽}() +function get_total_space(::SymplecticStiefel{TypeParameter{Tuple{n,k}},ℝ}) where {n,k} + return Symplectic(2 * n) +end +function get_total_space(M::SymplecticStiefel{Tuple{Int,Int},ℝ}) + n, k = get_parameter(M.size) + return Symplectic(2 * n; parameter=:field) +end @doc raw""" - inner(M::SymplecticStiefel{n, k}, p, X. Y) + inner(M::SymplecticStiefel, p, X. Y) Compute the Riemannian inner product ``g^{\operatorname{SpSt}}`` at ``p \in \operatorname{SpSt}`` between tangent vectors ``X, X \in T_p\operatorname{SpSt}``. @@ -297,7 +320,7 @@ g^{\operatorname{SpSt}}_p(X, Y) \frac{1}{2}Q_{2n}^{\mathrm{T}}p(p^{\mathrm{T}}p)^{-1}p^{\mathrm{T}}Q_{2n}\right)Y(p^{\mathrm{T}}p)^{-1}\right). ```` """ -function inner(::SymplecticStiefel{n,k}, p, X, Y) where {n,k} +function inner(::SymplecticStiefel, p, X, Y) Q = SymplecticMatrix(p, X, Y) # Procompute lu(p'p) since we solve a^{-1}* 3 times a = lu(p' * p) # note that p'p is symmetric, thus so is its inverse c=a^{-1} @@ -311,8 +334,8 @@ function inner(::SymplecticStiefel{n,k}, p, X, Y) where {n,k} end @doc raw""" - inv(::SymplecticStiefel{n, k}, A) - inv!(::SymplecticStiefel{n, k}, q, p) + inv(::SymplecticStiefel, A) + inv!(::SymplecticStiefel, q, p) Compute the symplectic inverse ``A^+`` of matrix ``A ∈ ℝ^{2n × 2k}``. Given a matrix ````math @@ -346,12 +369,13 @@ A^{+} = \end{bmatrix}. ```` """ -function Base.inv(M::SymplecticStiefel{n,k}, p) where {n,k} +function Base.inv(M::SymplecticStiefel, p) q = similar(p') return inv!(M, q, p) end -function inv!(::SymplecticStiefel{n,k}, q, p) where {n,k} +function inv!(M::SymplecticStiefel, q, p) + n, k = get_parameter(M.size) checkbounds(q, 1:(2k), 1:(2n)) checkbounds(p, 1:(2n), 1:(2k)) @inbounds for i in 1:k, j in 1:n @@ -419,7 +443,7 @@ Return false. [`SymplecticStiefel`](@ref) is not a flat manifold. is_flat(M::SymplecticStiefel) = false @doc raw""" - manifold_dimension(::SymplecticStiefel{n, k}) + manifold_dimension(::SymplecticStiefel) Returns the dimension of the symplectic Stiefel manifold embedded in ``ℝ^{2n \times 2k}``, i.e. [BendokatZimmermann:2021](@cite) @@ -427,7 +451,10 @@ i.e. [BendokatZimmermann:2021](@cite) \operatorname{dim}(\operatorname{SpSt}(2n, 2k)) = (4n - 2k + 1)k. ```` """ -manifold_dimension(::SymplecticStiefel{n,k}) where {n,k} = (4n - 2k + 1) * k +function manifold_dimension(M::SymplecticStiefel) + n, k = get_parameter(M.size) + return (4n - 2k + 1) * k +end @doc raw""" project(::SymplecticStiefel, p, A) @@ -491,10 +518,11 @@ and generates a random Hamiltonian matrix ``Ω_X \in \mathfrak{sp}(2n,F)`` with Frobenius norm of `hamiltonian_norm` before returning ``X = pΩ_X``. """ function Random.rand( - M::SymplecticStiefel{n}; + M::SymplecticStiefel; vector_at=nothing, hamiltonian_norm=(vector_at === nothing ? 1 / 2 : 1.0), -) where {n} +) + n, k = get_parameter(M.size) if vector_at === nothing return canonical_project(M, rand(Symplectic(2n); hamiltonian_norm=hamiltonian_norm)) else @@ -502,11 +530,8 @@ function Random.rand( end end -function random_vector( - ::SymplecticStiefel{n,k}, - p::AbstractMatrix; - hamiltonian_norm=1.0, -) where {n,k} +function random_vector(M::SymplecticStiefel, p::AbstractMatrix; hamiltonian_norm=1.0) + n, k = get_parameter(M.size) Ω = rand_hamiltonian(Symplectic(2k); frobenius_norm=hamiltonian_norm) return p * Ω end @@ -597,8 +622,12 @@ function riemannian_gradient!( return X end -function Base.show(io::IO, ::SymplecticStiefel{n,k,𝔽}) where {n,k,𝔽} - return print(io, "SymplecticStiefel{$(2n), $(2k), $(𝔽)}()") +function Base.show(io::IO, ::SymplecticStiefel{TypeParameter{Tuple{n,k}},𝔽}) where {n,k,𝔽} + return print(io, "SymplecticStiefel($(2n), $(2k), $(𝔽))") +end +function Base.show(io::IO, M::SymplecticStiefel{Tuple{Int,Int},𝔽}) where {𝔽} + n, k = get_parameter(M.size) + return print(io, "SymplecticStiefel($(2n), $(2k), $(𝔽); parameter=:field)") end @doc raw""" @@ -616,12 +645,14 @@ This function performs this common operation without allocating more than a ``2k \times 2k`` matrix to store the result in, or in the case of the in-place function, without allocating memory at all. """ -function symplectic_inverse_times(M::SymplecticStiefel{n,k}, p, q) where {n,k} +function symplectic_inverse_times(M::SymplecticStiefel, p, q) + n, k = get_parameter(M.size) A = similar(p, (2k, 2k)) return symplectic_inverse_times!(M, A, p, q) end -function symplectic_inverse_times!(::SymplecticStiefel{n,k}, A, p, q) where {n,k} +function symplectic_inverse_times!(M::SymplecticStiefel, A, p, q) + n, k = get_parameter(M.size) # we write p = [p1 p2; p3 p4] (and q, too), then p1 = @view(p[1:n, 1:k]) p2 = @view(p[1:n, (k + 1):(2k)]) diff --git a/src/manifolds/Tucker.jl b/src/manifolds/Tucker.jl index 0065765d47..c823895fba 100644 --- a/src/manifolds/Tucker.jl +++ b/src/manifolds/Tucker.jl @@ -36,7 +36,7 @@ where ``\mathcal{C}^\prime`` is arbitrary, ``U_d^{\mathrm{H}}`` is the Hermitian ``U_d``, and ``U_d^{\mathrm{H}} U_d^\prime = 0`` for all ``d``. # Constructor - Tucker(N::NTuple{D, Int}, R::NTuple{D, Int}[, field = ℝ]; parameter::Symbol=:field) + Tucker(N::NTuple{D, Int}, R::NTuple{D, Int}[, field = ℝ]; parameter::Symbol=:type) Generate the manifold of `field`-valued tensors of dimensions `N[1] × … × N[D]` and multilinear rank `R = (R[1], …, R[D])`. @@ -48,7 +48,7 @@ function Tucker( n⃗::NTuple{D,Int}, r⃗::NTuple{D,Int}, field::AbstractNumbers=ℝ; - parameter::Symbol=:field, + parameter::Symbol=:type, ) where {D} @assert is_valid_mlrank(n⃗, r⃗) size = wrap_type_parameter(parameter, (n⃗, r⃗)) @@ -224,7 +224,7 @@ to the unfoldings. For a [`TuckerPoint`](@ref) it is checked that the point is in correct HOSVD form. """ function check_point(M::Tucker, x; kwargs...) - N, R = get_nr(M) + N, R = get_parameter(M.size) s = "The point $(x) does not lie on $(M), " size(x) == N || return DomainError(size(x), s * "since its size is not $(N).") x_buffer = similar(x) @@ -235,7 +235,7 @@ function check_point(M::Tucker, x; kwargs...) return nothing end function check_point(M::Tucker, x::TuckerPoint; kwargs...) - N, R = get_nr(M) + N, R = get_parameter(M.size) s = "The point $(x) does not lie on $(M), " U = x.hosvd.U ℭ = x.hosvd.core @@ -497,9 +497,6 @@ function get_coordinates( return get_coordinates(M, 𝔄, X, get_basis(M, 𝔄, ℬ)) end -get_nr(::Tucker{TypeParameter{Tuple{n,r}}}) where {n,r} = (n, r) -get_nr(M::Tucker{<:Tuple}) = get_parameter(M.size) - #= get_vector(::Tucker, A, x, b) @@ -643,7 +640,7 @@ rank ``(R_1, \dots, R_D)``, i.e. ```` """ function manifold_dimension(M::Tucker) - n⃗, r⃗ = get_nr(M) + n⃗, r⃗ = get_parameter(M.size) return prod(r⃗) + sum(r⃗ .* (n⃗ .- r⃗)) end @@ -730,11 +727,11 @@ function Base.show( ::MIME"text/plain", ::Tucker{TypeParameter{Tuple{n,r}},D,𝔽}, ) where {n,r,D,𝔽} - return print(io, "Tucker($(n), $(r), $(𝔽); parameter=:type)") + return print(io, "Tucker($(n), $(r), $(𝔽))") end function Base.show(io::IO, ::MIME"text/plain", M::Tucker{<:Tuple,D,𝔽}) where {D,𝔽} - n, r = get_nr(M) - return print(io, "Tucker($(n), $(r), $(𝔽))") + n, r = get_parameter(M.size) + return print(io, "Tucker($(n), $(r), $(𝔽); parameter=:field)") end function Base.show(io::IO, ::MIME"text/plain", 𝔄::TuckerPoint) @@ -882,6 +879,6 @@ for fun in [:get_vector, :inverse_retract, :project, :zero_vector] end function ManifoldsBase.allocate_result(M::Tucker, f::typeof(embed), p, args...) - dims = get_nr(M)[1] + dims = get_parameter(M.size)[1] return Array{number_eltype(p),length(dims)}(undef, dims) end diff --git a/src/manifolds/Unitary.jl b/src/manifolds/Unitary.jl index bb5867b201..21e6fb90b3 100644 --- a/src/manifolds/Unitary.jl +++ b/src/manifolds/Unitary.jl @@ -29,7 +29,7 @@ see also [`OrthogonalMatrices`](@ref) for the real valued case. """ const UnitaryMatrices{T,𝔽} = GeneralUnitaryMatrices{T,𝔽,AbsoluteDeterminantOneMatrices} -function UnitaryMatrices(n::Int, 𝔽::AbstractNumbers=ℂ; parameter::Symbol=:field) +function UnitaryMatrices(n::Int, 𝔽::AbstractNumbers=ℂ; parameter::Symbol=:type) size = wrap_type_parameter(parameter, (n,)) return UnitaryMatrices{typeof(size),𝔽}(size) end @@ -89,7 +89,7 @@ Return the dimension of the manifold unitary matrices. ``` """ function manifold_dimension(M::UnitaryMatrices{<:Any,ℂ}) - n = get_n(M) + n = get_parameter(M.size)[1] return n^2 end @doc raw""" @@ -101,7 +101,7 @@ Return the dimension of the manifold unitary matrices. ``` """ function manifold_dimension(M::UnitaryMatrices{<:Any,ℍ}) - n = get_n(M) + n = get_parameter(M.size)[1] return n * (2n + 1) end @@ -131,18 +131,18 @@ function Random.rand( end function Base.show(io::IO, ::UnitaryMatrices{TypeParameter{Tuple{n}},ℂ}) where {n} - return print(io, "UnitaryMatrices($(n); parameter=:type)") + return print(io, "UnitaryMatrices($(n))") end function Base.show(io::IO, M::UnitaryMatrices{Tuple{Int},ℂ}) - n = get_n(M) - return print(io, "UnitaryMatrices($n)") + n = get_parameter(M.size)[1] + return print(io, "UnitaryMatrices($n; parameter=:field)") end function Base.show(io::IO, ::UnitaryMatrices{TypeParameter{Tuple{n}},ℍ}) where {n} - return print(io, "UnitaryMatrices($(n), ℍ; parameter=:type)") + return print(io, "UnitaryMatrices($(n), ℍ)") end function Base.show(io::IO, M::UnitaryMatrices{Tuple{Int},ℍ}) - n = get_n(M) - return print(io, "UnitaryMatrices($n, ℍ)") + n = get_parameter(M.size)[1] + return print(io, "UnitaryMatrices($n, ℍ; parameter=:field)") end @doc raw""" diff --git a/test/groups/general_linear.jl b/test/groups/general_linear.jl index 4afb0c6fc8..b41ca1abd4 100644 --- a/test/groups/general_linear.jl +++ b/test/groups/general_linear.jl @@ -36,7 +36,7 @@ using NLsolve @testset "GL(1,𝔽) special cases" begin @testset "real" begin - G = GeneralLinear(1; parameter=:type) + G = GeneralLinear(1) p = 3.0 * ones(1, 1) X = 1.0 * ones(1, 1) @test exp(G, p, X) ≈ p * exp(X)' * exp(X - X') @@ -49,7 +49,7 @@ using NLsolve log_lie(G, Identity(G)) == zeros(1, 1) # Matrix to matrix end @testset "complex" begin - G = GeneralLinear(1, ℂ; parameter=:type) + G = GeneralLinear(1, ℂ) p = (1 + im) * ones(1, 1) X = (1 - im) * ones(1, 1) @test exp(G, p, X) ≈ p * exp(X)' * exp(X - X') @@ -138,7 +138,7 @@ using NLsolve end @testset "Complex" begin - G = GeneralLinear(2, ℂ; parameter=:type) + G = GeneralLinear(2, ℂ) @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 2, 3), true) @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 3, 3), true) diff --git a/test/groups/general_unitary_groups.jl b/test/groups/general_unitary_groups.jl index b966424181..10ab1e8a21 100644 --- a/test/groups/general_unitary_groups.jl +++ b/test/groups/general_unitary_groups.jl @@ -103,8 +103,8 @@ include("group_utils.jl") end @testset "Quaternionic Unitary Group" begin - QU1 = Unitary(1, ℍ; parameter=:type) - @test repr(QU1) == "Unitary(1, ℍ; parameter=:type)" + QU1 = Unitary(1, ℍ) + @test repr(QU1) == "Unitary(1, ℍ)" @test identity_element(QU1) === Quaternion(1.0) @@ -142,8 +142,8 @@ include("group_utils.jl") end @testset "Special Unitary Group" begin - SU2 = SpecialUnitary(2; parameter=:type) - @test repr(SU2) == "SpecialUnitary(2; parameter=:type)" + SU2 = SpecialUnitary(2) + @test repr(SU2) == "SpecialUnitary(2)" p = ones(2, 2) q = project(SU2, p) @@ -210,22 +210,22 @@ include("group_utils.jl") end E = diagm(ones(4)) R1 = diagm([-1.0, -1.0, 1.0, 1.0]) - X1a = log(Rotations(4; parameter=:type), E, R1) - X1b = log_lie(SpecialOrthogonal(4; parameter=:type), R1) + X1a = log(Rotations(4), E, R1) + X1b = log_lie(SpecialOrthogonal(4), R1) @test isapprox(X1a, X1b) @test is_vector(Rotations(4), E, X1b) @test X1a[1, 2] ≈ π R2 = diagm([-1.0, 1.0, -1.0, 1.0]) - X2a = log(Rotations(4; parameter=:type), E, R2) - X2b = log_lie(SpecialOrthogonal(4; parameter=:type), R2) + X2a = log(Rotations(4), E, R2) + X2b = log_lie(SpecialOrthogonal(4), R2) @test isapprox(X2a, X2b) @test is_vector(Rotations(4), E, X2b) @test X2a[1, 3] ≈ π R3 = diagm([1.0, -1.0, -1.0, 1.0]) - X3a = log(Rotations(4; parameter=:type), E, R3) - X3b = log_lie(SpecialOrthogonal(4; parameter=:type), R3) + X3a = log(Rotations(4), E, R3) + X3b = log_lie(SpecialOrthogonal(4), R3) @test isapprox(X3a, X3b) @test is_vector(Rotations(4), E, X3b) @test X3a[2, 3] ≈ π diff --git a/test/groups/power_group.jl b/test/groups/power_group.jl index d67fbe7590..164196e39b 100644 --- a/test/groups/power_group.jl +++ b/test/groups/power_group.jl @@ -2,7 +2,7 @@ include("../utils.jl") include("group_utils.jl") @testset "Power group" begin - Mr = SpecialOrthogonal(3; parameter=:type) + Mr = SpecialOrthogonal(3) Mr1 = PowerManifold(Mr, 5) Mrn1 = PowerManifold(Mr, Manifolds.NestedPowerRepresentation(), 5) Mrnr1 = PowerManifold(Mr, Manifolds.NestedReplacingPowerRepresentation(), 5) diff --git a/test/groups/product_group.jl b/test/groups/product_group.jl index c2aaf6c73a..9dc00ba148 100644 --- a/test/groups/product_group.jl +++ b/test/groups/product_group.jl @@ -3,9 +3,9 @@ include("group_utils.jl") using RecursiveArrayTools @testset "Product group" begin - SOn = SpecialOrthogonal(3; parameter=:type) + SOn = SpecialOrthogonal(3) Tn = TranslationGroup(2) - Rn = Rotations(3; parameter=:type) + Rn = Rotations(3) M = ProductManifold(SOn, Tn) G = ProductGroup(M) @test_throws ErrorException ProductGroup(ProductManifold(Rotations(3), Stiefel(3, 2))) @@ -112,6 +112,6 @@ using RecursiveArrayTools ) @test sprint(show, "text/plain", G) === """ ProductGroup with 2 subgroups: - SpecialOrthogonal(3; parameter=:type) - TranslationGroup(2; field = ℝ)""" + SpecialOrthogonal(3) + TranslationGroup(2; field=ℝ)""" end diff --git a/test/groups/special_euclidean.jl b/test/groups/special_euclidean.jl index bc2a1d833a..31a917f7f4 100644 --- a/test/groups/special_euclidean.jl +++ b/test/groups/special_euclidean.jl @@ -16,9 +16,9 @@ Random.seed!(10) end if se_parameter === :field - @test repr(G) == "SpecialEuclidean($n)" + @test repr(G) == "SpecialEuclidean($n; parameter=:field)" else - @test repr(G) == "SpecialEuclidean($n; parameter=:type)" + @test repr(G) == "SpecialEuclidean($n)" end M = base_manifold(G) @test M === diff --git a/test/groups/special_orthogonal.jl b/test/groups/special_orthogonal.jl index e8a862d4b3..9f3f0faa06 100644 --- a/test/groups/special_orthogonal.jl +++ b/test/groups/special_orthogonal.jl @@ -3,10 +3,10 @@ include("group_utils.jl") @testset "Special Orthogonal group" begin for n in [2, 3] - G = SpecialOrthogonal(n; parameter=:type) - @test repr(G) == "SpecialOrthogonal($n; parameter=:type)" + G = SpecialOrthogonal(n) + @test repr(G) == "SpecialOrthogonal($n)" M = base_manifold(G) - @test M === Rotations(n; parameter=:type) + @test M === Rotations(n) p = Matrix(I, n, n) @test is_default_metric(MetricManifold(G, EuclideanMetric())) @test is_flat(G) == (n == 2) diff --git a/test/groups/translation_group.jl b/test/groups/translation_group.jl index 58c3520705..5c1b43d75c 100644 --- a/test/groups/translation_group.jl +++ b/test/groups/translation_group.jl @@ -4,10 +4,8 @@ include("group_utils.jl") @testset "Translation group" begin @testset "real" begin G = TranslationGroup(2, 3) - @test repr(G) == "TranslationGroup(2, 3; field = ℝ)" - @test repr(TranslationGroup(2, 3; field=ℂ)) == "TranslationGroup(2, 3; field = ℂ)" - @test repr(TranslationGroup(2, 3; field=ℂ, parameter=:type)) == - "TranslationGroup(2, 3; field = ℂ, parameter = :type)" + @test repr(G) == "TranslationGroup(2, 3; field=ℝ)" + @test repr(TranslationGroup(2, 3; field=ℂ)) == "TranslationGroup(2, 3; field=ℂ)" @test has_invariant_metric(G, LeftForwardAction()) @test has_invariant_metric(G, RightBackwardAction()) @@ -48,7 +46,7 @@ include("group_utils.jl") @testset "complex" begin G = TranslationGroup(2, 3; field=ℂ) - @test repr(G) == "TranslationGroup(2, 3; field = ℂ)" + @test repr(G) == "TranslationGroup(2, 3; field=ℂ)" types = [Matrix{ComplexF64}] @test base_manifold(G) === Euclidean(2, 3; field=ℂ) @@ -72,4 +70,8 @@ include("group_utils.jl") ) end end + @testset "field parameter" begin + @test repr(TranslationGroup(2, 3; field=ℂ, parameter=:field)) == + "TranslationGroup(2, 3; field=ℂ, parameter=:field)" + end end diff --git a/test/manifolds/centered_matrices.jl b/test/manifolds/centered_matrices.jl index 5b3b37d61e..21c7a92a4e 100644 --- a/test/manifolds/centered_matrices.jl +++ b/test/manifolds/centered_matrices.jl @@ -11,7 +11,7 @@ include("../utils.jl") @testset "Real Centered Matrices Basics" begin @test repr(M) == "CenteredMatrices(3, 2, ℝ)" @test representation_size(M) == (3, 2) - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{3,2}},ℝ} @test is_flat(M) @test check_point(M, A) === nothing @test_throws ManifoldDomainError is_point(M, B, true) diff --git a/test/manifolds/essential_manifold.jl b/test/manifolds/essential_manifold.jl index da9265085f..a466998f82 100644 --- a/test/manifolds/essential_manifold.jl +++ b/test/manifolds/essential_manifold.jl @@ -13,7 +13,7 @@ include("../utils.jl") p2 = [r1, r3] p3 = [r2, r2] @testset "Essential manifold Basics" begin - @test M.manifold == Rotations(3; parameter=:type) + @test M.manifold == Rotations(3) @test repr(M) == "EssentialManifold(true)" @test manifold_dimension(M) == 5 @test !is_flat(M) diff --git a/test/manifolds/euclidean.jl b/test/manifolds/euclidean.jl index 99c3ddecdf..89f873aae5 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 @@ -319,7 +319,7 @@ using FiniteDifferences end @testset "StaticArrays specializations" begin - M1 = Euclidean(3; parameter=:type) + M1 = Euclidean(3) @test get_vector( M1, SA[1.0, 2.0, 3.0], @@ -330,7 +330,7 @@ using FiniteDifferences c_sv = SizedVector{3}([-1.0, -2.0, -3.0]) @test get_vector(M1, SA[1.0, 2.0, 3.0], c_sv, DefaultOrthonormalBasis()) === c_sv - M2 = Euclidean(2, 2; parameter=:type) + M2 = Euclidean(2, 2) @test get_vector( M2, SA[1.0 2.0; 3.0 4.0], @@ -396,9 +396,9 @@ using FiniteDifferences @test volume_density(E, p, X) == 1.0 end - @testset "static parameter" begin - Ms = Euclidean(1; parameter=:type) - M0s = Euclidean(; parameter=:type) + @testset "field parameter" begin + Ms = Euclidean(1; parameter=:field) + M0s = Euclidean(; parameter=:field) @test distance(Ms, 2.0, 4.0) == 2.0 @test distance(M0s, 2.0, 4.0) == 2.0 diff --git a/test/manifolds/flag.jl b/test/manifolds/flag.jl index 61995d434f..2f08ba1838 100644 --- a/test/manifolds/flag.jl +++ b/test/manifolds/flag.jl @@ -270,10 +270,10 @@ using Random retract(OrthogonalMatrices(5), p1_ortho.value, X1_ortho.value, QRRetraction()) end - @testset "static parameters" begin - M = Flag(5, 1, 2; parameter=:type) - @test Manifolds.get_n(M) == 5 - @test get_embedding(M) == Stiefel(5, 2; parameter=:type) - @test repr(M) == "Flag(5, 1, 2; parameter=:type)" + @testset "field parameters" begin + M = Flag(5, 1, 2; parameter=:field) + @test Manifolds.get_parameter(M.size)[1] == 5 + @test get_embedding(M) == Stiefel(5, 2; parameter=:field) + @test repr(M) == "Flag(5, 1, 2; parameter=:field)" end end diff --git a/test/manifolds/hyperbolic.jl b/test/manifolds/hyperbolic.jl index fe0cbfc332..2fc06c114a 100644 --- a/test/manifolds/hyperbolic.jl +++ b/test/manifolds/hyperbolic.jl @@ -7,7 +7,7 @@ include("../utils.jl") @test base_manifold(M) == M @test manifold_dimension(M) == 2 @test typeof(get_embedding(M)) == - MetricManifold{ℝ,Euclidean{Tuple{Int},ℝ},MinkowskiMetric} + MetricManifold{ℝ,Euclidean{TypeParameter{Tuple{3}},ℝ},MinkowskiMetric} @test representation_size(M) == (3,) @test !is_flat(M) @test isinf(injectivity_radius(M)) diff --git a/test/manifolds/multinomial_matrices.jl b/test/manifolds/multinomial_matrices.jl index 1f30592bba..45fe1d87cf 100644 --- a/test/manifolds/multinomial_matrices.jl +++ b/test/manifolds/multinomial_matrices.jl @@ -6,8 +6,10 @@ include("../utils.jl") @test ProbabilitySimplex(2)^3 === MultinomialMatrices(3, 3) @test ProbabilitySimplex(2)^(3,) === PowerManifold(ProbabilitySimplex(2), 3) @test ^(ProbabilitySimplex(2), 2) === MultinomialMatrices(3, 2) - @test typeof(^(ProbabilitySimplex(2), 2)) == - MultinomialMatrices{Tuple{Int64,Int64},ProbabilitySimplex{Tuple{Int64},:open}} + @test typeof(^(ProbabilitySimplex(2), 2)) == MultinomialMatrices{ + TypeParameter{Tuple{3,2}}, + ProbabilitySimplex{TypeParameter{Tuple{2}},:open}, + } @test repr(M) == "MultinomialMatrices(3, 2)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 4 @@ -45,4 +47,9 @@ include("../utils.jl") test_inplace=true, ) end + @testset "field parameter" begin + M = ProbabilitySimplex(2; parameter=:field) + @test typeof(^(M, 2)) == + MultinomialMatrices{Tuple{Int64,Int64},ProbabilitySimplex{Tuple{Int64},:open}} + end end diff --git a/test/manifolds/oblique.jl b/test/manifolds/oblique.jl index 2c5e065fe9..b1037d59ff 100644 --- a/test/manifolds/oblique.jl +++ b/test/manifolds/oblique.jl @@ -6,10 +6,9 @@ include("../utils.jl") @test Sphere(2)^3 === Oblique(3, 3) @test Sphere(2)^(3,) === PowerManifold(Sphere(2), 3) @test ^(Sphere(2), 2) === Oblique(3, 2) - @test typeof(^(Sphere(2), 2)) == Oblique{Tuple{Int64,Int64},ℝ,Tuple{Int64}} - @test typeof(^(Sphere(2; parameter=:type), 2)) == + @test typeof(^(Sphere(2), 2)) == Oblique{TypeParameter{Tuple{3,2}},ℝ,TypeParameter{Tuple{2}}} - @test repr(M) == "Oblique(3, 2; field = ℝ)" + @test repr(M) == "Oblique(3, 2; field=ℝ)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 4 @test !is_flat(M) @@ -48,4 +47,8 @@ include("../utils.jl") test_inplace=true, ) end + @testset "field parameter" begin + @test typeof(^(Sphere(2; parameter=:field), 2)) == + Oblique{Tuple{Int,Int},ℝ,Tuple{Int}} + end end diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index b325d48703..c164511ff3 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -46,8 +46,7 @@ end @test manifold_dimension(Mrn2) == 105 @test repr(Ms1) == "PowerManifold(Sphere(2, ℝ), 5)" - @test repr(Mrn1) == - "PowerManifold(Rotations(3; parameter=:type), NestedPowerRepresentation(), 5)" + @test repr(Mrn1) == "PowerManifold(Rotations(3), NestedPowerRepresentation(), 5)" @test Manifolds.allocation_promotion_function(Ms, exp, ([1],)) == Manifolds.allocation_promotion_function(Ms1, exp, (1,)) diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index f8a448c8e8..330e037108 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -203,7 +203,7 @@ using RecursiveArrayTools: ArrayPartition ProductManifold(Sphere(2, ℝ), Euclidean(2; field = ℝ))""" end - M3 = Rotations(2; parameter=:type) + M3 = Rotations(2) Mser = ProductManifold(M1, M2, M3) @test submanifold(Mser, 2) == M2 @@ -406,7 +406,7 @@ using RecursiveArrayTools: ArrayPartition end @testset "vee/hat" begin - M1 = Rotations(3; parameter=:type) + M1 = Rotations(3) M2 = Euclidean(3) M = M1 × M2 diff --git a/test/manifolds/projective_space.jl b/test/manifolds/projective_space.jl index 24a4302440..fa6430cca0 100644 --- a/test/manifolds/projective_space.jl +++ b/test/manifolds/projective_space.jl @@ -290,7 +290,7 @@ include("../utils.jl") M = ArrayProjectiveSpace(2, 2; field=ℝ) @test manifold_dimension(M) == 3 @test repr(M) == "ArrayProjectiveSpace(2, 2; field = ℝ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{2,2}},ℝ} @test representation_size(M) == (2, 2) p = ones(2, 2) q = project(M, p) @@ -302,7 +302,7 @@ include("../utils.jl") M = ArrayProjectiveSpace(2, 2; field=ℂ) @test manifold_dimension(M) == 6 @test repr(M) == "ArrayProjectiveSpace(2, 2; field = ℂ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℂ} + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{2,2}},ℂ} @test representation_size(M) == (2, 2) end diff --git a/test/manifolds/skewhermitian.jl b/test/manifolds/skewhermitian.jl index 8c26531ed7..ce37ca70ef 100644 --- a/test/manifolds/skewhermitian.jl +++ b/test/manifolds/skewhermitian.jl @@ -22,7 +22,7 @@ end @test representation_size(M) == (3, 3) @test base_manifold(M) === M @test is_flat(M) - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{3,3}},ℝ} @test check_point(M, B_skewsym) === nothing @test_throws DomainError is_point(M, A, true) @test_throws ManifoldDomainError is_point(M, C, true) @@ -101,4 +101,8 @@ end ) end # testset type $T end # for + @testset "field parameter" begin + M = SkewHermitianMatrices(3, ℝ; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + end end # test SymmetricMatrices diff --git a/test/manifolds/sphere.jl b/test/manifolds/sphere.jl index 734641a9ea..0cfa7ed7b6 100644 --- a/test/manifolds/sphere.jl +++ b/test/manifolds/sphere.jl @@ -7,7 +7,7 @@ using ManifoldsBase: TFVector M = Sphere(2) @testset "Sphere Basics" begin @test repr(M) == "Sphere(2, ℝ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int},ℝ} + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{3}},ℝ} @test representation_size(M) == (3,) @test !is_flat(M) @test is_flat(Sphere(1)) @@ -139,7 +139,7 @@ using ManifoldsBase: TFVector @testset "Complex Sphere" begin M = Sphere(2, ℂ) @test repr(M) == "Sphere(2, ℂ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int},ℂ} + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{3}},ℂ} @test representation_size(M) == (3,) p = [1.0, 1.0im, 1.0] q = project(M, p) @@ -156,7 +156,7 @@ using ManifoldsBase: TFVector @testset "Quaternion Sphere" begin M = Sphere(2, ℍ) @test repr(M) == "Sphere(2, ℍ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int},ℍ} + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{3}},ℍ} @test representation_size(M) == (3,) p = [Quaternion(1.0), Quaternion(0, 1.0, 0, 0), Quaternion(0.0, 0.0, -1.0, 0.0)] q = project(M, p) @@ -168,8 +168,8 @@ using ManifoldsBase: TFVector @testset "Array Sphere" begin M = ArraySphere(2, 2; field=ℝ) - @test repr(M) == "ArraySphere(2, 2; field = ℝ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(M) == "ArraySphere(2, 2; field=ℝ)" + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{2,2}},ℝ} @test representation_size(M) == (2, 2) p = ones(2, 2) q = project(M, p) @@ -179,8 +179,8 @@ using ManifoldsBase: TFVector @test is_vector(M, q, X) M = ArraySphere(2, 2; field=ℂ) - @test repr(M) == "ArraySphere(2, 2; field = ℂ)" - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℂ} + @test repr(M) == "ArraySphere(2, 2; field=ℂ)" + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{2,2}},ℂ} @test representation_size(M) == (2, 2) end @@ -283,9 +283,11 @@ using ManifoldsBase: TFVector @test volume_density(M, p, [0.0, 0.5, 0.5]) ≈ 0.9187253698655684 end - @testset "static parameter" begin - @test repr(Sphere(2; parameter=:type)) == "Sphere(2, ℝ; parameter=:type)" - @test repr(ArraySphere(2, 3; parameter=:type)) == - "ArraySphere(2, 3; field = ℝ, parameter=:type)" + @testset "field parameter" begin + M = Sphere(2; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int},ℝ} + @test repr(M) == "Sphere(2, ℝ; parameter=:field)" + @test repr(ArraySphere(2, 3; parameter=:field)) == + "ArraySphere(2, 3; field=ℝ, parameter=:field)" end end diff --git a/test/manifolds/sphere_symmetric_matrices.jl b/test/manifolds/sphere_symmetric_matrices.jl index 9710acd478..f5d63f5a6f 100644 --- a/test/manifolds/sphere_symmetric_matrices.jl +++ b/test/manifolds/sphere_symmetric_matrices.jl @@ -15,7 +15,7 @@ include("../utils.jl") @test representation_size(M) == (3, 3) @test base_manifold(M) === M @test !is_flat(M) - @test typeof(get_embedding(M)) === ArraySphere{Tuple{Int,Int},ℝ} + @test typeof(get_embedding(M)) === ArraySphere{TypeParameter{Tuple{3,3}},ℝ} @test check_point(M, A) === nothing @test_throws ManifoldDomainError is_point(M, B, true) @test_throws ManifoldDomainError is_point(M, C, true) @@ -74,4 +74,9 @@ include("../utils.jl") test_inplace=true, ) end + @testset "field parameter" begin + M = SphereSymmetricMatrices(3; parameter=:field) + @test repr(M) == "SphereSymmetricMatrices(3, ℝ; parameter=:field)" + @test typeof(get_embedding(M)) === ArraySphere{Tuple{Int,Int},ℝ} + end end diff --git a/test/manifolds/stiefel.jl b/test/manifolds/stiefel.jl index 3f458b328c..2daa966b0b 100644 --- a/test/manifolds/stiefel.jl +++ b/test/manifolds/stiefel.jl @@ -2,10 +2,10 @@ include("../utils.jl") @testset "Stiefel" begin @testset "Real" begin - M = Stiefel(3, 2; parameter=:type) + M = Stiefel(3, 2) M2 = MetricManifold(M, EuclideanMetric()) @testset "Basics" begin - @test repr(M) == "Stiefel(3, 2, ℝ; parameter=:type)" + @test repr(M) == "Stiefel(3, 2, ℝ)" p = [1.0 0.0; 0.0 1.0; 0.0 0.0] @test is_default_metric(M, EuclideanMetric()) @test representation_size(M) == (3, 2) @@ -206,9 +206,9 @@ include("../utils.jl") end @testset "Complex" begin - M = Stiefel(3, 2, ℂ; parameter=:type) + M = Stiefel(3, 2, ℂ) @testset "Basics" begin - @test repr(M) == "Stiefel(3, 2, ℂ; parameter=:type)" + @test repr(M) == "Stiefel(3, 2, ℂ)" @test representation_size(M) == (3, 2) @test manifold_dimension(M) == 8 @test !is_flat(M) diff --git a/test/manifolds/symmetric.jl b/test/manifolds/symmetric.jl index c943000ca6..c586a3d347 100644 --- a/test/manifolds/symmetric.jl +++ b/test/manifolds/symmetric.jl @@ -17,7 +17,7 @@ include("../utils.jl") @test representation_size(M) == (3, 3) @test base_manifold(M) === M @test is_flat(M) - @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{3,3}},ℝ} @test check_point(M, B_sym) === nothing @test_throws DomainError is_point(M, A, true) @test_throws ManifoldDomainError is_point(M, C, true) @@ -85,4 +85,8 @@ include("../utils.jl") @test isapprox(-pts[1], exp(M, pts[1], log(M, pts[1], -pts[1]))) end # testset type $T end # for + @testset "field parameter" begin + M = SymmetricMatrices(3, ℝ; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + end end # test SymmetricMatrices diff --git a/test/manifolds/symmetric_positive_definite.jl b/test/manifolds/symmetric_positive_definite.jl index 0dc0a1854f..c08f1d2bd0 100644 --- a/test/manifolds/symmetric_positive_definite.jl +++ b/test/manifolds/symmetric_positive_definite.jl @@ -303,4 +303,8 @@ include("../utils.jl") ] @test volume_density(M, p, X) ≈ 5.141867280770719 end + @testset "field parameter" begin + M = SymmetricPositiveDefinite(3; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + end end diff --git a/test/manifolds/symplectic.jl b/test/manifolds/symplectic.jl index 894d622084..fb3e279993 100644 --- a/test/manifolds/symplectic.jl +++ b/test/manifolds/symplectic.jl @@ -74,7 +74,7 @@ using ManifoldDiff ] @testset "Basics" begin - @test repr(Sp_2) == "Symplectic{$(2), ℝ}()" + @test repr(Sp_2) == "Symplectic($(2), ℝ)" @test representation_size(Sp_2) == (2, 2) @test base_manifold(Sp_2) === Sp_2 @test !is_flat(Sp_2) @@ -154,7 +154,7 @@ using ManifoldDiff # Project Project matrix A ∈ ℝ^{2 × 2} onto (T_pSp): A_2 = [5.0 -21.5; 3.14 14.9] A_2_proj = similar(A_2) - Manifolds.project!(Extended_Sp_2, A_2_proj, p_2, A_2) + project!(Extended_Sp_2, A_2_proj, p_2, A_2) @test is_vector(Sp_2, p_2, A_2_proj; atol=1.0e-16) # Change representer of A onto T_pSp: diff --git a/test/manifolds/symplecticstiefel.jl b/test/manifolds/symplecticstiefel.jl index ff4563a914..63365d864f 100644 --- a/test/manifolds/symplecticstiefel.jl +++ b/test/manifolds/symplecticstiefel.jl @@ -137,7 +137,7 @@ end ] @testset "Basics" begin - @test repr(SpSt_6_4) == "SymplecticStiefel{6, 4, ℝ}()" + @test repr(SpSt_6_4) == "SymplecticStiefel(6, 4, ℝ)" @test representation_size(SpSt_6_4) == (6, 4) @test base_manifold(SpSt_6_4) === SpSt_6_4 @test get_total_space(SpSt_6_4) == Symplectic(6) diff --git a/test/manifolds/unitary_matrices.jl b/test/manifolds/unitary_matrices.jl index 3345f116f3..2d78d4cdb6 100644 --- a/test/manifolds/unitary_matrices.jl +++ b/test/manifolds/unitary_matrices.jl @@ -5,8 +5,8 @@ using Quaternions @testset "Orthogonal Matrices" begin M = OrthogonalMatrices(3) @test repr(M) == "OrthogonalMatrices(3)" - @test repr(OrthogonalMatrices(3; parameter=:type)) == - "OrthogonalMatrices(3; parameter=:type)" + @test repr(OrthogonalMatrices(3; parameter=:field)) == + "OrthogonalMatrices(3; parameter=:field)" @test injectivity_radius(M, PolarRetraction()) == π / sqrt(2.0) @test manifold_dimension(M) == 3 @test injectivity_radius(M) == π * sqrt(2.0) @@ -24,7 +24,8 @@ end @testset "Unitary Matrices" begin M = UnitaryMatrices(2) @test repr(M) == "UnitaryMatrices(2)" - @test repr(UnitaryMatrices(2; parameter=:type)) == "UnitaryMatrices(2; parameter=:type)" + @test repr(UnitaryMatrices(2; parameter=:field)) == + "UnitaryMatrices(2; parameter=:field)" @test manifold_dimension(M) == 4 @test !is_flat(M) @test injectivity_radius(M) == π @@ -76,9 +77,10 @@ end end @testset "Quaternionic Unitary Matrices" begin - M = UnitaryMatrices(1, ℍ; parameter=:type) - @test repr(M) == "UnitaryMatrices(1, ℍ; parameter=:type)" - @test repr(UnitaryMatrices(1, ℍ)) == "UnitaryMatrices(1, ℍ)" + M = UnitaryMatrices(1, ℍ) + @test repr(M) == "UnitaryMatrices(1, ℍ)" + @test repr(UnitaryMatrices(1, ℍ; parameter=:field)) == + "UnitaryMatrices(1, ℍ; parameter=:field)" @test manifold_dimension(M) == 3 @test injectivity_radius(M) == π @test !is_flat(M) diff --git a/test/recipes.jl b/test/recipes.jl index 831dcbda67..f595fa3044 100644 --- a/test/recipes.jl +++ b/test/recipes.jl @@ -10,7 +10,7 @@ include("utils.jl") ENV["GKSwstype"] = "100" gr() # function Hyp2PB_plot() - M = Hyperbolic(2; parameter=:type) + M = Hyperbolic(2) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) p2 = convert.(Ref(PoincareBallPoint), p) # return @@ -19,7 +19,7 @@ include("utils.jl") # @plottest Hyp2PB_plot joinpath(references_folder, "Hyp2PBPlot.png") false # function Hyp2PB_plot_geo() - M = Hyperbolic(2; parameter=:type) + M = Hyperbolic(2) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) p2 = convert.(Ref(PoincareBallPoint), p) # return @@ -28,7 +28,7 @@ include("utils.jl") # @plottest Hyp2PB_plot_geo joinpath(references_folder, "Hyp2PBPlotGeo.png") false # function Hyp2PB_quiver() - M = Hyperbolic(2; parameter=:type) + 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])] @@ -38,7 +38,7 @@ include("utils.jl") # @plottest Hyp2PB_quiver joinpath(references_folder, "Hyp2PBQuiver.png") false # function Hyp2PH_plot() - M = Hyperbolic(2; parameter=:type) + M = Hyperbolic(2) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) p2 = convert.(Ref(PoincareHalfSpacePoint), p) # return @@ -47,7 +47,7 @@ include("utils.jl") # @plottest Hyp2PH_plot joinpath(references_folder, "Hyp2PHPlot.png") false # function Hyp2PH_plot_geo() - M = Hyperbolic(2; parameter=:type) + M = Hyperbolic(2) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) p2 = convert.(Ref(PoincareHalfSpacePoint), p) # return @@ -56,7 +56,7 @@ include("utils.jl") # @plottest Hyp2PH_plot_geo joinpath(references_folder, "Hyp2PHPlotGeo.png") false # function Hyp2PH_quiver() - M = Hyperbolic(2; parameter=:type) + 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])] @@ -69,7 +69,7 @@ include("utils.jl") ENV["GKSwstype"] = "100" gr() # function Hyp2_plot() - M = Hyperbolic(2; parameter=:type) + M = Hyperbolic(2) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) # return plot(M, p) @@ -77,7 +77,7 @@ include("utils.jl") # @plottest Hyp2_plot joinpath(references_folder, "Hyp2Plot.png") false # function Hyp2_surfplot() - M = Hyperbolic(2; parameter=:type) + M = Hyperbolic(2) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) # return plot(M, p; surface=true) @@ -85,7 +85,7 @@ include("utils.jl") #@plottest Hyp2_surfplot joinpath(references_folder, "Hyp2SurfPlot.png") false # function Hyp2_plot_geo() - M = Hyperbolic(2; parameter=:type) + M = Hyperbolic(2) p = Manifolds._hyperbolize.(Ref(M), [[1.0, 0.0], [0.0, 1.0]]) # return plot(M, p; geodesic_interpolation=80) @@ -93,7 +93,7 @@ include("utils.jl") #@plottest Hyp2_plot_geo joinpath(references_folder, "Hyp2PlotGeo.png") false # function Hyp2_quiver() - M = Hyperbolic(2; parameter=:type) + 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 @@ -104,7 +104,7 @@ include("utils.jl") @testset "3D Recipes in pythonplot" begin pythonplot() # function Sphere2_plot() - M = Sphere(2; parameter=:type) + 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) @@ -112,7 +112,7 @@ include("utils.jl") # @plottest Sphere2_plot joinpath(references_folder, "Sphere2Plot.png") false # function Sphere2_surfplot() - M = Sphere(2; parameter=:type) + 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) @@ -120,7 +120,7 @@ include("utils.jl") # @plottest Sphere2_surfplot joinpath(references_folder, "Sphere2SurfPlot.png") false # function Sphere2_plot_geo() - M = Sphere(2; parameter=:type) + 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) @@ -129,7 +129,7 @@ include("utils.jl") # function Sphere2_quiver() pythonplot() - M = Sphere(2; parameter=:type) + 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)) From 999d0de98354892fa678d7ad44ed1b1941d77743 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 12 Sep 2023 18:12:35 +0200 Subject: [PATCH 23/81] remove get_mn --- src/manifolds/CenteredMatrices.jl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/manifolds/CenteredMatrices.jl b/src/manifolds/CenteredMatrices.jl index d6c53fd160..0856c49a52 100644 --- a/src/manifolds/CenteredMatrices.jl +++ b/src/manifolds/CenteredMatrices.jl @@ -36,7 +36,7 @@ zero. The tolerance for the column sums of `p` can be set using `kwargs...`. """ function check_point(M::CenteredMatrices, p; kwargs...) - m, n = get_mn(M) + m, n = get_parameter(M.size) if !isapprox(sum(p, dims=1), zeros(1, n); kwargs...) return DomainError( p, @@ -57,7 +57,7 @@ sum to zero and its values are from the correct [`AbstractNumbers`](https://juli The tolerance for the column sums of `p` and `X` can be set using `kwargs...`. """ function check_vector(M::CenteredMatrices, p, X; kwargs...) - m, n = get_mn(M) + m, n = get_parameter(M.size) if !isapprox(sum(X, dims=1), zeros(1, n); kwargs...) return DomainError( X, @@ -74,13 +74,10 @@ function get_embedding(::CenteredMatrices{TypeParameter{Tuple{m,n}},𝔽}) where return Euclidean(m, n; field=𝔽) end function get_embedding(M::CenteredMatrices{Tuple{Int,Int},𝔽}) where {𝔽} - m, n = get_mn(M) + m, n = get_parameter(M.size) return Euclidean(m, n; field=𝔽, parameter=:field) end -get_mn(::CenteredMatrices{TypeParameter{Tuple{m,n}}}) where {m,n} = (m, n) -get_mn(M::CenteredMatrices{Tuple{Int,Int}}) = get_parameter(M.size) - """ is_flat(::CenteredMatrices) @@ -100,7 +97,7 @@ Return the manifold dimension of the [`CenteredMatrices`](@ref) `m`-by-`n` matri where $\dim_ℝ 𝔽$ is the [`real_dimension`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.real_dimension-Tuple{ManifoldsBase.AbstractNumbers}) of `𝔽`. """ function manifold_dimension(M::CenteredMatrices{<:Any,𝔽}) where {𝔽} - m, n = get_mn(M) + m, n = get_parameter(M.size) return (m * n - n) * real_dimension(𝔽) end @@ -140,13 +137,13 @@ project(::CenteredMatrices, ::Any, ::Any) project!(::CenteredMatrices, Y, p, X) = (Y .= X .- mean(X, dims=1)) -representation_size(M::CenteredMatrices) = get_mn(M) +representation_size(M::CenteredMatrices) = get_parameter(M.size) function Base.show(io::IO, ::CenteredMatrices{TypeParameter{Tuple{m,n}},𝔽}) where {m,n,𝔽} return print(io, "CenteredMatrices($(m), $(n), $(𝔽))") end function Base.show(io::IO, M::CenteredMatrices{Tuple{Int,Int},𝔽}) where {𝔽} - m, n = get_mn(M) + m, n = get_parameter(M.size) return print(io, "CenteredMatrices($(m), $(n), $(𝔽); parameter=:field)") end From 49fa34da0f0c0ca2b119635d7e5dc28704968d1f Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 12 Sep 2023 19:28:12 +0200 Subject: [PATCH 24/81] add some comments, remove old code --- src/product_representations.jl | 6 ++---- src/trait_recursion_breaking.jl | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/product_representations.jl b/src/product_representations.jl index 095d714a15..669d4b1066 100644 --- a/src/product_representations.jl +++ b/src/product_representations.jl @@ -1,9 +1,9 @@ @doc raw""" submanifold_component(M::AbstractManifold, p, i::Integer) - submanifold_component(M::AbstractManifold, p, ::Val(i)) where {i} + submanifold_component(M::AbstractManifold, p, ::Val{i}) where {i} submanifold_component(p, i::Integer) - submanifold_component(p, ::Val(i)) where {i} + submanifold_component(p, ::Val{i}) where {i} Project the product array `p` on `M` to its `i`th component. A new array is returned. """ @@ -12,7 +12,6 @@ submanifold_component(::Any...) return submanifold_component(M, p, Val(i)) end @inline submanifold_component(M::AbstractManifold, p, i::Val) = submanifold_component(p, i) -@inline submanifold_component(p, ::Val{I}) where {I} = p.parts[I] @inline submanifold_component(p::ArrayPartition, ::Val{I}) where {I} = p.x[I] @inline submanifold_component(p, i::Integer) = submanifold_component(p, Val(i)) @@ -24,7 +23,6 @@ Get the projected components of `p` on the submanifolds of `M`. The components a """ submanifold_components(::Any...) @inline submanifold_components(::AbstractManifold, p) = submanifold_components(p) -@inline submanifold_components(p) = p.parts @inline submanifold_components(p::ArrayPartition) = p.x ## ArrayPartition diff --git a/src/trait_recursion_breaking.jl b/src/trait_recursion_breaking.jl index 611f01ae90..451417ea2c 100644 --- a/src/trait_recursion_breaking.jl +++ b/src/trait_recursion_breaking.jl @@ -1,5 +1,6 @@ # An unfortunate consequence of Julia's method recursion limitations +# (the code below makes some calls of `isapprox` faster) for trait_type in [ TraitList{<:IsDefaultMetric}, From 52f87241aed8b38aba0c47131a050073ec390c58 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 13 Sep 2023 14:24:05 +0200 Subject: [PATCH 25/81] bump tolerance --- test/groups/power_group.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/groups/power_group.jl b/test/groups/power_group.jl index 164196e39b..30525c8466 100644 --- a/test/groups/power_group.jl +++ b/test/groups/power_group.jl @@ -30,7 +30,7 @@ include("group_utils.jl") pts, X_pts, X_pts; - atol=1e-9, + atol=2e-9, test_diff=true, test_log_from_identity=true, test_exp_from_identity=true, From 73aadae09f99f0016f49685f9621aa605d5e6c4f Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 13 Sep 2023 16:05:45 +0200 Subject: [PATCH 26/81] fix test --- test/manifolds/rotations.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/manifolds/rotations.jl b/test/manifolds/rotations.jl index a6a403aeed..5207ae3538 100644 --- a/test/manifolds/rotations.jl +++ b/test/manifolds/rotations.jl @@ -163,7 +163,7 @@ include("../utils.jl") ) p = exp(SOn, pts[1], X) X2 = log(SOn, pts[1], p) - @test distance(SOn, p, exp(SOn, pts[1], X2)) < 20 * eps() + @test distance(SOn, p, exp(SOn, pts[1], X2)) < 25 * eps() end end @testset "Test AbstractManifold Point and Tangent Vector checks" begin From 59d98e0b6a6b006fa9152e612a265e4d6710ccc7 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Thu, 14 Sep 2023 13:32:46 +0200 Subject: [PATCH 27/81] improve code coverage --- docs/src/manifolds/sphere.md | 2 +- src/groups/special_orthogonal.jl | 2 +- src/groups/translation_group.jl | 4 ++-- src/groups/unitary.jl | 4 ++-- src/manifolds/Euclidean.jl | 6 +++--- src/manifolds/FixedRankMatrices.jl | 2 +- src/manifolds/GeneralUnitaryMatrices.jl | 12 +++++------ src/manifolds/Grassmann.jl | 2 +- src/manifolds/KendallsPreShapeSpace.jl | 8 +++++++ src/manifolds/KendallsShapeSpace.jl | 8 +++++++ src/manifolds/ProjectiveSpace.jl | 10 ++++----- src/manifolds/Spectrahedron.jl | 8 +++++-- src/manifolds/Stiefel.jl | 2 +- src/manifolds/StiefelCanonicalMetric.jl | 4 ++-- src/manifolds/StiefelEuclideanMetric.jl | 2 +- src/manifolds/StiefelSubmersionMetric.jl | 6 +++--- src/manifolds/Tucker.jl | 3 ++- test/groups/general_linear.jl | 5 +++++ test/groups/general_unitary_groups.jl | 14 +++++++++++++ test/groups/heisenberg.jl | 5 +++++ test/groups/special_linear.jl | 5 +++++ test/manifolds/centered_matrices.jl | 5 +++++ test/manifolds/cholesky_space.jl | 4 ++++ test/manifolds/elliptope.jl | 5 +++++ test/manifolds/euclidean.jl | 21 +++++++++++++------ test/manifolds/fixed_rank.jl | 5 +++++ test/manifolds/flag.jl | 5 +++++ test/manifolds/generalized_grassmann.jl | 8 +++++++ test/manifolds/generalized_stiefel.jl | 8 +++++++ test/manifolds/graph.jl | 4 ++-- test/manifolds/grassmann.jl | 10 +++++++++ test/manifolds/hyperbolic.jl | 10 +++++++++ .../multinomial_doubly_stochastic.jl | 5 +++++ test/manifolds/multinomial_matrices.jl | 3 +++ test/manifolds/multinomial_symmetric.jl | 5 +++++ test/manifolds/oblique.jl | 3 +++ test/manifolds/probability_simplex.jl | 8 +++++++ test/manifolds/product_manifold.jl | 4 ++-- test/manifolds/projective_space.jl | 12 +++++++++-- test/manifolds/rotations.jl | 6 ++++++ test/manifolds/shape_space.jl | 17 +++++++++++++++ test/manifolds/skewhermitian.jl | 3 +++ test/manifolds/spd_fixed_determinant.jl | 6 ++++++ test/manifolds/spectrahedron.jl | 5 +++++ test/manifolds/sphere.jl | 2 ++ test/manifolds/stiefel.jl | 5 +++++ ...metric_positive_semidefinite_fixed_rank.jl | 5 +++++ test/manifolds/symplectic.jl | 5 +++++ test/manifolds/symplecticstiefel.jl | 6 ++++++ test/manifolds/tucker.jl | 5 +++++ test/manifolds/unitary_matrices.jl | 1 + test/metric.jl | 2 +- test/statistics.jl | 1 + 53 files changed, 263 insertions(+), 45 deletions(-) diff --git a/docs/src/manifolds/sphere.md b/docs/src/manifolds/sphere.md index 8db38c7e1d..3153ffddc5 100644 --- a/docs/src/manifolds/sphere.md +++ b/docs/src/manifolds/sphere.md @@ -11,7 +11,7 @@ Sphere ``` For the higher-dimensional arrays, for example unit (Frobenius) norm matrices, the manifold is generated using the size of the matrix. -To create the unit sphere of $3×2$ real-valued matrices, write `ArraySphere(3,2)` and the complex case is done – as for the [`Euclidean`](@ref) case – with an keyword argument `ArraySphere(3,2; field = ℂ)`. This case also covers the classical sphere as a special case, but you specify the size of the vectors/embedding instead: The 2-sphere can here be generated `ArraySphere(3)`. +To create the unit sphere of $3×2$ real-valued matrices, write `ArraySphere(3,2)` and the complex case is done – as for the [`Euclidean`](@ref) case – with an keyword argument `ArraySphere(3,2; field=ℂ)`. This case also covers the classical sphere as a special case, but you specify the size of the vectors/embedding instead: The 2-sphere can here be generated `ArraySphere(3)`. ```@docs ArraySphere diff --git a/src/groups/special_orthogonal.jl b/src/groups/special_orthogonal.jl index 3cb7c947dc..2baf90822f 100644 --- a/src/groups/special_orthogonal.jl +++ b/src/groups/special_orthogonal.jl @@ -19,6 +19,6 @@ function Base.show(io::IO, ::SpecialOrthogonal{TypeParameter{Tuple{n}}}) where { return print(io, "SpecialOrthogonal($(n))") end function Base.show(io::IO, M::SpecialOrthogonal{Tuple{Int}}) - n = get_parameter(M.size)[1] + n = get_parameter(M.manifold.size)[1] return print(io, "SpecialOrthogonal($(n); parameter=:field)") end diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index 8705703cf6..550648b1cc 100644 --- a/src/groups/translation_group.jl +++ b/src/groups/translation_group.jl @@ -4,10 +4,10 @@ Translation group $\mathrm{T}(n)$ represented by translation arrays. # Constructor - TranslationGroup(n₁,...,nᵢ; field = 𝔽, parameter::Symbol=:type) + TranslationGroup(n₁,...,nᵢ; field=𝔽, parameter::Symbol=:type) Generate the translation group on -$𝔽^{n₁,…,nᵢ}$ = `Euclidean(n₁,...,nᵢ; field = 𝔽)`, which is isomorphic to the group itself. +$𝔽^{n₁,…,nᵢ}$ = `Euclidean(n₁,...,nᵢ; field=𝔽)`, which is isomorphic to the group itself. """ const TranslationGroup{T,𝔽} = GroupManifold{𝔽,Euclidean{T,𝔽},AdditionOperation} diff --git a/src/groups/unitary.jl b/src/groups/unitary.jl index 679ed39611..d775f401ef 100644 --- a/src/groups/unitary.jl +++ b/src/groups/unitary.jl @@ -103,13 +103,13 @@ function Base.show(io::IO, ::Unitary{TypeParameter{Tuple{n}},ℂ}) where {n} return print(io, "Unitary($(n))") end function Base.show(io::IO, M::Unitary{Tuple{Int},ℂ}) - n = get_parameter(M.size)[1] + n = get_parameter(M.manifold.size)[1] return print(io, "Unitary($(n); parameter=:field)") end function Base.show(io::IO, ::Unitary{TypeParameter{Tuple{n}},ℍ}) where {n} return print(io, "Unitary($(n), ℍ)") end function Base.show(io::IO, M::Unitary{Tuple{Int},ℍ}) - n = get_parameter(M.size)[1] + n = get_parameter(M.manifold.size)[1] return print(io, "Unitary($(n), ℍ; parameter=:field)") end diff --git a/src/manifolds/Euclidean.jl b/src/manifolds/Euclidean.jl index 3b78200001..c5173c21c0 100644 --- a/src/manifolds/Euclidean.jl +++ b/src/manifolds/Euclidean.jl @@ -307,7 +307,7 @@ function get_vector_orthonormal( # probably doesn't need rewrapping in SArray return c end -function get_vector_orthonormal( +function Manifolds.get_vector_orthonormal( ::Euclidean{TypeParameter{Tuple{N}}}, ::SizedArray{S}, c, @@ -700,11 +700,11 @@ end function Base.show(io::IO, M::Euclidean{N,𝔽}) where {N<:Tuple,𝔽} size = get_parameter(M.size) - return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽), parameter = :field)") + return print(io, "Euclidean($(join(size, ", ")); field=$(𝔽), parameter=:field)") end function Base.show(io::IO, M::Euclidean{N,𝔽}) where {N<:TypeParameter,𝔽} size = get_parameter(M.size) - return print(io, "Euclidean($(join(size, ", ")); field = $(𝔽))") + return print(io, "Euclidean($(join(size, ", ")); field=$(𝔽))") end # # Vector Transport diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index 0e8091c86e..9b09232b1c 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -549,7 +549,7 @@ function retract_polar!( QV, RV = qr([p.Vt' tX.Vt']) # Compute T = svd(RU * [diagm(p.S) + X.M I; I zeros(k, k)] * RV') - @views begin + @views begin # COV_EXCL_LINE RU11 = RU[:, 1:k] RU12 = RU[:, (k + 1):(2 * k)] RV11 = RV[:, 1:k] diff --git a/src/manifolds/GeneralUnitaryMatrices.jl b/src/manifolds/GeneralUnitaryMatrices.jl index 8f4441befe..e7359f3acc 100644 --- a/src/manifolds/GeneralUnitaryMatrices.jl +++ b/src/manifolds/GeneralUnitaryMatrices.jl @@ -1017,14 +1017,14 @@ function riemann_tensor!(::GeneralUnitaryMatrices, Xresult, p, X, Y, Z) end @doc raw""" - volume_density(M::GeneralUnitaryMatrices{n,ℝ}, p, X) where {n} + volume_density(M::GeneralUnitaryMatrices{<:Any,ℝ}, p, X) Compute volume density function of a sphere, i.e. determinant of the differential of exponential map `exp(M, p, X)`. It is derived from Eq. (4.1) and Corollary 4.4 in [ChevallierLiLuDunson:2022](@ref). See also Theorem 4.1 in [FalorsideHaanDavidsonForre:2019](@cite), (note that it uses a different convention). """ -function volume_density(M::GeneralUnitaryMatrices{n,ℝ}, p, X) where {n} +function volume_density(M::GeneralUnitaryMatrices{<:Any,ℝ}, p, X) dens = one(eltype(X)) B = get_basis(M, p, DefaultOrthonormalBasis()) Ys = get_vectors(M, p, B) @@ -1046,7 +1046,7 @@ function volume_density(M::GeneralUnitaryMatrices{n,ℝ}, p, X) where {n} end @doc raw""" - volume_density(M::GeneralUnitaryMatrices{3,ℝ}, p, X) + volume_density(M::GeneralUnitaryMatrices{TypeParameter{Tuple{3}},ℝ}, p, X) Compute the volume density on O(3)/SO(3). The formula reads [FalorsideHaanDavidsonForre:2019](@cite) @@ -1054,7 +1054,7 @@ Compute the volume density on O(3)/SO(3). The formula reads [FalorsideHaanDavids \frac{1-1\cos(\sqrt{2}\lVert X \rVert)}{\lVert X \rVert^2}. ``` """ -function volume_density(M::GeneralUnitaryMatrices{3,ℝ}, p, X) +function volume_density(M::GeneralUnitaryMatrices{TypeParameter{Tuple{3}},ℝ}, p, X) nX = norm(M, p, X) if nX > eps(eltype(X)) return (1 - 1 * cos(sqrt(2) * nX)) / nX^2 @@ -1064,10 +1064,10 @@ function volume_density(M::GeneralUnitaryMatrices{3,ℝ}, p, X) end @doc raw""" - volume_density(M::GeneralUnitaryMatrices{2,ℝ}, p, X) + volume_density(M::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, p, X) Volume density on O(2)/SO(2) is equal to 1. """ -function volume_density(::GeneralUnitaryMatrices{2,ℝ}, p, X) +function volume_density(::GeneralUnitaryMatrices{TypeParameter{Tuple{2}},ℝ}, p, X) return one(eltype(X)) end diff --git a/src/manifolds/Grassmann.jl b/src/manifolds/Grassmann.jl index 5ef3e7b214..c8f23a7612 100644 --- a/src/manifolds/Grassmann.jl +++ b/src/manifolds/Grassmann.jl @@ -68,7 +68,7 @@ A good overview can be found in[BendokatZimmermannAbsil:2020](@cite). Grassmann(n, k, field=ℝ, parameter::Symbol=:type) Generate the Grassmann manifold $\operatorname{Gr}(n,k)$, where the real-valued -case `field = ℝ` is the default. +case `field=ℝ` is the default. """ struct Grassmann{T,𝔽} <: AbstractDecoratorManifold{𝔽} size::T diff --git a/src/manifolds/KendallsPreShapeSpace.jl b/src/manifolds/KendallsPreShapeSpace.jl index fe8de11a2d..3a34719ad9 100644 --- a/src/manifolds/KendallsPreShapeSpace.jl +++ b/src/manifolds/KendallsPreShapeSpace.jl @@ -145,3 +145,11 @@ function Random.rand!( end return pX end + +function Base.show(io::IO, ::KendallsPreShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} + return print(io, "KendallsPreShapeSpace($n, $k)") +end +function Base.show(io::IO, M::KendallsPreShapeSpace{Tuple{Int,Int}}) + n, k = get_parameter(M.size) + return print(io, "KendallsPreShapeSpace($n, $k; parameter=:field)") +end diff --git a/src/manifolds/KendallsShapeSpace.jl b/src/manifolds/KendallsShapeSpace.jl index ff023efba6..0c3a76b698 100644 --- a/src/manifolds/KendallsShapeSpace.jl +++ b/src/manifolds/KendallsShapeSpace.jl @@ -198,3 +198,11 @@ function Random.rand!( rand!(rng, get_embedding(M), pX; vector_at=vector_at, σ=σ) return pX end + +function Base.show(io::IO, ::KendallsShapeSpace{TypeParameter{Tuple{n,k}}}) where {n,k} + return print(io, "KendallsShapeSpace($n, $k)") +end +function Base.show(io::IO, M::KendallsShapeSpace{Tuple{Int,Int}}) + n, k = get_parameter(M.size) + return print(io, "KendallsShapeSpace($n, $k; parameter=:field)") +end diff --git a/src/manifolds/ProjectiveSpace.jl b/src/manifolds/ProjectiveSpace.jl index 29b63729af..8d1eb2ca77 100644 --- a/src/manifolds/ProjectiveSpace.jl +++ b/src/manifolds/ProjectiveSpace.jl @@ -140,6 +140,9 @@ end function decorated_manifold(M::AbstractProjectiveSpace{𝔽}) where {𝔽} return Euclidean(representation_size(M)...; field=𝔽) end +function decorated_manifold(M::ProjectiveSpace{<:Tuple,𝔽}) where {𝔽} + return Euclidean(representation_size(M)...; field=𝔽, parameter=:field) +end get_embedding(M::AbstractProjectiveSpace) = decorated_manifold(M) @@ -490,14 +493,11 @@ function Base.show(io::IO, M::ProjectiveSpace{Tuple{Int},𝔽}) where {𝔽} return print(io, "ProjectiveSpace($(n), $(𝔽); parameter=:field)") end function Base.show(io::IO, ::ArrayProjectiveSpace{TypeParameter{tn},𝔽}) where {tn<:Tuple,𝔽} - return print(io, "ArrayProjectiveSpace($(join(tn.parameters, ", ")); field = $(𝔽))") + return print(io, "ArrayProjectiveSpace($(join(tn.parameters, ", ")); field=$(𝔽))") end function Base.show(io::IO, M::ArrayProjectiveSpace{<:Tuple,𝔽}) where {𝔽} n = M.size - return print( - io, - "ArrayProjectiveSpace($(join(n, ", ")); field = $(𝔽), parameter=:field)", - ) + return print(io, "ArrayProjectiveSpace($(join(n, ", ")); field=$(𝔽), parameter=:field)") end """ diff --git a/src/manifolds/Spectrahedron.jl b/src/manifolds/Spectrahedron.jl index e36a63f5f0..ed7fb88479 100644 --- a/src/manifolds/Spectrahedron.jl +++ b/src/manifolds/Spectrahedron.jl @@ -97,8 +97,12 @@ function check_vector(M::Spectrahedron, q, Y; kwargs...) return nothing end -function get_embedding(M::Spectrahedron) - return Euclidean(representation_size(M)...; field=ℝ) +function get_embedding(::Spectrahedron{TypeParameter{Tuple{n,k}}}) where {n,k} + return Euclidean(n, k) +end +function get_embedding(M::Spectrahedron{Tuple{Int,Int}}) + n, k = get_parameter(M.size) + return Euclidean(n, k; parameter=:field) end """ diff --git a/src/manifolds/Stiefel.jl b/src/manifolds/Stiefel.jl index 454871a9ef..e17da0546a 100644 --- a/src/manifolds/Stiefel.jl +++ b/src/manifolds/Stiefel.jl @@ -27,7 +27,7 @@ The manifold is named after [Eduard L. Stiefel](https://en.wikipedia.org/wiki/Eduard_Stiefel) (1909–1978). # Constructor - Stiefel(n, k, field = ℝ; parameter::Symbol=:type) + Stiefel(n, k, field=ℝ; parameter::Symbol=:type) Generate the (real-valued) Stiefel manifold of $n × k$ dimensional orthonormal matrices. """ diff --git a/src/manifolds/StiefelCanonicalMetric.jl b/src/manifolds/StiefelCanonicalMetric.jl index e60a31a924..7e750c3f9d 100644 --- a/src/manifolds/StiefelCanonicalMetric.jl +++ b/src/manifolds/StiefelCanonicalMetric.jl @@ -72,7 +72,7 @@ function exp!(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},CanonicalMetric}, q, p, n == k && return mul!(q, p, exp(A)) QR = qr(X - p * A) BC_ext = exp([A -QR.R'; QR.R 0*I]) - @views begin + @views begin # COV_EXCL_LINE mul!(q, p, BC_ext[1:k, 1:k]) mul!(q, Matrix(QR.Q), BC_ext[(k + 1):(2 * k), 1:k], true, true) end @@ -153,7 +153,7 @@ function inverse_retract!( V = allocate(qfact.Z, Size(2k, 2k)) LV = allocate(V) Zcompl = qr(qfact.Z).Q[1:(2k), (k + 1):(2k)] - @views begin + @views begin # COV_EXCL_LINE Vpcols = V[1:(2k), (k + 1):(2k)] #second half of the columns B = LV[(k + 1):(2k), 1:k] C = LV[(k + 1):(2k), (k + 1):(2k)] diff --git a/src/manifolds/StiefelEuclideanMetric.jl b/src/manifolds/StiefelEuclideanMetric.jl index f886d22c67..fa2e64518c 100644 --- a/src/manifolds/StiefelEuclideanMetric.jl +++ b/src/manifolds/StiefelEuclideanMetric.jl @@ -27,7 +27,7 @@ function exp!(M::Stiefel, q, p, X) n, k = get_parameter(M.size) A = p' * X B = exp([A -X'*X; I A]) - @views begin + @views begin # COV_EXCL_LINE r = p * B[1:k, 1:k] mul!(r, X, B[(k + 1):(2 * k), 1:k], true, true) end diff --git a/src/manifolds/StiefelSubmersionMetric.jl b/src/manifolds/StiefelSubmersionMetric.jl index 8dbe89d8e2..601db4d684 100644 --- a/src/manifolds/StiefelSubmersionMetric.jl +++ b/src/manifolds/StiefelSubmersionMetric.jl @@ -346,7 +346,7 @@ function stiefel_factorization(p, x) U = allocate(p, T, Size(n, 2k)) Z = allocate(p, T, Size(2k, k)) xfact = StiefelFactorization(U, Z) - @views begin + @views begin # COV_EXCL_LINE U1 = U[1:n, 1:k] U2 = U[1:n, (k + 1):(2k)] Z1 = Z[1:k, 1:k] @@ -442,7 +442,7 @@ function exp!( ) n, k = get_parameter(M.manifold.size) α = metric(M).α - @views begin + @views begin # COV_EXCL_LINE ZM = X.Z[1:k, 1:k] ZN = X.Z[(k + 1):(2k), 1:k] qM = q.Z[1:k, 1:k] @@ -450,7 +450,7 @@ function exp!( qM .= ZM .* (α / (α + 1)) D = exp(qM) C = allocate(D, Size(2k, 2k)) - @views begin + @views begin # COV_EXCL_LINE C[1:k, 1:k] .= ZM ./ (α + 1) C[1:k, (k + 1):(2k)] .= -ZN' C[(k + 1):(2k), 1:k] .= ZN diff --git a/src/manifolds/Tucker.jl b/src/manifolds/Tucker.jl index c823895fba..414d00e62c 100644 --- a/src/manifolds/Tucker.jl +++ b/src/manifolds/Tucker.jl @@ -36,7 +36,8 @@ where ``\mathcal{C}^\prime`` is arbitrary, ``U_d^{\mathrm{H}}`` is the Hermitian ``U_d``, and ``U_d^{\mathrm{H}} U_d^\prime = 0`` for all ``d``. # Constructor - Tucker(N::NTuple{D, Int}, R::NTuple{D, Int}[, field = ℝ]; parameter::Symbol=:type) + + Tucker(N::NTuple{D, Int}, R::NTuple{D, Int}[, field=ℝ]; parameter::Symbol=:type) Generate the manifold of `field`-valued tensors of dimensions `N[1] × … × N[D]` and multilinear rank `R = (R[1], …, R[D])`. diff --git a/test/groups/general_linear.jl b/test/groups/general_linear.jl index b41ca1abd4..2e0ac7b143 100644 --- a/test/groups/general_linear.jl +++ b/test/groups/general_linear.jl @@ -197,4 +197,9 @@ using NLsolve ) end end + @testset "field parameter" begin + G = GeneralLinear(3; parameter=:field) + @test typeof(get_embedding(G)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(G) == "GeneralLinear(3, ℝ; parameter=:field)" + end end diff --git a/test/groups/general_unitary_groups.jl b/test/groups/general_unitary_groups.jl index 10ab1e8a21..ed07437cd2 100644 --- a/test/groups/general_unitary_groups.jl +++ b/test/groups/general_unitary_groups.jl @@ -230,4 +230,18 @@ include("group_utils.jl") @test is_vector(Rotations(4), E, X3b) @test X3a[2, 3] ≈ π end + + @testset "field parameter" begin + G = Orthogonal(2; parameter=:field) + @test repr(G) == "Orthogonal(2; parameter=:field)" + + SU3 = SpecialUnitary(3; parameter=:field) + @test repr(SU3) == "SpecialUnitary(3; parameter=:field)" + + G = Unitary(3, ℂ; parameter=:field) + @test repr(G) == "Unitary(3; parameter=:field)" + + G = Unitary(3, ℍ; parameter=:field) + @test repr(G) == "Unitary(3, ℍ; parameter=:field)" + end end diff --git a/test/groups/heisenberg.jl b/test/groups/heisenberg.jl index 120bd43ab1..1e13d24387 100644 --- a/test/groups/heisenberg.jl +++ b/test/groups/heisenberg.jl @@ -55,4 +55,9 @@ include("group_utils.jl") test_rand_point=true, test_rand_tvector=true, ) + @testset "field parameter" begin + G = HeisenbergGroup(1; parameter=:field) + @test typeof(get_embedding(G)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(G) == "HeisenbergGroup(1; parameter=:field)" + end end diff --git a/test/groups/special_linear.jl b/test/groups/special_linear.jl index dc1651081f..d463223ee4 100644 --- a/test/groups/special_linear.jl +++ b/test/groups/special_linear.jl @@ -215,4 +215,9 @@ using NLsolve @test project(G, q, Y) ≈ Y end end + @testset "field parameter" begin + G = SpecialLinear(3; parameter=:field) + @test typeof(get_embedding(G)) === GeneralLinear{Tuple{Int64},ℝ} + @test repr(G) == "SpecialLinear(3, ℝ; parameter=:field)" + end end diff --git a/test/manifolds/centered_matrices.jl b/test/manifolds/centered_matrices.jl index 21c7a92a4e..994519eb5b 100644 --- a/test/manifolds/centered_matrices.jl +++ b/test/manifolds/centered_matrices.jl @@ -59,4 +59,9 @@ include("../utils.jl") test_inplace=true, ) end + @testset "field parameter" begin + M = CenteredMatrices(3, 2; parameter=:field) + @test repr(M) == "CenteredMatrices(3, 2, ℝ; parameter=:field)" + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + end end diff --git a/test/manifolds/cholesky_space.jl b/test/manifolds/cholesky_space.jl index 8a51258e41..f9ee129128 100644 --- a/test/manifolds/cholesky_space.jl +++ b/test/manifolds/cholesky_space.jl @@ -55,4 +55,8 @@ include("../utils.jl") @test_throws DomainError is_vector(M, pt4, pt3f, true) @test is_vector(M, pt4, pt2f) end + @testset "field parameter" begin + M = CholeskySpace(3; parameter=:field) + @test repr(M) == "CholeskySpace(3; parameter=:field)" + end end diff --git a/test/manifolds/elliptope.jl b/test/manifolds/elliptope.jl index 0498a1b108..e99850d4dc 100644 --- a/test/manifolds/elliptope.jl +++ b/test/manifolds/elliptope.jl @@ -51,4 +51,9 @@ include("../utils.jl") ) end end + @testset "field parameter" begin + M = Elliptope(4, 2; parameter=:field) + @test repr(M) == "Elliptope(4, 2; parameter=:field)" + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + end end diff --git a/test/manifolds/euclidean.jl b/test/manifolds/euclidean.jl index 89f873aae5..8e7742f94c 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 = ℝ)" - @test repr(Ec) == "Euclidean(3; field = ℂ)" - @test repr(EH) == "Euclidean(2, 3; field = ℍ)" + @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)" + @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 @@ -344,6 +344,14 @@ using FiniteDifferences SizedMatrix{2,2}([-1.0, -2.0, -3.0, -4.0]), DefaultOrthonormalBasis(), ) == SA[-1.0 -3.0; -2.0 -4.0] + + M1c = Euclidean(3, field=ℂ) + get_vector( + M1c, + SizedVector{3}([1.0im, 2.0, 4.0im]), + SizedVector{3}([-1.0, -3.0, -4.0im]), + DefaultOrthonormalBasis(), + ) == SA[-1.0, -3.0, -4.0im] end @testset "Euclidean(1)" begin @@ -404,6 +412,7 @@ using FiniteDifferences @test distance(M0s, 2.0, 4.0) == 2.0 @test log(M0s, 2.0, 4.0) == 2.0 @test manifold_dimension(M0s) == 1 + @test project(M0s, 4.0) == 4.0 @test project(M0s, 2.0, 4.0) == 4.0 @test retract(M0s, 2.0, 4.0) == 6.0 @test retract(M0s, 2.0, 4.0, ExponentialRetraction()) == 6.0 diff --git a/test/manifolds/fixed_rank.jl b/test/manifolds/fixed_rank.jl index e381bb4da8..969b5c1ba7 100644 --- a/test/manifolds/fixed_rank.jl +++ b/test/manifolds/fixed_rank.jl @@ -239,4 +239,9 @@ include("../utils.jl") H = [0.0 3.0; 0.0 4.0; 0.0 1.0] @test is_vector(M, p, riemannian_Hessian(M, p, G, H, X)) end + @testset "field parameter" begin + M = FixedRankMatrices(3, 2, 2; parameter=:field) + @test repr(M) == "FixedRankMatrices(3, 2, 2, ℝ; parameter=:field)" + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + end end diff --git a/test/manifolds/flag.jl b/test/manifolds/flag.jl index 2f08ba1838..fb4533c7e2 100644 --- a/test/manifolds/flag.jl +++ b/test/manifolds/flag.jl @@ -268,6 +268,11 @@ using Random @test retract(M, p1_ortho, X1_ortho, QRRetraction()).value ≈ retract(OrthogonalMatrices(5), p1_ortho.value, X1_ortho.value, QRRetraction()) + + @testset "field parameters" begin + M = Flag(5, 1, 2; parameter=:field) + @test get_embedding(M, p1_ortho) == OrthogonalMatrices(5; parameter=:field) + end end @testset "field parameters" begin diff --git a/test/manifolds/generalized_grassmann.jl b/test/manifolds/generalized_grassmann.jl index d7790f8b8d..c0206f8c9a 100644 --- a/test/manifolds/generalized_grassmann.jl +++ b/test/manifolds/generalized_grassmann.jl @@ -164,4 +164,12 @@ include("../utils.jl") end end end + @testset "field parameter" begin + B = [1.0 0.0 0.0; 0.0 4.0 0.0; 0.0 0.0 1.0] + M = GeneralizedGrassmann(3, 2, B; parameter=:field) + @test repr(M) == + "GeneralizedGrassmann(3, 2, [1.0 0.0 0.0; 0.0 4.0 0.0; 0.0 0.0 1.0], ℝ; parameter=:field)" + @test typeof(get_embedding(M)) === + GeneralizedStiefel{Tuple{Int64,Int64},ℝ,Matrix{Float64}} + end end diff --git a/test/manifolds/generalized_stiefel.jl b/test/manifolds/generalized_stiefel.jl index a1e6141ebd..efffa2bf90 100644 --- a/test/manifolds/generalized_stiefel.jl +++ b/test/manifolds/generalized_stiefel.jl @@ -134,4 +134,12 @@ include("../utils.jl") @test !is_flat(M) end end + + @testset "field parameter" begin + B = [1.0 0.0 0.0; 0.0 4.0 0.0; 0.0 0.0 1.0] + M = GeneralizedStiefel(3, 2, B; parameter=:field) + @test repr(M) == + "GeneralizedStiefel(3, 2, [1.0 0.0 0.0; 0.0 4.0 0.0; 0.0 0.0 1.0], ℝ; parameter=:field)" + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int64,Int64},ℝ} + end end diff --git a/test/manifolds/graph.jl b/test/manifolds/graph.jl index d0db4ca5f7..8490ce8f69 100644 --- a/test/manifolds/graph.jl +++ b/test/manifolds/graph.jl @@ -30,7 +30,7 @@ include("../utils.jl") Graph: {3, 2} undirected simple Int64 graph AbstractManifold on vertices: - Euclidean(2; field = ℝ)""" + Euclidean(2; field=ℝ)""" NE = GraphManifold(G, M, EdgeManifold()) @test is_point(NE, x[1:2]) @@ -53,7 +53,7 @@ include("../utils.jl") Graph: {3, 2} undirected simple Int64 graph AbstractManifold on edges: - Euclidean(2; field = ℝ)""" + Euclidean(2; field=ℝ)""" G2 = SimpleDiGraph(3) add_edge!(G2, 1, 2) diff --git a/test/manifolds/grassmann.jl b/test/manifolds/grassmann.jl index bbab887d7b..21474fb4e6 100644 --- a/test/manifolds/grassmann.jl +++ b/test/manifolds/grassmann.jl @@ -388,4 +388,14 @@ include("../utils.jl") Z = [0.0 -1.0; 1.0 0.0; 1.0 0.0] @test riemannian_Hessian(M, p, Y, Z, X) == [0.0 0.0; 0.0 0.0; 2.0 0.0] end + @testset "field parameter" begin + M = Grassmann(3, 2; parameter=:field) + @test repr(M) == "Grassmann(3, 2, ℝ; parameter=:field)" + @test get_total_space(M) == Stiefel(3, 2; parameter=:field) + @test typeof(get_embedding(M)) === Stiefel{Tuple{Int64,Int64},ℝ} + + p = StiefelPoint([1.0 0.0; 0.0 1.0; 0.0 0.0]) + p2 = convert(ProjectorPoint, p) + @test get_embedding(M, p2) == Euclidean(3, 3; parameter=:field) + end end diff --git a/test/manifolds/hyperbolic.jl b/test/manifolds/hyperbolic.jl index 2fc06c114a..a84a0b3780 100644 --- a/test/manifolds/hyperbolic.jl +++ b/test/manifolds/hyperbolic.jl @@ -357,4 +357,14 @@ include("../utils.jl") @test volume_density(M, p, X) ≈ 2.980406103535168 @test volume_density(M, p, [0.0, 0.0, 0.0]) ≈ 1.0 end + @testset "field parameter" begin + M = Hyperbolic(2; parameter=:field) + @test repr(M) == "Hyperbolic(2; parameter=:field)" + @test typeof(get_embedding(M)) === Lorentz{Tuple{Int64},MinkowskiMetric} + + for Tp in [PoincareBallPoint, PoincareHalfSpacePoint] + p = convert(Tp, [1.0, 0.0, sqrt(2.0)]) + @test get_embedding(M, p) === Euclidean(2; parameter=:field) + end + end end diff --git a/test/manifolds/multinomial_doubly_stochastic.jl b/test/manifolds/multinomial_doubly_stochastic.jl index db7f65e6ec..bdfa960a36 100644 --- a/test/manifolds/multinomial_doubly_stochastic.jl +++ b/test/manifolds/multinomial_doubly_stochastic.jl @@ -63,4 +63,9 @@ include("../utils.jl") ) end end + @testset "field parameter" begin + M = MultinomialDoubleStochastic(3; parameter=:field) + @test repr(M) == "MultinomialDoubleStochastic(3; parameter=:field)" + @test get_embedding(M) === MultinomialMatrices(3, 3; parameter=:field) + end end diff --git a/test/manifolds/multinomial_matrices.jl b/test/manifolds/multinomial_matrices.jl index 45fe1d87cf..ffc57cf903 100644 --- a/test/manifolds/multinomial_matrices.jl +++ b/test/manifolds/multinomial_matrices.jl @@ -51,5 +51,8 @@ include("../utils.jl") M = ProbabilitySimplex(2; parameter=:field) @test typeof(^(M, 2)) == MultinomialMatrices{Tuple{Int64,Int64},ProbabilitySimplex{Tuple{Int64},:open}} + + M = MultinomialMatrices(3, 2; parameter=:field) + @test repr(M) == "MultinomialMatrices(3, 2; parameter=:field)" end end diff --git a/test/manifolds/multinomial_symmetric.jl b/test/manifolds/multinomial_symmetric.jl index 6a89c4302f..afe1c7ad79 100644 --- a/test/manifolds/multinomial_symmetric.jl +++ b/test/manifolds/multinomial_symmetric.jl @@ -68,4 +68,9 @@ include("../utils.jl") ) end end + @testset "field parameter" begin + M = MultinomialSymmetric(3; parameter=:field) + @test repr(M) == "MultinomialSymmetric(3; parameter=:field)" + @test get_embedding(M) === MultinomialMatrices(3, 3; parameter=:field) + end end diff --git a/test/manifolds/oblique.jl b/test/manifolds/oblique.jl index b1037d59ff..59dde46ff5 100644 --- a/test/manifolds/oblique.jl +++ b/test/manifolds/oblique.jl @@ -50,5 +50,8 @@ include("../utils.jl") @testset "field parameter" begin @test typeof(^(Sphere(2; parameter=:field), 2)) == Oblique{Tuple{Int,Int},ℝ,Tuple{Int}} + + M = Oblique(3, 2; parameter=:field) + @test repr(M) == "Oblique(3, 2; field=ℝ, parameter=:field)" end end diff --git a/test/manifolds/probability_simplex.jl b/test/manifolds/probability_simplex.jl index e34c24127a..f4b4308c26 100644 --- a/test/manifolds/probability_simplex.jl +++ b/test/manifolds/probability_simplex.jl @@ -66,6 +66,8 @@ include("../utils.jl") test_rand_tvector=true, rand_tvector_atol_multiplier=20.0, ) + X = similar(pts[1]) + @test exp!(M_euc, X, pts[1], [0.0, 0.1, -0.1]) ≈ [0.5, 0.4, 0.1] test_manifold( M_euc, pts, @@ -165,4 +167,10 @@ include("../utils.jl") @test manifold_volume(M_euc) ≈ sqrt(3) / 2 @test volume_density(M_euc, p, Y) ≈ 1.0 end + + @testset "field parameter" begin + M = ProbabilitySimplex(2; parameter=:field) + @test repr(M) == "ProbabilitySimplex(2; boundary=:open, parameter=:field)" + @test get_embedding(M) === Euclidean(3; parameter=:field) + end end diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index 330e037108..278f26e922 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -199,8 +199,8 @@ using RecursiveArrayTools: ArrayPartition @test sprint(show, "text/plain", ProductManifold(Mse, Mse)) == """ ProductManifold with 2 submanifolds: - ProductManifold(Sphere(2, ℝ), Euclidean(2; field = ℝ)) - ProductManifold(Sphere(2, ℝ), Euclidean(2; field = ℝ))""" + ProductManifold(Sphere(2, ℝ), Euclidean(2; field=ℝ)) + ProductManifold(Sphere(2, ℝ), Euclidean(2; field=ℝ))""" end M3 = Rotations(2) diff --git a/test/manifolds/projective_space.jl b/test/manifolds/projective_space.jl index fa6430cca0..88e76daa33 100644 --- a/test/manifolds/projective_space.jl +++ b/test/manifolds/projective_space.jl @@ -289,7 +289,7 @@ include("../utils.jl") @testset "ArrayProjectiveSpace" begin M = ArrayProjectiveSpace(2, 2; field=ℝ) @test manifold_dimension(M) == 3 - @test repr(M) == "ArrayProjectiveSpace(2, 2; field = ℝ)" + @test repr(M) == "ArrayProjectiveSpace(2, 2; field=ℝ)" @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{2,2}},ℝ} @test representation_size(M) == (2, 2) p = ones(2, 2) @@ -301,7 +301,7 @@ include("../utils.jl") M = ArrayProjectiveSpace(2, 2; field=ℂ) @test manifold_dimension(M) == 6 - @test repr(M) == "ArrayProjectiveSpace(2, 2; field = ℂ)" + @test repr(M) == "ArrayProjectiveSpace(2, 2; field=ℂ)" @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{2,2}},ℂ} @test representation_size(M) == (2, 2) end @@ -331,4 +331,12 @@ include("../utils.jl") @test manifold_volume(ProjectiveSpace(2)) ≈ 2 * π @test manifold_volume(ProjectiveSpace(3)) ≈ π * π end + + @testset "field parameter" begin + M = ProjectiveSpace(2; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int},ℝ} + @test repr(M) == "ProjectiveSpace(2, ℝ; parameter=:field)" + @test repr(ArrayProjectiveSpace(2, 3; parameter=:field)) == + "ArrayProjectiveSpace(2, 3; field=ℝ, parameter=:field)" + end end diff --git a/test/manifolds/rotations.jl b/test/manifolds/rotations.jl index a6a403aeed..13e5c3597b 100644 --- a/test/manifolds/rotations.jl +++ b/test/manifolds/rotations.jl @@ -274,4 +274,10 @@ include("../utils.jl") 0.050996416671166 -0.024666891276861697 0.0 ] end + + @testset "field parameter" begin + M = Rotations(2; parameter=:field) + @test is_flat(M) + @test repr(M) == "Rotations(2; parameter=:field)" + end end diff --git a/test/manifolds/shape_space.jl b/test/manifolds/shape_space.jl index ea1bbcc876..b64829ad15 100644 --- a/test/manifolds/shape_space.jl +++ b/test/manifolds/shape_space.jl @@ -2,6 +2,7 @@ include("../utils.jl") @testset "KendallsPreShapeSpace" begin M = KendallsPreShapeSpace(2, 3) + @test repr(M) == "KendallsPreShapeSpace(2, 3)" @test representation_size(M) === (2, 3) @test manifold_dimension(M) == 3 @test injectivity_radius(M) == pi @@ -37,10 +38,16 @@ include("../utils.jl") test_rand_tvector=true, rand_tvector_atol_multiplier=5, ) + @testset "field parameter" begin + M = KendallsPreShapeSpace(2, 3; parameter=:field) + @test repr(M) == "KendallsPreShapeSpace(2, 3; parameter=:field)" + @test get_embedding(M) === ArraySphere(2, 3; field=ℝ, parameter=:field) + end end @testset "KendallsShapeSpace" begin M = KendallsShapeSpace(2, 3) + @test repr(M) == "KendallsShapeSpace(2, 3)" @test manifold_dimension(M) == 2 @test !is_flat(M) @test get_total_space(M) === KendallsPreShapeSpace(2, 3) @@ -105,4 +112,14 @@ end @test manifold_dimension(Md3_2) == 0 @test manifold_dimension(Md2_1) == 0 end + @testset "field parameter" begin + M = KendallsShapeSpace(2, 3; parameter=:field) + @test repr(M) == "KendallsShapeSpace(2, 3; parameter=:field)" + @test get_embedding(M) === KendallsPreShapeSpace(2, 3; parameter=:field) + @test get_total_space(M) === KendallsPreShapeSpace(2, 3; parameter=:field) + @test get_orbit_action(M) === Manifolds.ColumnwiseMultiplicationAction( + M, + SpecialOrthogonal(2; parameter=:field), + ) + end end diff --git a/test/manifolds/skewhermitian.jl b/test/manifolds/skewhermitian.jl index ce37ca70ef..1cfae74130 100644 --- a/test/manifolds/skewhermitian.jl +++ b/test/manifolds/skewhermitian.jl @@ -103,6 +103,9 @@ end end # for @testset "field parameter" begin M = SkewHermitianMatrices(3, ℝ; parameter=:field) + Mc = SkewHermitianMatrices(3, ℂ; parameter=:field) @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(M) == "SkewSymmetricMatrices(3; parameter=:field)" + @test repr(Mc) == "SkewHermitianMatrices(3, ℂ; parameter=:field)" end end # test SymmetricMatrices diff --git a/test/manifolds/spd_fixed_determinant.jl b/test/manifolds/spd_fixed_determinant.jl index 4e66ecd7b2..445d005a31 100644 --- a/test/manifolds/spd_fixed_determinant.jl +++ b/test/manifolds/spd_fixed_determinant.jl @@ -34,4 +34,10 @@ include("../utils.jl") @test distance(M, q, exp(get_embedding(M), p, X)) ≈ 0 atol = 6e-16 @test norm(M, p, log(M, p, q) - X) ≈ 0 atol = 3e-16 @test norm(M, p, log(get_embedding(M), p, q) - X) ≈ 0 atol = 3e-16 + + @testset "field parameter" begin + M = SPDFixedDeterminant(2, 1.0; parameter=:field) + @test repr(M) == "SPDFixedDeterminant(2, 1.0; parameter=:field)" + @test get_embedding(M) == SymmetricPositiveDefinite(2; parameter=:field) + end end diff --git a/test/manifolds/spectrahedron.jl b/test/manifolds/spectrahedron.jl index 912750a738..f40f67651a 100644 --- a/test/manifolds/spectrahedron.jl +++ b/test/manifolds/spectrahedron.jl @@ -54,4 +54,9 @@ include("../utils.jl") ) end end + @testset "field parameter" begin + M = Spectrahedron(4, 2; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(M) == "Spectrahedron(4, 2; parameter=:field)" + end end diff --git a/test/manifolds/sphere.jl b/test/manifolds/sphere.jl index 0cfa7ed7b6..ffb9585744 100644 --- a/test/manifolds/sphere.jl +++ b/test/manifolds/sphere.jl @@ -289,5 +289,7 @@ using ManifoldsBase: TFVector @test repr(M) == "Sphere(2, ℝ; parameter=:field)" @test repr(ArraySphere(2, 3; parameter=:field)) == "ArraySphere(2, 3; field=ℝ, parameter=:field)" + p = [1.0, 0.0, 0.0] + @test local_metric(M, p, DefaultOrthonormalBasis()) == Diagonal([1.0, 1.0]) end end diff --git a/test/manifolds/stiefel.jl b/test/manifolds/stiefel.jl index 2daa966b0b..5c4d36f68a 100644 --- a/test/manifolds/stiefel.jl +++ b/test/manifolds/stiefel.jl @@ -541,4 +541,9 @@ include("../utils.jl") @test W == Wb end end + @testset "field parameter" begin + M = Stiefel(3, 2; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(M) == "Stiefel(3, 2, ℝ; parameter=:field)" + end end diff --git a/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl b/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl index ca7cc13552..c0c6a3a54c 100644 --- a/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl +++ b/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl @@ -41,4 +41,9 @@ include("../utils.jl") @test repr(M) == "SymmetricPositiveSemidefiniteFixedRank(4, 2, ℂ)" @test manifold_dimension(M) == 12 end + @testset "field parameter" begin + M = SymmetricPositiveSemidefiniteFixedRank(4, 2; parameter=:field) + @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(M) == "SymmetricPositiveSemidefiniteFixedRank(4, 2, ℝ; parameter=:field)" + end end diff --git a/test/manifolds/symplectic.jl b/test/manifolds/symplectic.jl index fb3e279993..b0ac697785 100644 --- a/test/manifolds/symplectic.jl +++ b/test/manifolds/symplectic.jl @@ -416,4 +416,9 @@ using ManifoldDiff @test ((Q' * pQ_1' * Q) * pQ_1 - I) == zeros(eltype(pQ_1), size(pQ_1)...) end end + @testset "field parameter" begin + Sp_2 = Symplectic(2; parameter=:field) + @test typeof(get_embedding(Sp_2)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(Sp_2) == "Symplectic(2, ℝ; parameter=:field)" + end end diff --git a/test/manifolds/symplecticstiefel.jl b/test/manifolds/symplecticstiefel.jl index 63365d864f..8c01949fe8 100644 --- a/test/manifolds/symplecticstiefel.jl +++ b/test/manifolds/symplecticstiefel.jl @@ -302,4 +302,10 @@ end @test isapprox(grad_f_p, analytical_grad_f(p_grad); atol=1.0e-9) end end + @testset "field parameter" begin + SpSt_6_4 = SymplecticStiefel(2 * 3, 2 * 2; parameter=:field) + @test typeof(get_embedding(SpSt_6_4)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(SpSt_6_4) == "SymplecticStiefel(6, 4, ℝ; parameter=:field)" + @test get_total_space(SpSt_6_4) == Symplectic(6; parameter=:field) + end end diff --git a/test/manifolds/tucker.jl b/test/manifolds/tucker.jl index 1dbd39722e..762662cbcb 100644 --- a/test/manifolds/tucker.jl +++ b/test/manifolds/tucker.jl @@ -184,4 +184,9 @@ include("../utils.jl") ) end end + @testset "field parameter" begin + M = Tucker(n⃗, r⃗; parameter=:field) + @test sprint(show, "text/plain", M) == + "Tucker((4, 5, 6), (2, 3, 4), ℝ; parameter=:field)" + end end diff --git a/test/manifolds/unitary_matrices.jl b/test/manifolds/unitary_matrices.jl index 2d78d4cdb6..49177569b8 100644 --- a/test/manifolds/unitary_matrices.jl +++ b/test/manifolds/unitary_matrices.jl @@ -141,4 +141,5 @@ end @testset "Flatness edge cases" begin @test is_flat(SpecialUnitary(1)) + @test is_flat(SpecialUnitary(1; parameter=:field)) end diff --git a/test/metric.jl b/test/metric.jl index 2ea9879806..8d145df19d 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -248,7 +248,7 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, Random.seed!(42) @testset "Metric Basics" begin @test repr(MetricManifold(Euclidean(3), EuclideanMetric())) === - "MetricManifold(Euclidean(3; field = ℝ), EuclideanMetric())" + "MetricManifold(Euclidean(3; field=ℝ), EuclideanMetric())" @test repr(IsDefaultMetric(EuclideanMetric())) === "IsDefaultMetric(EuclideanMetric())" end diff --git a/test/statistics.jl b/test/statistics.jl index 1c25de3282..c4356d3788 100644 --- a/test/statistics.jl +++ b/test/statistics.jl @@ -515,6 +515,7 @@ end x = [1.0, 2.0, 3.0, 4.0] w = pweights(ones(length(x)) / length(x)) @test mean(M, x) ≈ mean(x) + @test mean(Euclidean(; parameter=:field), x) ≈ mean(x) @test mean(M, x, w) ≈ mean(x, w) @test median(M, x; rng=MersenneTwister(1212), atol=10^-12) ≈ median(x) @test median(M, x, w; rng=MersenneTwister(1212), atol=10^-12) ≈ median(x, w) From 8e8bc40ed2075c848a1b302609bd49406e2ee400 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Thu, 14 Sep 2023 17:05:30 +0200 Subject: [PATCH 28/81] fix Orthogonal, _get_parameter for SE(N), improved NEWS --- NEWS.md | 103 ++++++++++++++++---------------- src/groups/orthogonal.jl | 2 +- src/groups/special_euclidean.jl | 42 +++++++------ 3 files changed, 78 insertions(+), 69 deletions(-) diff --git a/NEWS.md b/NEWS.md index 03bced2917..4c61ccdafc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -16,79 +16,80 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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 type parameter. For field storage, pass the `parameter=:type` keyword - argument to manifold constructor. Related changes: - - 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: - - `CenteredMatrices{m,n}`, - - `CholeskySpace{N}`, - - `Elliptope{N,K}`, - - `Euclidean`, - - `FixedRankMatrices{m,n,k}`, - - `KendallsPreShapeSpace{n,k}`, - - `KendallsShapeSpace{n,k}`, - - `GeneralLinear{n}`, - - `GeneralUnitaryMultiplicationGroup{n}`, - - `GeneralizedGrassmann{n,k}`, - - `GeneralizedStiefel{n,k}`, - - `Grassmann{n,k}`, - - `Heisenberg{n}`, - - `Hyperbolic{n}`, - - `MultinomialMatrices{N,M}`, - - `MultinomialDoublyStochastic{n}`, - - `MultinomialSymmetric{n}`, - - `Orthogonal{n}`, - - `ProbabilitySimplex{n}`, - - `SPDFixedDeterminant{n}`, - - `SpecialLinear{n}`, - - `SpecialOrthogonal{n}`, - - `SpecialUnitary{n}`, - - `SpecialEuclideanManifold{n}`, - - `Spectrahedron{n,k}`, - - `SphereSymmetricMatrices{N}`, - - `Stiefel{n,k}`, - - `SymmetricMatrices{N}`, - - `SymmetricPositiveDefinite{n}`, - - `SymmetricPositiveSemidefiniteFixedRank{n,k}`, - - `Symplectic{n}`, - - `SymplecticStiefel{n,k}`, - - `TranslationGroup`, - - `Tucker`. + The default is set to store the size in type parameter. For field storage, pass the `parameter=:field` keyword + argument to manifold constructor. + For example statically sized `CenteredMatrices{m,n}` is now `CenteredMatrices{TypeParameter{Tuple{m,n}}}`, whereas the type of special Euclidean group with field-stored size is `CenteredMatrices{Tuple{Int,Int}}`. Similar change applies to: + - `CenteredMatrices{m,n}`, + - `CholeskySpace{N}`, + - `Elliptope{N,K}`, + - `Euclidean`, + - `FixedRankMatrices{m,n,k}`, + - `KendallsPreShapeSpace{n,k}`, + - `KendallsShapeSpace{n,k}`, + - `GeneralLinear{n}`, + - `GeneralUnitaryMultiplicationGroup{n}`, + - `GeneralizedGrassmann{n,k}`, + - `GeneralizedStiefel{n,k}`, + - `Grassmann{n,k}`, + - `Heisenberg{n}`, + - `Hyperbolic{n}`, + - `MultinomialMatrices{N,M}`, + - `MultinomialDoublyStochastic{n}`, + - `MultinomialSymmetric{n}`, + - `Orthogonal{n}`, + - `ProbabilitySimplex{n}`, + - `SPDFixedDeterminant{n}`, + - `SpecialLinear{n}`, + - `SpecialOrthogonal{n}`, + - `SpecialUnitary{n}`, + - `SpecialEuclidean{n}`, + - `SpecialEuclideanManifold{n}`, + - `Spectrahedron{n,k}`, + - `SphereSymmetricMatrices{N}`, + - `Stiefel{n,k}`, + - `SymmetricMatrices{N}`, + - `SymmetricPositiveDefinite{n}`, + - `SymmetricPositiveSemidefiniteFixedRank{n,k}`, + - `Symplectic{n}`, + - `SymplecticStiefel{n,k}`, + - `TranslationGroup`, + - `Tucker`. For example ```{julia} - function Base.show(io::IO, ::SpecialEuclidean{n}) where {n} - return print(io, "SpecialEuclidean($(n))") + function Base.show(io::IO, ::CenteredMatrices{m,n}) where {m,n} + return print(io, "CenteredMatrices($m, $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)") + function Base.show(io::IO, ::CenteredMatrices{TypeParameter{Tuple{m,n}}}) where {m,n} + return print(io, "CenteredMatrices($m, $n)") 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))") + function Base.show(io::IO, M::CenteredMatrices{Tuple{Int,Int}}) + m, n = get_parameter(M.size) + return print(io, "CenteredMatrices($m, $n; parameter=:field)") end ``` for groups with size stored in field. Alternatively, you can use a single generic method like this: ```{julia} - function Base.show(io::IO, G::SpecialEuclidean{T}) where {T} - n = get_n(G) - if T <: TypeParameter - return print(io, "SpecialEuclidean($(n); parameter=:type)") - else - return print(io, "SpecialEuclidean($(n))") - end + function Base.show(io::IO, M::CenteredMatrices{T}) where {T} + m, n = get_parameter(M) + if T <: TypeParameter + return print(io, "CenteredMatrices($m, $n)") + else + return print(io, "CenteredMatrices($m, $n; parameter=:field)") + end end ``` diff --git a/src/groups/orthogonal.jl b/src/groups/orthogonal.jl index ed6a476b0d..2448d0356a 100644 --- a/src/groups/orthogonal.jl +++ b/src/groups/orthogonal.jl @@ -17,6 +17,6 @@ function Base.show(io::IO, ::Orthogonal{TypeParameter{Tuple{n}}}) where {n} return print(io, "Orthogonal($(n))") end function Base.show(io::IO, M::Orthogonal{Tuple{Int}}) - n = get_parameter(M.size)[1] + n = get_parameter(M.manifold.size)[1] return print(io, "Orthogonal($(n); parameter=:field)") end diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index 9ce2cc1ec1..e573066304 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -51,7 +51,7 @@ function Base.show(io::IO, ::SpecialEuclidean{TypeParameter{Tuple{n}}}) where {n return print(io, "SpecialEuclidean($(n))") end function Base.show(io::IO, G::SpecialEuclidean{Tuple{Int}}) - n = get_n(G) + n = _get_parameter(G) return print(io, "SpecialEuclidean($(n); parameter=:field)") end @@ -59,10 +59,18 @@ end return merge_traits(IsGroupManifold(M.op), IsExplicitDecorator()) end -get_n(::SpecialEuclidean{TypeParameter{Tuple{N}}}) where {N} = N -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]) +""" + _get_parameter(M::AbstractManifold) + +Similar to `get_parameter` but it can be specialized for manifolds without breaking +manifolds being parametrized by other manifolds. +""" +_get_parameter(::AbstractManifold) + +_get_parameter(::SpecialEuclidean{TypeParameter{Tuple{N}}}) where {N} = N +_get_parameter(M::SpecialEuclidean{Tuple{Int}}) = _get_parameter(M.manifold) +_get_parameter(::SpecialEuclideanManifold{TypeParameter{Tuple{N}}}) where {N} = N +_get_parameter(M::SpecialEuclideanManifold{Tuple{Int}}) = manifold_dimension(M.manifolds[1]) Base.@propagate_inbounds function Base.getindex( p::AbstractMatrix, @@ -87,7 +95,7 @@ Base.@propagate_inbounds function submanifold_component( p::AbstractMatrix, ::Val{1}, ) - n = get_n(G) + n = _get_parameter(G) return view(p, 1:n, n + 1) end Base.@propagate_inbounds function submanifold_component( @@ -95,7 +103,7 @@ Base.@propagate_inbounds function submanifold_component( p::AbstractMatrix, ::Val{2}, ) - n = get_n(G) + n = _get_parameter(G) return view(p, 1:n, 1:n) end @@ -103,7 +111,7 @@ function submanifold_components( G::Union{SpecialEuclidean,SpecialEuclideanManifold}, p::AbstractMatrix, ) - n = get_n(G) + n = _get_parameter(G) @assert size(p) == (n + 1, n + 1) @inbounds t = submanifold_component(G, p, Val(1)) @inbounds R = submanifold_component(G, p, Val(2)) @@ -114,7 +122,7 @@ Base.@propagate_inbounds function _padpoint!( G::Union{SpecialEuclidean,SpecialEuclideanManifold}, q::AbstractMatrix, ) - n = get_n(G) + n = _get_parameter(G) for i in 1:n q[n + 1, i] = 0 end @@ -126,7 +134,7 @@ Base.@propagate_inbounds function _padvector!( G::Union{SpecialEuclidean,SpecialEuclideanManifold}, X::AbstractMatrix, ) - n = get_n(G) + n = _get_parameter(G) for i in 1:(n + 1) X[n + 1, i] = 0 end @@ -196,12 +204,12 @@ function affine_matrix( G::SpecialEuclidean{Tuple{Int}}, ::SpecialEuclideanIdentity{Tuple{Int}}, ) - n = get_n(G) + n = _get_parameter(G) return Diagonal{Float64}(I, n) end function check_point(G::SpecialEuclideanManifold, p::AbstractMatrix; kwargs...) - n = get_n(G) + n = _get_parameter(G) errs = DomainError[] # homogeneous if !isapprox(p[end, :], [zeros(size(p, 2) - 1)..., 1]; kwargs...) @@ -226,7 +234,7 @@ function check_point(G::SpecialEuclideanManifold, p::AbstractMatrix; kwargs...) end function check_size(G::SpecialEuclideanManifold, p::AbstractMatrix; kwargs...) - n = get_n(G) + n = _get_parameter(G) return check_size(Euclidean(n + 1, n + 1), p) end function check_size( @@ -235,7 +243,7 @@ function check_size( X::AbstractMatrix; kwargs..., ) - n = get_n(G) + n = _get_parameter(G) return check_size(Euclidean(n + 1, n + 1), X) end @@ -245,7 +253,7 @@ function check_vector( X::AbstractMatrix; kwargs..., ) - n = get_n(G) + n = _get_parameter(G) errs = DomainError[] # homogeneous if !isapprox(X[end, :], zeros(size(X, 2)); kwargs...) @@ -296,11 +304,11 @@ end screw_matrix(::SpecialEuclidean, X::AbstractMatrix) = X function allocate_result(G::SpecialEuclidean, ::typeof(affine_matrix), p...) - n = get_n(G) + n = _get_parameter(G) return allocate(p[1], Size(n + 1, n + 1)) end function allocate_result(G::SpecialEuclidean, ::typeof(screw_matrix), X...) - n = get_n(G) + n = _get_parameter(G) return allocate(X[1], Size(n + 1, n + 1)) end From 89265a1300050f6f853df11a346181c433aa7bd7 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 18 Sep 2023 11:28:15 +0200 Subject: [PATCH 29/81] a few tests --- test/groups/rotation_translation_action.jl | 2 ++ test/manifolds/rotations.jl | 6 ++++++ test/manifolds/symmetric.jl | 1 + test/manifolds/symmetric_positive_definite.jl | 1 + 4 files changed, 10 insertions(+) diff --git a/test/groups/rotation_translation_action.jl b/test/groups/rotation_translation_action.jl index 96398d0a12..4bbf3cf841 100644 --- a/test/groups/rotation_translation_action.jl +++ b/test/groups/rotation_translation_action.jl @@ -110,4 +110,6 @@ end q = similar(p1) apply!(A, q, a, p1) @test isapprox(q, apply(A, a, p1)) + apply!(A, q, Identity(G), p1) + @test isapprox(q, p1) end diff --git a/test/manifolds/rotations.jl b/test/manifolds/rotations.jl index 47a0b115a8..186886e82b 100644 --- a/test/manifolds/rotations.jl +++ b/test/manifolds/rotations.jl @@ -279,5 +279,11 @@ include("../utils.jl") M = Rotations(2; parameter=:field) @test is_flat(M) @test repr(M) == "Rotations(2; parameter=:field)" + + M = Rotations(1; parameter=:field) + p = fill(1.0, 1, 1) + X = get_vector(M, p, Float64[], DefaultOrthonormalBasis()) + @test X isa Matrix{Float64} + @test X == fill(0.0, 1, 1) end end diff --git a/test/manifolds/symmetric.jl b/test/manifolds/symmetric.jl index c586a3d347..4a36cb8d19 100644 --- a/test/manifolds/symmetric.jl +++ b/test/manifolds/symmetric.jl @@ -88,5 +88,6 @@ include("../utils.jl") @testset "field parameter" begin M = SymmetricMatrices(3, ℝ; parameter=:field) @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(M) == "SymmetricMatrices(3, ℝ; parameter=:field)" end end # test SymmetricMatrices diff --git a/test/manifolds/symmetric_positive_definite.jl b/test/manifolds/symmetric_positive_definite.jl index c08f1d2bd0..f099737bb0 100644 --- a/test/manifolds/symmetric_positive_definite.jl +++ b/test/manifolds/symmetric_positive_definite.jl @@ -306,5 +306,6 @@ include("../utils.jl") @testset "field parameter" begin M = SymmetricPositiveDefinite(3; parameter=:field) @test typeof(get_embedding(M)) === Euclidean{Tuple{Int,Int},ℝ} + @test repr(M) == "SymmetricPositiveDefinite(3; parameter=:field)" end end From 43051ebc4f0f55b2b5d8499817288878e50ce677 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 18 Sep 2023 13:21:46 +0200 Subject: [PATCH 30/81] tests --- src/groups/rotation_translation_action.jl | 6 ++--- test/groups/rotation_translation_action.jl | 27 ++++++++++++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/groups/rotation_translation_action.jl b/src/groups/rotation_translation_action.jl index b753603a76..c1a428b7c1 100644 --- a/src/groups/rotation_translation_action.jl +++ b/src/groups/rotation_translation_action.jl @@ -290,16 +290,16 @@ end optimal_alignment(A::LeftColumnwiseSpecialEuclideanAction, p, q) Compute optimal alignment of `p` to `q` under the forward left [`ColumnwiseSpecialEuclideanAction`](@ref). -The algorithm, in sequence, computes optimal translation and optimal rotation +The algorithm, in sequence, computes optimal translation and optimal rotation. """ function optimal_alignment( A::LeftColumnwiseSpecialEuclideanAction{<:AbstractManifold,<:SpecialEuclidean}, p, q, ) - N = get_n(A.SE) + N = _get_parameter(A.SE) tr_opt = mean(q; dims=1) - mean(p; dims=1) - p_moved = p + tr_opt + p_moved = p .+ tr_opt Ostar = optimal_alignment( ColumnwiseMultiplicationAction(A.manifold, SpecialOrthogonal(N)), diff --git a/test/groups/rotation_translation_action.jl b/test/groups/rotation_translation_action.jl index 4bbf3cf841..4f31971721 100644 --- a/test/groups/rotation_translation_action.jl +++ b/test/groups/rotation_translation_action.jl @@ -88,19 +88,27 @@ end @test group_manifold(A) === M @test base_group(A) === SpecialEuclidean(2) - a = ArrayPartition( + a1 = ArrayPartition( [1.0, 2.0], [0.5851302132737501 -0.8109393525500014; 0.8109393525500014 0.5851302132737504], ) + a2 = ArrayPartition( + [2.0, -1.0], + [0.903025374532402 -0.4295872122754759; 0.4295872122754759 0.9030253745324022], + ) + a3 = ArrayPartition( + [2.0, 0.0], + [0.5851302132737501 -0.8109393525500014; 0.8109393525500014 0.5851302132737504], + ) @test isapprox( - apply(A, a, p1), + apply(A, a1, p1), [ 1.567197334849809 0.3109111254828243 1.1218915396673668 2.1314863675092206 1.6490786599533187 2.2194349725374605 ], ) @test isapprox( - inverse_apply(A, a, p1), + inverse_apply(A, a1, p1), [ -2.2610332854401007 -2.3228048546690494 -2.0371886150121097 -0.9390476037204165 0.40525761065242144 -0.5441732289245019 @@ -108,8 +116,17 @@ end ) @test apply(A, Identity(G), p1) === p1 q = similar(p1) - apply!(A, q, a, p1) - @test isapprox(q, apply(A, a, p1)) + apply!(A, q, a1, p1) + @test isapprox(q, apply(A, a1, p1)) apply!(A, q, Identity(G), p1) @test isapprox(q, p1) + test_action( + A, + [a1, a2, a3], + [p1, p2]; + test_optimal_alignment=true, + test_diff=false, + test_switch_direction=false, + atol=1e-14, + ) end From 48dd28867fe8ae5232978d572d51992ede38ddf3 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 19 Sep 2023 12:30:16 +0200 Subject: [PATCH 31/81] more tests --- src/manifolds/Fiber.jl | 1 - src/manifolds/FiberBundle.jl | 2 +- src/manifolds/VectorBundle.jl | 49 +++------------------------------ test/ambiguities.jl | 2 +- test/manifolds/fiber_bundle.jl | 10 +++++++ test/manifolds/vector_bundle.jl | 44 +++++++++++++++-------------- test/runtests.jl | 1 + 7 files changed, 40 insertions(+), 69 deletions(-) create mode 100644 test/manifolds/fiber_bundle.jl diff --git a/src/manifolds/Fiber.jl b/src/manifolds/Fiber.jl index e150392205..3658d714b9 100644 --- a/src/manifolds/Fiber.jl +++ b/src/manifolds/Fiber.jl @@ -114,7 +114,6 @@ const VectorSpaceAtPoint{𝔽,M,TSpaceType} = FiberAtPoint{ function VectorSpaceAtPoint(M::AbstractManifold, fiber::VectorSpaceFiberType, p) return FiberAtPoint(BundleFibers(fiber, M), p) end -VectorSpaceAtPoint(fiber::BundleFibers{<:VectorSpaceFiberType}, p) = FiberAtPoint(fiber, p) """ TangentSpaceAtPoint{𝔽,M} diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index b43a741d2c..a94cd9affd 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -202,7 +202,7 @@ function get_coordinates!( return Y end -function _get_vector(M::FiberBundle, p, X, B::AbstractBasis) +function get_vector(M::FiberBundle, p, X, B::AbstractBasis) n = manifold_dimension(M.manifold) xp1 = submanifold_component(p, Val(1)) return ArrayPartition( diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index a3f14d4110..84ee32dab1 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -154,13 +154,8 @@ end Inner product of vectors `X` and `Y` from the vector space of type `B.fiber` at point `p` from manifold `B.manifold`. """ -function inner(B::BundleFibers, p, X, Y) - return error( - "inner not defined for vector space family of type $(typeof(B)), " * - "point of type $(typeof(p)) and " * - "vectors of types $(typeof(X)) and $(typeof(Y)).", - ) -end +inner(B::BundleFibers, p, X, Y) + inner(B::TangentBundleFibers, p, X, Y) = inner(B.manifold, p, X, Y) function inner(B::CotangentBundleFibers, p, X, Y) return inner(B.manifold, p, sharp(B.manifold, p, X), sharp(B.manifold, p, Y)) @@ -305,11 +300,6 @@ end function project!(B::TangentBundleFibers, Y, p, X) return project!(B.manifold, Y, p, X) end -function project!(B::BundleFibers, Y, p, X) - return error( - "project! not implemented for vector space family of type $(typeof(B)), output vector of type $(typeof(Y)) and input vector at point $(typeof(p)) with type of w $(typeof(X)).", - ) -end function _retract(M::VectorBundle, p, X, t::Number, ::FiberBundleProductRetraction) return retract_product(M, p, X, t) @@ -576,11 +566,8 @@ end Save the zero vector from the vector space of type `B.fiber` at point `p` from manifold `B.manifold` to `X`. """ -function zero_vector!(B::BundleFibers, X, p) - return error( - "zero_vector! not implemented for vector space family of type $(typeof(B)).", - ) -end +zero_vector!(B::BundleFibers, X, p) + function zero_vector!(B::TangentBundleFibers, X, p) return zero_vector!(B.manifold, X, p) end @@ -597,31 +584,3 @@ Since this a flat space by itself, the result is always the zero tangent vector. Weingarten(::TangentSpaceAtPoint, p, X, V) Weingarten!(::TangentSpaceAtPoint, Y, p, X, V) = fill!(Y, 0) - -@doc raw""" - zero_vector(B::VectorBundle, p) - -Zero tangent vector at point `p` from the vector bundle `B` -over manifold `B.fiber` (denoted $\mathcal M$). The zero vector belongs to the space $T_{p}B$ - -Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - -The zero vector is calculated as - -$\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)$ - -where $\mathbf{0}_{x_p}$ is the zero tangent vector from $T_{x_p}\mathcal M$ and -$\mathbf{0}_F$ is the zero element of the vector space $F$. -""" -zero_vector(::VectorBundle, ::Any...) - -function zero_vector!(B::VectorBundle, X, p) - xp, Vp = submanifold_components(B.manifold, p) - VXM, VXF = submanifold_components(B.manifold, X) - zero_vector!(B.manifold, VXM, xp) - zero_vector!(B.fiber, VXF, Vp) - return X -end diff --git a/test/ambiguities.jl b/test/ambiguities.jl index 9af12a6e78..b46f568017 100644 --- a/test/ambiguities.jl +++ b/test/ambiguities.jl @@ -17,7 +17,7 @@ # Interims solution until we follow what was proposed in # https://discourse.julialang.org/t/avoid-ambiguities-with-individual-number-element-identity/62465/2 fms = filter(x -> !any(has_type_in_signature.(x, Identity)), ms) - FMS_LIMIT = 45 + FMS_LIMIT = 47 println("Number of Manifolds.jl ambiguities: $(length(fms))") if length(fms) > FMS_LIMIT for amb in fms diff --git a/test/manifolds/fiber_bundle.jl b/test/manifolds/fiber_bundle.jl new file mode 100644 index 0000000000..9950c1ac67 --- /dev/null +++ b/test/manifolds/fiber_bundle.jl @@ -0,0 +1,10 @@ +include("../utils.jl") + +using RecursiveArrayTools + +@testset "fiber bundle" begin + M = Stiefel(3, 2) + vm = default_vector_transport_method(M) + @test Manifolds.FiberBundleProductVectorTransport(M) == + Manifolds.FiberBundleProductVectorTransport(vm, vm) +end diff --git a/test/manifolds/vector_bundle.jl b/test/manifolds/vector_bundle.jl index c183cd740c..9c0cdc5d00 100644 --- a/test/manifolds/vector_bundle.jl +++ b/test/manifolds/vector_bundle.jl @@ -4,14 +4,14 @@ using RecursiveArrayTools struct TestVectorSpaceType <: VectorSpaceType end -@testset "Tangent bundle" begin +@testset "Vector bundle" begin M = Sphere(2) + TB = TangentBundle(M) m_prod_retr = Manifolds.FiberBundleProductRetraction() m_prod_invretr = Manifolds.FiberBundleInverseProductRetraction() m_sasaki = SasakiRetraction(5) @testset "Nice access to vector bundle components" begin - TB = TangentBundle(M) @testset "ArrayPartition" begin p = ArrayPartition([1.0, 0.0, 0.0], [0.0, 2.0, 4.0]) @test p[TB, :point] === p.x[1] @@ -33,39 +33,43 @@ struct TestVectorSpaceType <: VectorSpaceType end end end - types = [Vector{Float64}] - TEST_FLOAT32 && push!(types, Vector{Float32}) - TEST_STATIC_SIZED && push!(types, MVector{3,Float64}) - - for T in types - p = convert(T, [1.0, 0.0, 0.0]) - TB = TangentBundle(M) + @testset "basic tests" begin @test injectivity_radius(TB) == 0 - TpM = TangentSpaceAtPoint(M, p) @test sprint(show, TB) == "TangentBundle(Sphere(2, ℝ))" @test base_manifold(TB) == M @test manifold_dimension(TB) == 2 * manifold_dimension(M) @test !is_flat(TB) - @test is_flat(TpM) @test representation_size(TB) === nothing @test default_inverse_retraction_method(TB) === m_prod_invretr @test default_retraction_method(TB) == m_prod_retr @test default_vector_transport_method(TB) isa Manifolds.FiberBundleProductVectorTransport CTB = CotangentBundle(M) + CTBF = CotangentBundleFibers(M) @test sprint(show, CTB) == "CotangentBundle(Sphere(2, ℝ))" @test sprint(show, VectorBundle(TestVectorSpaceType(), M)) == "VectorBundle(TestVectorSpaceType(), Sphere(2, ℝ))" + @test sprint(show, CTBF) == "VectorBundleFibers(CotangentSpace, Sphere(2, ℝ))" + @test Manifolds.fiber_dimension(CTBF) == 2 + @test Manifolds.fiber_dimension(M, ManifoldsBase.CotangentSpace) == 2 + @test base_manifold(TangentBundle(M)) == M + end + + types = [Vector{Float64}] + TEST_FLOAT32 && push!(types, Vector{Float32}) + TEST_STATIC_SIZED && push!(types, MVector{3,Float64}) + + for T in types + p = convert(T, [1.0, 0.0, 0.0]) + TpM = TangentSpaceAtPoint(M, p) + @test is_flat(TpM) + @testset "Type $T" begin pts_tb = [ ArrayPartition(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, -1.0, -1.0])), ArrayPartition(convert(T, [0.0, 1.0, 0.0]), convert(T, [2.0, 0.0, 1.0])), ArrayPartition(convert(T, [1.0, 0.0, 0.0]), convert(T, [0.0, 2.0, -1.0])), ] - @inferred ArrayPartition( - convert(T, [1.0, 0.0, 0.0]), - convert(T, [0.0, -1.0, -1.0]), - ) for pt in pts_tb @test bundle_projection(TB, pt) ≈ pt.x[1] end @@ -87,7 +91,7 @@ struct TestVectorSpaceType <: VectorSpaceType end retraction_methods=[m_prod_retr, m_sasaki], test_exp_log=false, test_injectivity_radius=false, - test_tangent_vector_broadcasting=false, + test_tangent_vector_broadcasting=true, test_vee_hat=true, test_project_tangent=true, test_project_point=true, @@ -168,8 +172,6 @@ struct TestVectorSpaceType <: VectorSpaceType end @test CotangentBundle{ℝ,Sphere{2,ℝ}} == VectorBundle{ℝ,Manifolds.CotangentSpaceType,Sphere{2,ℝ}} - @test base_manifold(TangentBundle(M)) == M - @testset "tensor product" begin TT = Manifolds.TensorProductType(TangentSpace, TangentSpace) @test sprint(show, TT) == "TensorProductType(TangentSpace, TangentSpace)" @@ -182,9 +184,9 @@ struct TestVectorSpaceType <: VectorSpaceType end @testset "Error messages" begin vbf = VectorBundleFibers(TestVectorSpaceType(), Euclidean(3)) - @test_throws ErrorException inner(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) - @test_throws ErrorException Manifolds.project!(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) - @test_throws ErrorException zero_vector!(vbf, [1, 2, 3], [1, 2, 3]) + @test_throws MethodError inner(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) + @test_throws MethodError project!(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) + @test_throws MethodError zero_vector!(vbf, [1, 2, 3], [1, 2, 3]) @test_throws MethodError vector_space_dimension(vbf) end diff --git a/test/runtests.jl b/test/runtests.jl index fe76ea0cf9..475bd06777 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -177,6 +177,7 @@ include("utils.jl") include_test("manifolds/power_manifold.jl") include_test("manifolds/quotient_manifold.jl") include_test("manifolds/fiber.jl") + include_test("manifolds/fiber_bundle.jl") include_test("manifolds/vector_bundle.jl") include_test("manifolds/direct_sum_bundle.jl") include_test("manifolds/graph.jl") From ca31836d682081df1acbe1e880c4985323279c6e Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 19 Sep 2023 18:37:26 +0200 Subject: [PATCH 32/81] mostly docs and structure --- docs/make.jl | 1 + docs/src/manifolds/fiber_bundle.md | 17 ++ docs/src/manifolds/vector_bundle.md | 9 +- docs/src/misc/notation.md | 2 +- src/Manifolds.jl | 3 +- src/manifolds/Fiber.jl | 298 +--------------------------- src/manifolds/SkewHermitian.jl | 6 +- src/manifolds/VectorBundle.jl | 5 + src/manifolds/VectorFiber.jl | 292 +++++++++++++++++++++++++++ test/manifolds/fiber.jl | 6 +- 10 files changed, 334 insertions(+), 305 deletions(-) create mode 100644 docs/src/manifolds/fiber_bundle.md create mode 100644 src/manifolds/VectorFiber.jl diff --git a/docs/make.jl b/docs/make.jl index e16a3356e0..04e0a3cc89 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -137,6 +137,7 @@ makedocs( "Unit-norm symmetric matrices" => "manifolds/spheresymmetricmatrices.md", ], "Combined manifolds" => [ + "Fiber bundle" => "manifolds/fiber_bundle.md", "Graph manifold" => "manifolds/graph.md", "Power manifold" => "manifolds/power.md", "Product manifold" => "manifolds/product.md", diff --git a/docs/src/manifolds/fiber_bundle.md b/docs/src/manifolds/fiber_bundle.md new file mode 100644 index 0000000000..9acbe33db4 --- /dev/null +++ b/docs/src/manifolds/fiber_bundle.md @@ -0,0 +1,17 @@ +# [Fiber bundles](@id FiberBundleSection) + +Fiber bundle $E$ is a manifold that is built on top of another manifold $\mathcal M$ (base space). +It is characterized by a continuous function $Π : E → \mathcal M$. For each point $p ∈ \mathcal M$ the preimage of $p$ by $Π$, $Π^{-1}(\{p\})$ is called a fiber $F$. +Bundle projection can be performed using function [`bundle_projection`](@ref). + +`Manifolds.jl` primarily deals with the case of trivial bundles, where $E$ can be identified with a product $M \times F$. + +[Vector bundles](@ref VectorBundleSection) is a special case of a fiber bundle. Other examples include unit tangent bundle. Note that in general fiber bundles don't have a canonical Riemannian structure but can at least be equipped with an [Ehresmann connection](https://en.wikipedia.org/wiki/Ehresmann_connection), providing notions of parallel transport and curvature. + +## Documentation + +```@autodocs +Modules = [Manifolds, ManifoldsBase] +Pages = ["manifolds/Fiber.jl", "manifolds/FiberBundle.jl"] +Order = [:constant, :type, :function] +``` diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index 8de9bb9157..b996756a25 100644 --- a/docs/src/manifolds/vector_bundle.md +++ b/docs/src/manifolds/vector_bundle.md @@ -1,11 +1,8 @@ # [Vector bundles](@id VectorBundleSection) -Vector bundle $E$ is a manifold that is built on top of another manifold $\mathcal M$ (base space). -It is characterized by a continuous function $Π : E → \mathcal M$, such that for each point $p ∈ \mathcal M$ the preimage of $p$ by $Π$, $Π^{-1}(\{p\})$, has a structure of a vector space. -These vector spaces are called fibers. -Bundle projection can be performed using function [`bundle_projection`](@ref). +Vector bundle $E$ is a special case of a [fiber bundle](@ref FiberBundleSection) where each fiber is a vector space. -Tangent bundle is a simple example of a vector bundle, where each fiber is the tangent space at the specified point $x$. +Tangent bundle is a simple example of a vector bundle, where each fiber is the tangent space at the specified point $p$. An object representing a tangent bundle can be obtained using the constructor called `TangentBundle`. Fibers of a vector bundle are represented by the type `VectorBundleFibers`. @@ -25,7 +22,7 @@ It is used for example in musical isomorphisms (the [`flat`](@ref) and [`sharp`] ```@autodocs Modules = [Manifolds, ManifoldsBase] -Pages = ["manifolds/VectorBundle.jl"] +Pages = ["manifolds/VectorFiber.jl", "manifolds/VectorBundle.jl"] Order = [:constant, :type, :function] ``` diff --git a/docs/src/misc/notation.md b/docs/src/misc/notation.md index 768c426c78..3ea725d2f5 100644 --- a/docs/src/misc/notation.md +++ b/docs/src/misc/notation.md @@ -22,7 +22,7 @@ Within the documented functions, the utf8 symbols are used whenever possible, as | ``n`` | dimension (of a manifold) | ``n_1,n_2,\ldots,m, \dim(\mathcal M)``| for the real dimension sometimes also ``\dim_{\mathbb R}(\mathcal M)``| | ``d(\cdot,\cdot)`` | (Riemannian) distance | ``d_{\mathcal M}(\cdot,\cdot)`` | | | ``\exp_p X`` | exponential map at ``p \in \mathcal M`` of a vector ``X \in T_p \mathcal M`` | ``\exp_p(X)`` | | -| ``F`` | a fiber | | see [`VectorBundleFibers`](@ref) | +| ``F`` | a fiber | | see [`BundleFibers`](@ref), [`VectorBundleFibers`](@ref) | | ``\mathbb F`` | a field, usually ``\mathbb F \in \{\mathbb R,\mathbb C, \mathbb H\}``, i.e. the real, complex, and quaternion numbers, respectively. | |field a manifold or a basis is based on | | ``\gamma`` | a geodesic | ``\gamma_{p;q}``, ``\gamma_{p,X}`` | connecting two points ``p,q`` or starting in ``p`` with velocity ``X``. | | ``\operatorname{grad} f(p)`` | (Riemannian) gradient of function ``f \colon \mathcal{M} \to \mathbb{R}`` at ``p \in \mathcal{M}`` | | | diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 4311cd8710..98ce866a33 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -344,6 +344,7 @@ include("manifolds/MetricManifold.jl") include("manifolds/QuotientManifold.jl") include("manifolds/Fiber.jl") include("manifolds/FiberBundle.jl") +include("manifolds/VectorFiber.jl") include("manifolds/VectorBundle.jl") include("manifolds/DirectSumBundle.jl") include("groups/group.jl") @@ -672,7 +673,7 @@ export GraphManifold, GraphManifoldType, VertexManifold, EdgeManifold export ArrayPartition export ProjectedPointDistribution, TangentBundle, TangentBundleFibers export TangentSpace, TangentSpaceAtPoint, VectorSpaceAtPoint, VectorSpaceType, VectorBundle -export VectorBundleFibers +export BundleFibers, VectorBundleFibers export AbstractVectorTransportMethod, DifferentiatedRetractionVectorTransport, ParallelTransport, ProjectedPointDistribution export PoleLadderTransport, SchildsLadderTransport diff --git a/src/manifolds/Fiber.jl b/src/manifolds/Fiber.jl index 3658d714b9..0041e52a4b 100644 --- a/src/manifolds/Fiber.jl +++ b/src/manifolds/Fiber.jl @@ -1,16 +1,4 @@ -""" - TensorProductType(spaces::VectorSpaceType...) - -Vector space type corresponding to the tensor product of given vector space -types. -""" -struct TensorProductType{TS<:Tuple} <: VectorSpaceType - spaces::TS -end - -TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) - """ abstract type FiberType end @@ -18,30 +6,15 @@ An abstract type for fiber types. """ abstract type FiberType end -struct VectorSpaceFiberType{TVS<:VectorSpaceType} <: FiberType - fiber::TVS -end - -function Base.show(io::IO, vsf::VectorSpaceFiberType) - return print(io, "VectorSpaceFiberType($(vsf.fiber))") -end - -const TangentFiberType = VectorSpaceFiberType{TangentSpaceType} - -const CotangentFiberType = VectorSpaceFiberType{CotangentSpaceType} - -const TangentFiber = VectorSpaceFiberType{TangentSpaceType}(TangentSpace) -const CotangentFiber = VectorSpaceFiberType{CotangentSpaceType}(CotangentSpace) - """ BundleFibers(fiber::FiberType, M::AbstractManifold) -Type representing a family of vector spaces (fibers) of a vector bundle over `M` -with vector spaces of type `fiber`. In contrast with `FiberBundle`, operations -on `BundleFibers` expect point-like and vector-like parts to be +Type representing a family of fibers of a fiber bundle over `M` +with vectorfiber of type `fiber`. In contrast with `FiberBundle`, operations +on `BundleFibers` expect point-like and fiber-like parts to be passed separately instead of being bundled together. It can be thought of -as a representation of vector spaces from a vector bundle but without -storing the point at which a vector space is attached (which is specified +as a representation of fibers from a fiber bundle but without +storing the point at which a fiber is attached (which is specified separately in various functions). """ struct BundleFibers{TF<:FiberType,TM<:AbstractManifold} @@ -49,32 +22,6 @@ struct BundleFibers{TF<:FiberType,TM<:AbstractManifold} manifold::TM end -""" - VectorBundleFibers{TVS,TM} - -Alias for [`BundleFibers`](@ref) when the fiber is a vector space. -""" -const VectorBundleFibers{TVS,TM} = BundleFibers{ - VectorSpaceFiberType{TVS}, - TM, -} where {TVS<:VectorSpaceType,TM<:AbstractManifold} - -function VectorBundleFibers(fiber::VectorSpaceType, M::AbstractManifold) - return BundleFibers(VectorSpaceFiberType(fiber), M) -end -function VectorBundleFibers(fiber::VectorSpaceFiberType, M::AbstractManifold) - return BundleFibers(fiber, M) -end - -const TangentBundleFibers{M} = BundleFibers{TangentFiberType,M} where {M<:AbstractManifold} - -TangentBundleFibers(M::AbstractManifold) = BundleFibers(TangentFiber, M) - -const CotangentBundleFibers{M} = - BundleFibers{CotangentFiberType,M} where {M<:AbstractManifold} - -CotangentBundleFibers(M::AbstractManifold) = BundleFibers(CotangentFiber, M) - """ FiberAtPoint{ 𝔽, @@ -101,62 +48,11 @@ struct FiberAtPoint{𝔽,TFiber<:BundleFibers{<:FiberType,<:AbstractManifold{ point::TX end -""" - VectorSpaceAtPoint{𝔽,M,TFiber} - -Alias for [`FiberAtPoint`](@ref) when the fiber is a vector space. -""" -const VectorSpaceAtPoint{𝔽,M,TSpaceType} = FiberAtPoint{ - 𝔽, - VectorBundleFibers{TSpaceType,M}, -} where {𝔽,M<:AbstractManifold{𝔽},TSpaceType<:VectorSpaceType} - -function VectorSpaceAtPoint(M::AbstractManifold, fiber::VectorSpaceFiberType, p) - return FiberAtPoint(BundleFibers(fiber, M), p) -end - -""" - TangentSpaceAtPoint{𝔽,M} - -Alias for [`VectorSpaceAtPoint`](@ref) for the tangent space at a point. -""" -const TangentSpaceAtPoint{𝔽,M} = - VectorSpaceAtPoint{𝔽,M,TangentSpaceType} where {𝔽,M<:AbstractManifold{𝔽}} - -""" - TangentSpaceAtPoint(M::AbstractManifold, p) - -Return an object of type [`VectorSpaceAtPoint`](@ref) representing tangent -space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. -""" -TangentSpaceAtPoint(M::AbstractManifold, p) = VectorSpaceAtPoint(M, TangentFiber, p) - -""" - TangentSpace(M::AbstractManifold, p) - -Return a [`TangentSpaceAtPoint`](@ref Manifolds.TangentSpaceAtPoint) representing tangent -space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. -""" -TangentSpace(M::AbstractManifold, p) = TangentSpaceAtPoint(M, p) - -const CotangentSpaceAtPoint{𝔽,M} = - VectorSpaceAtPoint{𝔽,CotangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} - -""" - CotangentSpaceAtPoint(M::AbstractManifold, p) - -Return an object of type [`VectorSpaceAtPoint`](@ref) representing cotangent -space at `p`. -""" -function CotangentSpaceAtPoint(M::AbstractManifold, p) - return VectorSpaceAtPoint(M, CotangentFiber, p) -end - """ allocate_result(B::BundleFibers, f, x...) Allocates an array for the result of function `f` that is -an element of the vector space of type `B.fiber` on manifold `B.manifold` +an element of the fiber space of type `B.fiber` on manifold `B.manifold` and arguments `x...` for implementing the non-modifying operation using the modifying operation. """ @@ -175,7 +71,7 @@ end allocate_result_type(B::BundleFibers, f, args::NTuple{N,Any}) where N Return type of element of the array that will represent the result of -function `f` for representing an operation with result in the vector space `fiber` +function `f` for representing an operation with result in the fiber `fiber` for manifold `M` on given arguments (passed at a tuple). """ @inline function allocate_result_type( @@ -189,159 +85,9 @@ end base_manifold(B::BundleFibers) = base_manifold(B.manifold) base_manifold(B::FiberAtPoint) = base_manifold(B.fiber) -""" - distance(M::TangentSpaceAtPoint, p, q) - -Distance between vectors `p` and `q` from the vector space `M`. It is calculated as the norm -of their difference. -""" -function distance(M::TangentSpaceAtPoint, p, q) - return norm(M.fiber.manifold, M.point, q - p) -end - -function embed!(M::TangentSpaceAtPoint, q, p) - return embed!(M.fiber.manifold, q, M.point, p) -end -function embed!(M::TangentSpaceAtPoint, Y, p, X) - return embed!(M.fiber.manifold, Y, M.point, X) -end - -@doc raw""" - exp(M::TangentSpaceAtPoint, p, X) - -Exponential map of tangent vectors `X` and `p` from the tangent space `M`. It is -calculated as their sum. -""" -exp(::TangentSpaceAtPoint, ::Any, ::Any) - -function exp!(M::TangentSpaceAtPoint, q, p, X) - copyto!(M.fiber.manifold, q, p + X) - return q -end - function fiber_dimension(B::BundleFibers) return fiber_dimension(B.manifold, B.fiber) end -fiber_dimension(M::TangentBundleFibers) = manifold_dimension(M.manifold) -fiber_dimension(M::CotangentBundleFibers) = manifold_dimension(M.manifold) -fiber_dimension(M::AbstractManifold, V::VectorSpaceFiberType) = fiber_dimension(M, V.fiber) -fiber_dimension(M::AbstractManifold, ::CotangentSpaceType) = manifold_dimension(M) -fiber_dimension(M::AbstractManifold, ::TangentSpaceType) = manifold_dimension(M) - -function get_basis(M::TangentSpaceAtPoint, p, B::CachedBasis) - return invoke( - get_basis, - Tuple{AbstractManifold,Any,CachedBasis}, - M.fiber.manifold, - M.point, - B, - ) -end -function get_basis(M::TangentSpaceAtPoint, p, B::AbstractBasis{<:Any,TangentSpaceType}) - return get_basis(M.fiber.manifold, M.point, B) -end - -function get_coordinates(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) - return get_coordinates(M.fiber.manifold, M.point, X, B) -end - -function get_coordinates!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) - return get_coordinates!(M.fiber.manifold, Y, M.point, X, B) -end - -function get_vector(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) - return get_vector(M.fiber.manifold, M.point, X, B) -end - -function get_vector!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) - return get_vector!(M.fiber.manifold, Y, M.point, X, B) -end - -function get_vectors(M::TangentSpaceAtPoint, p, B::CachedBasis) - return get_vectors(M.fiber.manifold, M.point, B) -end - -@doc raw""" - injectivity_radius(M::TangentSpaceAtPoint) - -Return the injectivity radius on the [`TangentSpaceAtPoint`](@ref Manifolds.TangentSpaceAtPoint) `M`, which is $∞$. -""" -injectivity_radius(::TangentSpaceAtPoint) = Inf - -""" - inner(M::TangentSpaceAtPoint, p, X, Y) - -Inner product of vectors `X` and `Y` from the tangent space at `M`. -""" -function inner(M::TangentSpaceAtPoint, p, X, Y) - return inner(M.fiber.manifold, M.point, X, Y) -end - -""" - is_flat(::TangentSpaceAtPoint) - -Return true. [`TangentSpaceAtPoint`](@ref Manifolds.TangentSpaceAtPoint) is a flat manifold. -""" -is_flat(::TangentSpaceAtPoint) = true - -function _isapprox(M::TangentSpaceAtPoint, X, Y; kwargs...) - return isapprox(M.fiber.manifold, M.point, X, Y; kwargs...) -end - -""" - log(M::TangentSpaceAtPoint, p, q) - -Logarithmic map on the tangent space manifold `M`, calculated as the difference of tangent -vectors `q` and `p` from `M`. -""" -log(::TangentSpaceAtPoint, ::Any...) -function log!(::TangentSpaceAtPoint, X, p, q) - copyto!(X, q - p) - return X -end - -function manifold_dimension(M::FiberAtPoint) - return fiber_dimension(M.fiber) -end - -LinearAlgebra.norm(M::VectorSpaceAtPoint, p, X) = norm(M.fiber.manifold, M.point, X) - -function parallel_transport_to!(M::TangentSpaceAtPoint, Y, p, X, q) - return copyto!(M.fiber.manifold, Y, p, X) -end - -@doc raw""" - project(M::TangentSpaceAtPoint, p) - -Project the point `p` from the tangent space `M`, that is project the vector `p` -tangent at `M.point`. -""" -project(::TangentSpaceAtPoint, ::Any) - -function project!(M::TangentSpaceAtPoint, q, p) - return project!(M.fiber.manifold, q, M.point, p) -end - -@doc raw""" - project(M::TangentSpaceAtPoint, p, X) - -Project the vector `X` from the tangent space `M`, that is project the vector `X` -tangent at `M.point`. -""" -project(::TangentSpaceAtPoint, ::Any, ::Any) - -function project!(M::TangentSpaceAtPoint, Y, p, X) - return project!(M.fiber.manifold, Y, M.point, X) -end - -function Random.rand!(rng::AbstractRNG, M::TangentSpaceAtPoint, X; vector_at=nothing) - rand!(rng, M.fiber.manifold, X; vector_at=M.point) - return X -end - -function representation_size(B::TangentSpaceAtPoint) - return representation_size(B.fiber.manifold) -end function Base.show(io::IO, fiber::BundleFibers) return print(io, "BundleFibers($(fiber.fiber), $(fiber.manifold))") @@ -358,24 +104,6 @@ function Base.show(io::IO, ::MIME"text/plain", vs::FiberAtPoint) sp = replace(sp, '\n' => "\n$(pre)") return print(io, pre, sp) end -function Base.show(io::IO, ::MIME"text/plain", TpM::TangentSpaceAtPoint) - println(io, "Tangent space to the manifold $(base_manifold(TpM)) at point:") - pre = " " - sp = sprint(show, "text/plain", TpM.point; context=io, sizehint=0) - sp = replace(sp, '\n' => "\n$(pre)") - return print(io, pre, sp) -end - -function vector_transport_to!( - M::TangentSpaceAtPoint, - Y, - p, - X, - q, - m::AbstractVectorTransportMethod, -) - return copyto!(M.fiber.manifold, Y, p, X) -end """ zero_vector(B::BundleFibers, p) @@ -387,15 +115,3 @@ function zero_vector(B::BundleFibers, p) X = allocate_result(B, zero_vector, p) return zero_vector!(B, X, p) end - -@doc raw""" - zero_vector(M::TangentSpaceAtPoint, p) - -Zero tangent vector at point `p` from the tangent space `M`, that is the zero tangent vector -at point `M.point`. -""" -zero_vector(::TangentSpaceAtPoint, ::Any...) - -function zero_vector!(M::VectorSpaceAtPoint, X, p) - return zero_vector!(M.fiber.manifold, X, M.point) -end diff --git a/src/manifolds/SkewHermitian.jl b/src/manifolds/SkewHermitian.jl index 8fdc87c757..0427cf8c8a 100644 --- a/src/manifolds/SkewHermitian.jl +++ b/src/manifolds/SkewHermitian.jl @@ -1,5 +1,5 @@ @doc raw""" - SkewHermitianMatrices{n,𝔽} <: AbstractDecoratorManifold{𝔽} + SkewHermitianMatrices{T,𝔽} <: AbstractDecoratorManifold{𝔽} The [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) $ \operatorname{SkewHerm}(n)$ consisting of the real- or complex-valued skew-hermitian matrices of size ``n × n``, i.e. the set @@ -14,11 +14,11 @@ Though it is slightly redundant, usually the matrices are stored as ``n × n`` a Note that in this representation, the real-valued part of the diagonal must be zero, which is also reflected in the -[`manifold_dimension`](@ref manifold_dimension(::SkewHermitianMatrices{N,𝔽}) where {N,𝔽}). +[`manifold_dimension`](@ref manifold_dimension(::SkewHermitianMatrices)). # Constructor - SkewHermitianMatrices(n::Int, field::AbstractNumbers=ℝ) + SkewHermitianMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type) Generate the manifold of ``n × n`` skew-hermitian matrices. """ diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 84ee32dab1..72fe58d8be 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -29,6 +29,11 @@ Deprecated: an alias for `FiberBundleProductVectorTransport`. """ const VectorBundleVectorTransport = FiberBundleProductVectorTransport +""" + VectorBundle{𝔽,TVS,TM,TVT} + +Alias for [`FiberBundle`](@ref) when fiber type is a [`VectorSpaceFiberType`](@ref) `TVS`. +""" const VectorBundle{𝔽,TVS,TM,TVT} = FiberBundle{ 𝔽, VectorSpaceFiberType{TVS}, diff --git a/src/manifolds/VectorFiber.jl b/src/manifolds/VectorFiber.jl new file mode 100644 index 0000000000..ebd86c7225 --- /dev/null +++ b/src/manifolds/VectorFiber.jl @@ -0,0 +1,292 @@ + +""" + TensorProductType(spaces::VectorSpaceType...) + +Vector space type corresponding to the tensor product of given vector space +types. +""" +struct TensorProductType{TS<:Tuple} <: VectorSpaceType + spaces::TS +end + +TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) + +""" + VectorSpaceFiberType{TVS<:VectorSpaceType} + +`FiberType` of a [`FiberBundle`](@ref) corresponding to [`VectorSpaceType`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases.html#ManifoldsBase.VectorSpaceType) +`TVS`. +""" +struct VectorSpaceFiberType{TVS<:VectorSpaceType} <: FiberType + fiber::TVS +end + +function Base.show(io::IO, vsf::VectorSpaceFiberType) + return print(io, "VectorSpaceFiberType($(vsf.fiber))") +end + +const TangentFiberType = VectorSpaceFiberType{TangentSpaceType} + +const CotangentFiberType = VectorSpaceFiberType{CotangentSpaceType} + +const TangentFiber = VectorSpaceFiberType{TangentSpaceType}(TangentSpace) +const CotangentFiber = VectorSpaceFiberType{CotangentSpaceType}(CotangentSpace) + +""" + VectorBundleFibers{TVS,TM} + +Alias for [`BundleFibers`](@ref) when the fiber is a vector space. +""" +const VectorBundleFibers{TVS,TM} = BundleFibers{ + VectorSpaceFiberType{TVS}, + TM, +} where {TVS<:VectorSpaceType,TM<:AbstractManifold} + +function VectorBundleFibers(fiber::VectorSpaceType, M::AbstractManifold) + return BundleFibers(VectorSpaceFiberType(fiber), M) +end +function VectorBundleFibers(fiber::VectorSpaceFiberType, M::AbstractManifold) + return BundleFibers(fiber, M) +end + +const TangentBundleFibers{M} = BundleFibers{TangentFiberType,M} where {M<:AbstractManifold} + +TangentBundleFibers(M::AbstractManifold) = BundleFibers(TangentFiber, M) + +const CotangentBundleFibers{M} = + BundleFibers{CotangentFiberType,M} where {M<:AbstractManifold} + +CotangentBundleFibers(M::AbstractManifold) = BundleFibers(CotangentFiber, M) + +""" + VectorSpaceAtPoint{𝔽,M,TFiber} + +Alias for [`FiberAtPoint`](@ref) when the fiber is a vector space. +""" +const VectorSpaceAtPoint{𝔽,M,TSpaceType} = FiberAtPoint{ + 𝔽, + VectorBundleFibers{TSpaceType,M}, +} where {𝔽,M<:AbstractManifold{𝔽},TSpaceType<:VectorSpaceType} + +function VectorSpaceAtPoint(M::AbstractManifold, fiber::VectorSpaceFiberType, p) + return FiberAtPoint(BundleFibers(fiber, M), p) +end + +""" + TangentSpaceAtPoint{𝔽,M} + +Alias for [`VectorSpaceAtPoint`](@ref) for the tangent space at a point. +""" +const TangentSpaceAtPoint{𝔽,M} = + VectorSpaceAtPoint{𝔽,M,TangentSpaceType} where {𝔽,M<:AbstractManifold{𝔽}} + +""" + TangentSpaceAtPoint(M::AbstractManifold, p) + +Return an object of type [`VectorSpaceAtPoint`](@ref) representing tangent +space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. +""" +TangentSpaceAtPoint(M::AbstractManifold, p) = VectorSpaceAtPoint(M, TangentFiber, p) + +""" + TangentSpace(M::AbstractManifold, p) + +Return a [`TangentSpaceAtPoint`] representing tangent +space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. +""" +TangentSpace(M::AbstractManifold, p) = TangentSpaceAtPoint(M, p) + +const CotangentSpaceAtPoint{𝔽,M} = + VectorSpaceAtPoint{𝔽,CotangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} + +""" + CotangentSpaceAtPoint(M::AbstractManifold, p) + +Return an object of type [`VectorSpaceAtPoint`](@ref) representing cotangent +space at `p`. +""" +function CotangentSpaceAtPoint(M::AbstractManifold, p) + return VectorSpaceAtPoint(M, CotangentFiber, p) +end + +""" + distance(M::TangentSpaceAtPoint, p, q) + +Distance between vectors `p` and `q` from the vector space `M`. It is calculated as the norm +of their difference. +""" +function distance(M::TangentSpaceAtPoint, p, q) + return norm(M.fiber.manifold, M.point, q - p) +end + +function embed!(M::TangentSpaceAtPoint, q, p) + return embed!(M.fiber.manifold, q, M.point, p) +end +function embed!(M::TangentSpaceAtPoint, Y, p, X) + return embed!(M.fiber.manifold, Y, M.point, X) +end + +@doc raw""" + exp(M::TangentSpaceAtPoint, p, X) + +Exponential map of tangent vectors `X` and `p` from the tangent space `M`. It is +calculated as their sum. +""" +exp(::TangentSpaceAtPoint, ::Any, ::Any) + +function exp!(M::TangentSpaceAtPoint, q, p, X) + copyto!(M.fiber.manifold, q, p + X) + return q +end + +fiber_dimension(M::TangentBundleFibers) = manifold_dimension(M.manifold) +fiber_dimension(M::CotangentBundleFibers) = manifold_dimension(M.manifold) +fiber_dimension(M::AbstractManifold, V::VectorSpaceFiberType) = fiber_dimension(M, V.fiber) +fiber_dimension(M::AbstractManifold, ::CotangentSpaceType) = manifold_dimension(M) +fiber_dimension(M::AbstractManifold, ::TangentSpaceType) = manifold_dimension(M) + +function get_basis(M::TangentSpaceAtPoint, p, B::CachedBasis) + return invoke( + get_basis, + Tuple{AbstractManifold,Any,CachedBasis}, + M.fiber.manifold, + M.point, + B, + ) +end +function get_basis(M::TangentSpaceAtPoint, p, B::AbstractBasis{<:Any,TangentSpaceType}) + return get_basis(M.fiber.manifold, M.point, B) +end + +function get_coordinates(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) + return get_coordinates(M.fiber.manifold, M.point, X, B) +end + +function get_coordinates!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) + return get_coordinates!(M.fiber.manifold, Y, M.point, X, B) +end + +function get_vector(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) + return get_vector(M.fiber.manifold, M.point, X, B) +end + +function get_vector!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) + return get_vector!(M.fiber.manifold, Y, M.point, X, B) +end + +function get_vectors(M::TangentSpaceAtPoint, p, B::CachedBasis) + return get_vectors(M.fiber.manifold, M.point, B) +end + +@doc raw""" + injectivity_radius(M::TangentSpaceAtPoint) + +Return the injectivity radius on the [`TangentSpaceAtPoint`](@ref) `M`, which is $∞$. +""" +injectivity_radius(::TangentSpaceAtPoint) = Inf + +""" + inner(M::TangentSpaceAtPoint, p, X, Y) + +Inner product of vectors `X` and `Y` from the tangent space at `M`. +""" +function inner(M::TangentSpaceAtPoint, p, X, Y) + return inner(M.fiber.manifold, M.point, X, Y) +end + +""" + is_flat(::TangentSpaceAtPoint) + +Return true. [`TangentSpaceAtPoint`](@ref) is a flat manifold. +""" +is_flat(::TangentSpaceAtPoint) = true + +function _isapprox(M::TangentSpaceAtPoint, X, Y; kwargs...) + return isapprox(M.fiber.manifold, M.point, X, Y; kwargs...) +end + +""" + log(M::TangentSpaceAtPoint, p, q) + +Logarithmic map on the tangent space manifold `M`, calculated as the difference of tangent +vectors `q` and `p` from `M`. +""" +log(::TangentSpaceAtPoint, ::Any...) +function log!(::TangentSpaceAtPoint, X, p, q) + copyto!(X, q - p) + return X +end + +function manifold_dimension(M::FiberAtPoint) + return fiber_dimension(M.fiber) +end + +LinearAlgebra.norm(M::VectorSpaceAtPoint, p, X) = norm(M.fiber.manifold, M.point, X) + +function parallel_transport_to!(M::TangentSpaceAtPoint, Y, p, X, q) + return copyto!(M.fiber.manifold, Y, p, X) +end + +@doc raw""" + project(M::TangentSpaceAtPoint, p) + +Project the point `p` from the tangent space `M`, that is project the vector `p` +tangent at `M.point`. +""" +project(::TangentSpaceAtPoint, ::Any) + +function project!(M::TangentSpaceAtPoint, q, p) + return project!(M.fiber.manifold, q, M.point, p) +end + +@doc raw""" + project(M::TangentSpaceAtPoint, p, X) + +Project the vector `X` from the tangent space `M`, that is project the vector `X` +tangent at `M.point`. +""" +project(::TangentSpaceAtPoint, ::Any, ::Any) + +function project!(M::TangentSpaceAtPoint, Y, p, X) + return project!(M.fiber.manifold, Y, M.point, X) +end + +function Random.rand!(rng::AbstractRNG, M::TangentSpaceAtPoint, X; vector_at=nothing) + rand!(rng, M.fiber.manifold, X; vector_at=M.point) + return X +end + +function representation_size(B::TangentSpaceAtPoint) + return representation_size(B.fiber.manifold) +end + +function Base.show(io::IO, ::MIME"text/plain", TpM::TangentSpaceAtPoint) + println(io, "Tangent space to the manifold $(base_manifold(TpM)) at point:") + pre = " " + sp = sprint(show, "text/plain", TpM.point; context=io, sizehint=0) + sp = replace(sp, '\n' => "\n$(pre)") + return print(io, pre, sp) +end + +function vector_transport_to!( + M::TangentSpaceAtPoint, + Y, + p, + X, + q, + m::AbstractVectorTransportMethod, +) + return copyto!(M.fiber.manifold, Y, p, X) +end + +@doc raw""" + zero_vector(M::TangentSpaceAtPoint, p) + +Zero tangent vector at point `p` from the tangent space `M`, that is the zero tangent vector +at point `M.point`. +""" +zero_vector(::TangentSpaceAtPoint, ::Any...) + +function zero_vector!(M::VectorSpaceAtPoint, X, p) + return zero_vector!(M.fiber.manifold, X, M.point) +end diff --git a/test/manifolds/fiber.jl b/test/manifolds/fiber.jl index ce052a0149..cf7c7bc2b8 100644 --- a/test/manifolds/fiber.jl +++ b/test/manifolds/fiber.jl @@ -37,8 +37,8 @@ struct TestVectorSpaceType <: VectorSpaceType end fiber_s = sprint(show, "text/plain", fiber) X_ps_test = "$(typeof(X_p))\nFiber:\n $(fiber_s)\nBase point:\n $(sp)" @test X_ps == X_ps_test - @test_throws ErrorException project(fiber, p, X) - @test_throws ErrorException norm(fiber, p, X) - @test_throws ErrorException distance(fiber, p, X, X) + @test_throws MethodError project(fiber, p, X) + @test_throws MethodError norm(fiber, p, X) + @test_throws MethodError distance(fiber, p, X, X) end end From c90e9a35cdb781e069c82db542fa6f5461d9650b Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 25 Sep 2023 10:40:04 +0200 Subject: [PATCH 33/81] generalize a couple of methods --- src/manifolds/FiberBundle.jl | 11 +++++++++++ src/manifolds/VectorBundle.jl | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index a94cd9affd..9dc935bba4 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -135,6 +135,17 @@ See also [`FiberBundleInverseProductRetraction`](@ref). """ struct FiberBundleProductRetraction <: AbstractRetractionMethod end +base_manifold(B::FiberBundle) = base_manifold(B.manifold) + +""" + bundle_projection(B::FiberBundle, 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 +of `p` is attached. +""" +bundle_projection(B::FiberBundle, p) = submanifold_component(B.manifold, p, Val(1)) + function get_basis(M::FiberBundle, p, B::AbstractBasis) xp1 = submanifold_component(p, Val(1)) base_basis = get_basis(M.manifold, xp1, B) diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 72fe58d8be..68f352d12b 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -86,17 +86,6 @@ function CotangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTrans return VectorBundle(CotangentSpace, M, vtm) end -base_manifold(B::VectorBundle) = base_manifold(B.manifold) - -""" - 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 -of `p` is attached. -""" -bundle_projection(B::VectorBundle, p) = submanifold_component(B.manifold, p, Val(1)) - function default_inverse_retraction_method(::TangentBundle) return FiberBundleInverseProductRetraction() end From bfe4fe0bc6c29299ae44517b67f93cc53d940753 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 25 Sep 2023 11:11:05 +0200 Subject: [PATCH 34/81] remove some less-developed parts --- NEWS.md | 7 +- docs/make.jl | 1 - src/Manifolds.jl | 1 - src/manifolds/DirectSumBundle.jl | 232 --------------------------- src/manifolds/FiberBundle.jl | 3 +- src/manifolds/VectorBundle.jl | 5 +- test/runtests.jl | 1 - tutorials/rigid-body-dynamics.qmd | 251 ------------------------------ 8 files changed, 9 insertions(+), 492 deletions(-) delete mode 100644 src/manifolds/DirectSumBundle.jl delete mode 100644 tutorials/rigid-body-dynamics.qmd diff --git a/NEWS.md b/NEWS.md index 4c61ccdafc..23c2457c0f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,13 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Vector bundles are generalized to fiber bundles. - `RotationTranslationAction` is introduced. -- `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 type parameter. For field storage, pass the `parameter=:field` keyword - argument to manifold constructor. + The default is set to store the size in type parameter, replicating the previous behavior. + For field storage, pass the `parameter=:field` keyword argument to manifold constructor. For example statically sized `CenteredMatrices{m,n}` is now `CenteredMatrices{TypeParameter{Tuple{m,n}}}`, whereas the type of special Euclidean group with field-stored size is `CenteredMatrices{Tuple{Int,Int}}`. Similar change applies to: - `CenteredMatrices{m,n}`, - `CholeskySpace{N}`, @@ -98,4 +97,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - `ProductRepr` is removed; please use `ArrayPartition` instead. -- Default methods throwing "not implemented" `ErrorException` for some group-related operations. +- Default methods throwing "not implemented" `ErrorException` for some group-related operations. Standard `MethodError` is now thrown instead. diff --git a/docs/make.jl b/docs/make.jl index 04e0a3cc89..1f22a75707 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -95,7 +95,6 @@ makedocs( "work in charts" => "tutorials/working-in-charts.md", "perform Hand gesture analysis" => "tutorials/hand-gestures.md", "integrate on manifolds and handle probability densities" => "tutorials/integration.md", - "do rigid body dynamics with geometry" => "tutorials/rigid-body-dynamics.md", ], "Manifolds" => [ "Basic manifolds" => [ diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 98ce866a33..03019dafad 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -346,7 +346,6 @@ include("manifolds/Fiber.jl") include("manifolds/FiberBundle.jl") include("manifolds/VectorFiber.jl") include("manifolds/VectorBundle.jl") -include("manifolds/DirectSumBundle.jl") include("groups/group.jl") # Features I: Which are extended on Meta Manifolds diff --git a/src/manifolds/DirectSumBundle.jl b/src/manifolds/DirectSumBundle.jl deleted file mode 100644 index 9482d4462e..0000000000 --- a/src/manifolds/DirectSumBundle.jl +++ /dev/null @@ -1,232 +0,0 @@ - -struct DirectSumType{TS<:Tuple} <: VectorSpaceType - spaces::TS -end - -DirectSumType(spaces::VectorSpaceType...) = DirectSumType{typeof(spaces)}(spaces) - -function fiber_dimension(M::AbstractManifold, V::DirectSumType) - dim = 0 - for space in V.spaces - dim += fiber_dimension(M, space) - end - return dim -end - -function vector_space_dimension(M::AbstractManifold, V::DirectSumType) - dim = 0 - for space in V.spaces - dim += vector_space_dimension(M, space) - end - return dim -end - -const MultitangentBundle{N,𝔽,M} = VectorBundle{ - 𝔽, - DirectSumType{NTuple{N,TangentSpaceType}}, - M, -} where {𝔽,M<:AbstractManifold{𝔽}} - -function MultitangentBundle{N}(M::AbstractManifold) where {N} - return VectorBundle(DirectSumType(ntuple(f -> TangentSpace, N)), M) -end - -const MultitangentBundleFibers{N,M} = VectorBundleFibers{ - DirectSumType{NTuple{N,TangentSpaceType}}, - M, -} where {M<:AbstractManifold} - -function MultitangentBundleFibers{N}(M::AbstractManifold) where {N} - return VectorBundleFibers(DirectSumType(ntuple(f -> TangentSpace, N)), M) -end - -const MultitangentSpaceAtPoint{𝔽,N,M} = VectorSpaceAtPoint{ - 𝔽, - M, - DirectSumType{NTuple{N,TangentSpaceType}}, -} where {𝔽,M<:AbstractManifold{𝔽}} -function MultitangentSpaceAtPoint{N}(M::AbstractManifold, p) where {N} - return VectorSpaceAtPoint(MultitangentBundleFibers{N}(M), p) -end - -@inline function allocate_result(B::MultitangentBundleFibers{N}, f::TF) where {N,TF} - return ArrayPartition(ntuple(_ -> allocate_result(B.manifold, f), Val(N))...) -end - -function default_inverse_retraction_method(::MultitangentBundle) - return FiberBundleInverseProductRetraction() -end - -function default_retraction_method(::MultitangentBundle) - return FiberBundleProductRetraction() -end - -function default_vector_transport_method(B::MultitangentBundle) - default_vt_m = default_vector_transport_method(B.manifold) - return FiberBundleProductVectorTransport(default_vt_m, default_vt_m) -end - -function get_basis(M::MultitangentBundleFibers, p, B::AbstractBasis{<:Any,TangentSpaceType}) - return get_basis(M.manifold, p, B) -end - -function get_coordinates(M::MultitangentBundleFibers, p, X, B::AbstractBasis) - return reduce(vcat, map(Xp -> get_coordinates(M.manifold, p, Xp, B), X.x)) -end - -function get_coordinates!(M::MultitangentBundleFibers, Y, p, X, B::AbstractBasis) - Y .= reduce(vcat, map(Xp -> get_coordinates(M.manifold, p, Xp, B), X.x)) - return Y -end - -function get_vector(M::MultitangentBundleFibers{N}, p, Xc, B::AbstractBasis) where {N} - d = manifold_dimension(M.manifold) - c_parts = ntuple(n -> view(Xc, (1 + ((n - 1) * d)):(n * d)), Val(N)) - return ArrayPartition(map(Xp -> get_vector(M.manifold, p, Xp, B), c_parts)...) -end - -function get_vector!(M::MultitangentBundleFibers{N}, Y, p, Xc, B::AbstractBasis) where {N} - d = manifold_dimension(M.manifold) - c_parts = ntuple(n -> view(Xc, (1 + ((n - 1) * d)):(n * d)), Val(N)) - map((Yp, Xcp) -> get_vector!(M.manifold, Yp, p, Xcp, B), Y.x, c_parts) - return Y -end - -function get_vectors(M::MultitangentBundleFibers{N}, p, B::CachedBasis) where {N} - bvs = get_vectors(M.manifold, p, B) - X0 = zero_vector(M.manifold, p) - vs = ArrayPartition{eltype(X0),NTuple{N,typeof(X0)}}[] - for i in 1:N - Xs1 = ntuple(_ -> X0, i - 1) - Xs2 = ntuple(_ -> X0, N - i) - for X in bvs - push!(vs, ArrayPartition(Xs1..., X, Xs2...)) - end - end - return vs -end - -function inner(M::MultitangentSpaceAtPoint, p, X, Y) - return sum(map((Xp, Yp) -> inner(M.fiber.manifold, M.point, Xp, Yp), X.x, Y.x)) -end -function inner(M::MultitangentBundleFibers, p, X, Y) - return sum(map((Xp, Yp) -> inner(M.manifold, p, X, Y), X.x, Y.x)) -end - -function Random.rand!(rng::AbstractRNG, M::MultitangentSpaceAtPoint, X; vector_at=nothing) - map(X.x) do Xp - return rand!(rng, M.fiber.manifold, Xp; vector_at=M.point) - end - return X -end - -function retract_product!(B::MultitangentBundle, q, p, X, t::Number) - tX = t * X - xp, Xps = submanifold_components(B.manifold, p) - xq, Xqs = submanifold_components(B.manifold, q) - VXM, VXFs = submanifold_components(B.manifold, tX) - # this temporary avoids overwriting `p` when `q` and `p` occupy the same memory - xqt = exp(B.manifold, xp, VXM) - map(Xps.x, Xqs.x, VXFs.x) do Xp, Xq, VXF - return vector_transport_direction!( - B.manifold, - Xq, - xp, - Xp + VXF, - VXM, - B.vector_transport.method_point, - ) - end - copyto!(B.manifold, xq, xqt) - return q -end - -function vector_transport_direction( - M::MultitangentBundleFibers, - p, - X, - d, - m::AbstractVectorTransportMethod, -) - return ArrayPartition(map(X.x) do VXF - return vector_transport_direction(M.manifold, p, VXF, d, m) - end) -end - -function _vector_transport_direction!( - M::MultitangentBundle, - Y, - p, - X, - d, - m::FiberBundleProductVectorTransport, -) - VYM, VYFs = submanifold_components(M.manifold, Y) - px, pVxs = submanifold_components(M.manifold, p) - VXM, VXFs = submanifold_components(M.manifold, X) - dx, dVxs = submanifold_components(M.manifold, d) - vector_transport_direction!(M.manifold, VYM, px, VXM, dx, m.method_point) - map(VYFs.x, VXFs.x) do VYF, VXF - return vector_transport_direction!(M.manifold, VYF, px, VXF, dx, m.method_fiber) - end - return Y -end - -function _vector_transport_to( - M::MultitangentBundle, - p, - X, - q, - m::FiberBundleProductVectorTransport, -) - px, pVx = submanifold_components(M.manifold, p) - VXM, VXFs = submanifold_components(M.manifold, X) - qx, qVx = submanifold_components(M.manifold, q) - vectors = map(VXFs.x) do VXF - return vector_transport_to(M.manifold, px, VXF, qx, m.method_fiber) - end - return ArrayPartition( - vector_transport_to(M.manifold, px, VXM, qx, m.method_point), - ArrayPartition(vectors...), - ) -end - -function vector_transport_to!( - M::MultitangentBundle, - Y, - p, - X, - q, - m::FiberBundleProductVectorTransport, -) - px, pVx = submanifold_components(M.manifold, p) - VXM, VXFs = submanifold_components(M.manifold, X) - VYM, VYFs = submanifold_components(M.manifold, Y) - qx, qVx = submanifold_components(M.manifold, q) - vector_transport_to!(M.manifold, VYM, px, VXM, qx, m.method_point) - map(VYFs.x, VXFs.x) do VYF, VXF - return vector_transport_to!(M.manifold, VYF, px, VXF, qx, m.method_fiber) - end - return Y -end -function vector_transport_to!( - M::MultitangentBundleFibers, - Y, - p, - X, - q, - m::AbstractVectorTransportMethod, -) - map(Y.x, X.x) do VYF, VXF - return vector_transport_to!(M.manifold, VYF, p, VXF, q, m) - end - return Y -end - -function zero_vector(B::MultitangentBundleFibers{N}, p) where {N} - return ArrayPartition(ntuple(_ -> zero_vector(B.manifold, p), Val(N))...) -end - -function zero_vector!(::MultitangentBundleFibers, X, p) - return fill!(X, 0) -end diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index 9dc935bba4..c9317254c3 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -41,7 +41,8 @@ end """ FiberBundle{𝔽,TVS<:FiberType,TM<:AbstractManifold{𝔽},TVT<:FiberBundleProductVectorTransport} <: AbstractManifold{𝔽} -Vector bundle on a [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M` of type [`VectorSpaceType`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases.html#ManifoldsBase.VectorSpaceType). +Fiber bundle on a [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M` +of type [`FiberType`](@ref). Examples include vector bundles, principal bundles or unit tangent bundles. # Constructor diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 68f352d12b..2c6be0e4ba 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -32,7 +32,10 @@ const VectorBundleVectorTransport = FiberBundleProductVectorTransport """ VectorBundle{𝔽,TVS,TM,TVT} -Alias for [`FiberBundle`](@ref) when fiber type is a [`VectorSpaceFiberType`](@ref) `TVS`. +Alias for [`FiberBundle`](@ref) when fiber type is a `TVS` of type +[`VectorSpaceType`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases.html#ManifoldsBase.VectorSpaceType). + +`VectorSpaceFiberType` is used to encode vector spaces as fiber types. """ const VectorBundle{𝔽,TVS,TM,TVT} = FiberBundle{ 𝔽, diff --git a/test/runtests.jl b/test/runtests.jl index 475bd06777..c5f5f730b1 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -179,7 +179,6 @@ include("utils.jl") include_test("manifolds/fiber.jl") include_test("manifolds/fiber_bundle.jl") include_test("manifolds/vector_bundle.jl") - include_test("manifolds/direct_sum_bundle.jl") include_test("manifolds/graph.jl") include_test("metric.jl") diff --git a/tutorials/rigid-body-dynamics.qmd b/tutorials/rigid-body-dynamics.qmd deleted file mode 100644 index 6b65c5007a..0000000000 --- a/tutorials/rigid-body-dynamics.qmd +++ /dev/null @@ -1,251 +0,0 @@ ---- -title: Rigid body dynamics ---- - -In this tutorial we will learn how rigid body dynamics relates to concepts from Riemannian geometry and Lie groups. - -```{julia} -#| echo: false -#| code-fold: true -#| output: false -using Pkg; -cd(@__DIR__) -Pkg.activate("."); # for reproducibility use the local tutorial environment. -using Markdown -``` - -In this tutorial we need to load a few libraries: -```{julia} -using Manifolds, LinearAlgebra, StaticArrays, RecursiveArrayTools, Plots -``` - -## 2-dimensional introduction - -We will start by considering 2-D rigid bodies. They are characterized by three numbers: position along $x$ and $x$ axes and an angle. -Geometrically it can be represented by elements of the special euclidean group in 2 dimensions, $\operatorname{SE}(2)$: - -```{julia} -SE2 = SpecialEuclidean(2) -se2_id = identity_element(SE2) -R2 = Euclidean(2) -``` - -For example, an object at $(2, 5)$ and at angle $\pi/3$ is represented by -```{julia} -p = exp(SE2, se2_id, hat(SE2, se2_id, [2.0, 5.0, pi/3])) -``` - -What does it do, exactly? This is one of many ways of creating a points on $\operatorname{SE}(2)$. -We can clearly recognize the numbers representing position and orientation of our object. -The `hat` function converts them to the Lie algebra of $\operatorname{SE}(2)$, denoted $\mathfrak{se}(2)$, while `exp` turns an element of $\mathfrak{se}(2)$ to an element of $\operatorname{SE}(2)$. -There are many ways of mapping between these two structures but `exp` is the one we need at the moment. - -Now we will draw our object as an arrow, with position represented by the central-left point in the default orientation. - -```{julia} -function draw_arrow_at(p; plt=nothing, arrow_kwargs=(;)) - if plt === nothing - plt = plot(; aspect_ratio=:equal) - end - # constructing matrix where columns are vertices of our arrow - pts = reduce(hcat, [[0.0, -0.125], [0.5, -0.125], [0.5, -0.25], [1.0, 0.0], [0.5, 0.25], [0.5, 0.125], [0.0, 0.125], [0.0, -0.125]]) - # action that transforms the arrow - A = Manifolds.ColumnwiseSpecialEuclideanAction(R2, SE2) - # applying the transformation - pts_pos = apply(A, p, pts) - # drawing the arrow - arr = Shape(pts_pos[1, :], pts_pos[2, :]) - plot!(plt, arr; arrow_kwargs...) -end -plt1 = plot(; aspect_ratio=:equal) -draw_arrow_at(se2_id; plt=plt1, arrow_kwargs=(; label="default position")) -draw_arrow_at(p; plt=plt1, arrow_kwargs=(; label="arrow at p")) -plt1 -``` - -### Simple transformations in global coordinates - -There are several ways of transforming position and orientation of an object. -The first one translates and rotates an object by composition. -In the following example, translation by $(-1, -2)$ and rotation by $-\pi/6$ are applied. - -```{julia} -p_tr = exp(SE2, se2_id, hat(SE2, se2_id, [-1.0, -2.0, -pi/6])) -p_transformed = compose(SE2, p_tr, p) -plt2 = plot(; aspect_ratio=:equal) -draw_arrow_at(p; plt=plt2, arrow_kwargs=(; label="arrow at p")) -draw_arrow_at(p_transformed; plt=plt2, arrow_kwargs=(; label="arrow after transformation")) -plt2 -``` - -Transformation is applied using group composition operation of $\operatorname{SE}(2)$. -`p_transformed` corresponds to active transformation, represented also by [left-forward](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Manifolds.LeftForwardAction) [group operation action](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Manifolds.GroupOperationAction): - -```{julia} -G_LF = GroupOperationAction(SE2, LeftForwardAction()) -p_transformed_2 = apply(G_LF, p_tr, p) -isapprox(p_transformed, p_transformed_2) -``` - -Note that translation and rotation happen in the global coordinates, not local ones. -We will cover local coordinates later. - -Basis transformation in the passive transformation can be understood as [right-forward](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Manifolds.RightForwardAction) group operation action. -First we need to get vectors constituting the orthonormal basis of tangent space to $\operatorname{SE}(2)$ at $p$. -These vectors are elements of the special Euclidean Lie algebra $\mathfrak{se}(2)$. -```{julia} -B_SE2 = get_basis(SE2, p, DefaultOrthonormalBasis()) -B_SE2_vecs = get_vectors(SE2, p, B_SE2) -``` - -Next we can use the right-forward action to transform these vectors. - -```{julia} -G_RF = GroupOperationAction(SE2, RightForwardAction()) -B_transformed = [apply(G_LF, p_tr, X_i) for X_i in B_SE2_vecs] -``` - -Notice that active transformation does not require a basis of the tangent space while the passive one does. - -### Local transformations - -Performing local transformations of an object requires different geometric tools. -Moving and rotating an object from its current position can be performed using an exponential map. -Its input are the current position and rotation (a point on $\operatorname{SE}(2)$) and a tangent vector that corresponds to the desired change. - -```{julia} -X = hat(SE2, se2_id, [-1.0, -2.0, -pi/6]) -``` - -Let's see how it affects our arrow: - -```{julia} -p_exp = exp(SE2, p, X) -println("Old position: ", p) -println("New position: ", p_exp) -plt3 = plot(; aspect_ratio=:equal) -draw_arrow_at(p; plt=plt3, arrow_kwargs=(; label="arrow at p")) -draw_arrow_at(p_exp; plt=plt3, arrow_kwargs=(; label="arrow at exp(M, p, X)")) -plt3 -``` - -As we can see, the arrow moved exactly as expected. - -The inverse problem, i.e. figuring out what transformation needs to be applied to transform one position and orientation to another one, is solved by a logarithmic map. -Additionally, we can use [`vee`](@ref) to get back the coordinates of a tangent vector: -```{julia} -X_from_log = log(SE2, p, p_exp) -vee(SE2, p, X_from_log) -``` - -which gives us back the same coordinates that we used for `X`. - -### A freely moving object - -A freely moving object moves at a constant velocity and rotates with a constant angular velocity. -Both velocity and angular velocity can be represented by tangent vectors. -The following plot shows what happens to an object in such conditions. - -```{julia} -ts = 0.0:0.5:10.0 -pts = [exp(SE2, p, t * X) for t in ts] -plt4 = plot(; aspect_ratio=:equal) -for (t, p) in zip(ts, pts) - draw_arrow_at(p; plt=plt4, arrow_kwargs=(; label="arrow at t=$t")) -end -plt4 -``` - -As we can see, the arrow moves at velocity defined by `X`: -1 space units per time unit in the $x$ direction, -2 space units per time unit in the $y$ direction and rotates by $\pi/6$ clockwise per time unit. -Any kind of free movement can be expressed using a tangent vector. -You may notice that our object actually follows a geodesic in the special Euclidean group. - -### Effects of external forces - -::: {.callout-caution} - -This tutorial, but most importantly this section, does not aim to follow a particular physical model of forces. -The aim is to get a description that can be reasonably used for motion estimation from noisy sensors. -However, the language used here is fairly close to what geometric descriptions of physical models use so likely it could be developed much futher. -Original author of this tutorial is not a physicist though so take it with a grain of salt. - -::: - -An external force acting upon a rigid body makes it accelerate or changes its angular velocity. -There are many possible geometric descriptions of these phenomena. -In this tutorial we will use the most simple one, i.e. representing acceleration as an additional tangent vector. -This ignores a lot of geometric content that could be exploited but it lets us move forward towards representing and analysing uncertainties caused by measurement inaccuracy. - -The freely moving object followed a trajectory $\gamma(t)=\exp_p(tX)$ -We can think of it as an object in the tangent bundle $T\operatorname{SE}(2)$ (though see technical note about cotangent bundle). -We could do again the trick with representing change as a tangent vector $(X_p, X_X) \in T_{(p, X)} T\operatorname{SE}(2)$ but it is a redundant representation: $X_p$ describes change in $p$ which is already described by $X$. -So all we need is the $X_X$ part of the tangent vector (to the tangent bundle). -Using the metric of $\operatorname{SE}(2)$ we can construct an isomorphism between the space in which $X_X$ lives in and the tangent space $T_p\operatorname{SE}(2)$. - -Finally we determined that an accelerating object can be described by a triple $(p, X, X_X)$. -We do not yet have a complete description of the dynamics: how does the triple evolve with time? -A freely moving object followed a geodesic. -An accelerating object could also just follow a geodesic but we would have to manufacture an appropriate affine connection (see: [📖 general relativity](https://en.wikipedia.org/wiki/General_relativity)). -We are trying to keep things relatively simple so we will use the following ODE: -```math -\begin{aligned} -\dot{p} &= X \\ -\dot{X} &= X_X\\ -\dot{X_X} &= 0 -\end{aligned} -``` - -To solve the ODE we still need an affine connection on the manifold $(p, X, X_X)$ lives on. -A nice way of describing that manifold in `Manifolds.jl` is by using a vector bundle where each fiber corresponds to direct sum of tangent spaces. -There is, however, one small caveat: `X_X` was brought from the double tangent bundle and depends on the connection we (should have) selected for the tangent bundle $T\operatorname{SE}(2)$. -So we may use the connection from $\operatorname{SE}(2)$ but we may also use the Levi-Civita connection of the Sasaki metric on $T\operatorname{SE}(2)$. -We shall explore this choice later in the tutorial. - -For simplicity again we will use a first order forward Euler discretization to solve our ODE (see `[technical note](@ref sec-forward-euler)`{=commonmark} if you disapprove). -For the initial conditions in our example we will use `p` and `X` from earlier examples and add `X_X`: -```{julia} -X_X = hat(SE2, se2_id, [0.5, -1.0, pi/6]) -``` - - -## Technical notes - -These technical notes elaborate on some aspects of the tutorial that a curious reader may find interesting but are not necessary to effectively work with `Manifolds.jl` - -### Different exponential maps and connections - -The exponential map we used in this tutorial is not the only one available on Lie groups. -Another choice is the so-called ``[Lie group exponential](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Manifolds.exp_lie-Tuple{SpecialEuclidean,%20Any})``{=commonmark}. -In the case of the special Euclidean group, Lie group exponential mixes the translation and rotation parts of a tangent vector: - -```{julia} -ts = 0.0:0.25:1.0 -pts = [exp_lie(SE2, t * X) for t in ts] -plt5 = plot(; aspect_ratio=:equal) -for (t, p) in zip(ts, pts) - draw_arrow_at(p; plt=plt5, arrow_kwargs=(; label="arrow at t=$t")) -end -plt5 -``` - -Note also that the default choice of connection for Lie groups is not the only one. -There are also ``[Cartan-Schouten connections](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/group.html#Cartan-Schouten-connections)``{=commonmark}. -They give the same exponential and logarithmic maps but a different parallel transport. - -### Jet bundles? - -Yes, [📖 jet bundles](https://en.wikipedia.org/wiki/Jet_bundle) are more mathematically enticing objects for describing dynamics but practical details of their implementation haven't been worked out yet. - -### Phase space as a contangent bundle - -Phase space in classical mechanics is typically defined as a contangent bundle. -Cotangent bundles are, however, hard to use for computation. -So we select Riesz representers of cotangent vectors according to the metric of our manifold. -In this way we get an isomorphism between tangent and cotangent bundles. - -Note that a generic description of a phase space doesn't require a Riemannian metric. -Generic tools come from symplectic geometry which imposes a less rich structure. - -### `[Forward Euler? Really?](@id sec-forward-euler)`{=commonmark} - -Yes, for simplicity. For a start. I may try a higher order or a symplectic solver later. You can do that too! From 3f04abdfa3538d2a1e69cfda854fe65058897642 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 26 Sep 2023 13:31:13 +0200 Subject: [PATCH 35/81] reworking action direction and invariant metrics --- NEWS.md | 1 + docs/src/manifolds/group.md | 5 +- ext/ManifoldsTestExt/tests_group.jl | 10 +- src/Manifolds.jl | 9 +- src/groups/GroupManifold.jl | 4 +- src/groups/addition_operation.jl | 8 +- src/groups/circle_group.jl | 14 +- src/groups/general_linear.jl | 4 +- src/groups/general_unitary_groups.jl | 4 +- src/groups/group.jl | 185 ++++++++--------- src/groups/group_action.jl | 30 +-- src/groups/group_operation_action.jl | 108 +++++----- src/groups/heisenberg.jl | 8 +- src/groups/metric.jl | 62 ++++-- src/groups/multiplication_operation.jl | 2 +- src/groups/power_group.jl | 33 +++- src/groups/product_group.jl | 16 +- src/groups/rotation_action.jl | 74 ++++--- src/groups/rotation_translation_action.jl | 76 +++---- src/groups/special_euclidean.jl | 4 +- src/groups/special_linear.jl | 4 +- src/groups/translation_action.jl | 27 +-- src/groups/translation_group.jl | 6 +- src/groups/validation_group.jl | 29 ++- test/groups/circle_group.jl | 5 +- test/groups/group_operation_action.jl | 28 +-- test/groups/groups_general.jl | 34 ++-- test/groups/metric.jl | 11 +- test/groups/rotation_action.jl | 11 +- test/groups/rotation_translation_action.jl | 8 +- test/groups/special_euclidean.jl | 218 ++++++++++----------- test/groups/translation_action.jl | 4 +- 32 files changed, 516 insertions(+), 526 deletions(-) diff --git a/NEWS.md b/NEWS.md index 23c2457c0f..3e6ae1018c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -93,6 +93,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ``` - Argument order for type aliases `RotationActionOnVector` and `RotationTranslationActionOnVector`: most often dispatched on argument is now first. +- `HasLeftInvariantMetric`, `HasRightInvariantMetric` and `HasBiinvariantMetric` now explicitly refer to using default implementations of invariant metric functions. If you provide your own implementations, these traits should not be specified. ### Removed diff --git a/docs/src/manifolds/group.md b/docs/src/manifolds/group.md index ebd1f780e3..78d7593db4 100644 --- a/docs/src/manifolds/group.md +++ b/docs/src/manifolds/group.md @@ -185,16 +185,17 @@ Order = [:constant, :type, :function] Group actions represent actions of a given group on a specified manifold. The following operations are available: +* [`action_side`](@ref): whether action acts from the [`LeftSide`](@ref) or [`RightSide`](@ref) (not to be confused with action direction). * [`apply`](@ref): performs given action of an element of the group on an object of compatible type. * [`apply_diff`](@ref): differential of [`apply`](@ref) with respect to the object it acts upon. -* [`direction`](@ref): tells whether a given action is [`LeftForwardAction`](@ref), [`RightForwardAction`](@ref), [`LeftBackwardAction`](@ref) or [`RightBackwardAction`](@ref). +* [`direction`](@ref): tells whether a given action is [`LeftAction`](@ref), [`RightAction`](@ref). * [`inverse_apply`](@ref): performs given action of the inverse of an element of the group on an object of compatible type. By default inverts the element and calls [`apply`](@ref) but it may be have a faster implementation for some actions. * [`inverse_apply_diff`](@ref): counterpart of [`apply_diff`](@ref) for [`inverse_apply`](@ref). * [`optimal_alignment`](@ref): determine the element of a group that, when it acts upon a point, produces the element closest to another given point in the metric of the G-manifold. Furthermore, group operation action features the following: -* [`translate`](@ref Main.Manifolds.translate): an operation that performs either left ([`LeftForwardAction`](@ref)) or right ([`RightBackwardAction`](@ref)) translation, or actions by inverses of elements ([`RightForwardAction`](@ref) and [`LeftBackwardAction`](@ref)). This is by default performed by calling [`compose`](@ref) with appropriate order of arguments. This function is separated from `compose` mostly to easily represent its differential, [`translate_diff`](@ref). +* [`translate`](@ref Main.Manifolds.translate): an operation that performs either ([`LeftAction`](@ref)) on the [`LeftSide`](@ref) or ([`RightAction`](@ref)) on the [`RightSide`](@ref) translation, or actions by inverses of elements ([`RightAction`](@ref) on the [`LeftSide`](@ref) and [`LeftAction`](@ref) on the [`RightSide`](@ref)). This is by default performed by calling [`compose`](@ref) with appropriate order of arguments. This function is separated from `compose` mostly to easily represent its differential, [`translate_diff`](@ref). * [`translate_diff`](@ref): differential of [`translate`](@ref Main.Manifolds.translate) with respect to the point being translated. * [`adjoint_action`](@ref): adjoint action of a given element of a Lie group on an element of its Lie algebra. * [`lie_bracket`](@ref): Lie bracket of two vectors from a Lie algebra corresponding to a given group. diff --git a/ext/ManifoldsTestExt/tests_group.jl b/ext/ManifoldsTestExt/tests_group.jl index 48c41a7427..10ead50598 100644 --- a/ext/ManifoldsTestExt/tests_group.jl +++ b/ext/ManifoldsTestExt/tests_group.jl @@ -1,4 +1,8 @@ using Base: IdentityUnitRange + +using Manifolds: + LeftForwardAction, LeftBackwardAction, RightForwardAction, RightBackwardAction + """ test_group( G, @@ -552,18 +556,18 @@ function test_action( test_mutating_group=true, test_mutating_action=true, test_diff=false, - test_switch_direction=Manifolds.LeftRightSwitch(), + test_switch_direction=true, ) G = base_group(A) M = group_manifold(A) e = Identity(G) Test.@testset "Basic action properties" begin # COV_EXCL_LINE - test_switch_direction !== false && Test.@testset "Direction" begin + test_switch_direction && Test.@testset "Direction" begin Aswitch = switch_direction(A) Test.@test direction(A) === _direction_from_type(A) - sd = switch_direction(_direction_from_type(A), test_switch_direction) + sd = switch_direction(_direction_from_type(A)) Test.@test isa(Aswitch, AbstractGroupAction{typeof(sd)}) Test.@test direction(Aswitch) === sd end diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 03019dafad..445bafa139 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -746,6 +746,7 @@ export CachedBasis, export ComponentManifoldError, CompositeManifoldError # Functions on Manifolds export ×, + action_side, allocate, allocate_result, base_manifold, @@ -906,9 +907,8 @@ export AbstractGroupAction, Identity, InvariantMetric, LeftAction, - LeftBackwardAction, - LeftForwardAction, LeftInvariantMetric, + LeftSide, MultiplicationOperation, Orthogonal, PowerGroup, @@ -916,9 +916,8 @@ export AbstractGroupAction, ProductOperation, RealCircleGroup, RightAction, - RightBackwardAction, - RightForwardAction, RightInvariantMetric, + RightSide, RotationAction, RotationTranslationAction, SemidirectProductGroup, @@ -950,6 +949,7 @@ export adjoint_action, compose, compose!, direction, + direction_and_side, exp_lie, exp_lie!, group_manifold, @@ -988,6 +988,7 @@ export adjoint_action, optimal_alignment!, screw_matrix, switch_direction, + switch_side, translate, translate!, translate_diff, diff --git a/src/groups/GroupManifold.jl b/src/groups/GroupManifold.jl index 9e117b3a79..86fa9fb2d3 100644 --- a/src/groups/GroupManifold.jl +++ b/src/groups/GroupManifold.jl @@ -46,7 +46,7 @@ function inverse_retract( q, method::GroupLogarithmicInverseRetraction, ) - conv = direction(method) + conv = direction_and_side(method) pinvq = inverse_translate(G, p, q, conv) Xₑ = log_lie(G, pinvq) return translate_diff(G, p, Identity(G), Xₑ, conv) @@ -60,7 +60,7 @@ function inverse_retract!( q, method::GroupLogarithmicInverseRetraction, ) - conv = direction(method) + conv = direction_and_side(method) pinvq = inverse_translate(G, p, q, conv) Xₑ = log_lie(G, pinvq) return translate_diff!(G, X, p, Identity(G), Xₑ, conv) diff --git a/src/groups/addition_operation.jl b/src/groups/addition_operation.jl index be37c9b688..2c0f110d47 100644 --- a/src/groups/addition_operation.jl +++ b/src/groups/addition_operation.jl @@ -93,7 +93,7 @@ function inverse_translate_diff( p, q, X, - ::ActionDirection, + ::ActionDirectionAndSide, ) return X end @@ -105,7 +105,7 @@ function inverse_translate_diff!( p, q, X, - ::ActionDirection, + ::ActionDirectionAndSide, ) return copyto!(G, Y, p, X) end @@ -138,7 +138,7 @@ function translate_diff( p, q, X, - ::ActionDirection, + ::ActionDirectionAndSide, ) return X end @@ -150,7 +150,7 @@ function translate_diff!( p, q, X, - ::ActionDirection, + ::ActionDirectionAndSide, ) return copyto!(G, Y, p, X) end diff --git a/src/groups/circle_group.jl b/src/groups/circle_group.jl index cb057778b6..58cbba4278 100644 --- a/src/groups/circle_group.jl +++ b/src/groups/circle_group.jl @@ -15,7 +15,6 @@ CircleGroup() = GroupManifold(Circle{ℂ}(), MultiplicationOperation()) else return merge_traits( IsGroupManifold(M.op), - HasBiinvariantMetric(), IsDefaultMetric(EuclideanMetric()), active_traits(f, M.manifold, args...), IsExplicitDecorator(), #pass to Euclidean by default/last fallback @@ -48,6 +47,11 @@ function compose!( return copyto!(x, compose(G, p, q)) end +has_biinvariant_metric(::CircleGroup) = true + +has_invariant_metric(::CircleGroup) = true +has_invariant_metric(::CircleGroup, ::ActionDirectionAndSide) = true + identity_element(G::CircleGroup) = 1.0 identity_element(::CircleGroup, p::Number) = one(p) @@ -74,18 +78,20 @@ lie_bracket(::CircleGroup, X, Y) = zero(X) 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, p, q, X, ::ActionDirectionAndSide) where {GT<:CircleGroup} + return map(*, p, X) +end function translate_diff( ::CircleGroup, ::Identity{MultiplicationOperation}, q, X, - ::ActionDirection, + ::ActionDirectionAndSide, ) return X end -function translate_diff!(G::CircleGroup, Y, p, q, X, conv::ActionDirection) +function translate_diff!(G::CircleGroup, Y, p, q, X, conv::ActionDirectionAndSide) return copyto!(Y, translate_diff(G, p, q, X, conv)) end diff --git a/src/groups/general_linear.jl b/src/groups/general_linear.jl index 62673e6519..8f2f24d21b 100644 --- a/src/groups/general_linear.jl +++ b/src/groups/general_linear.jl @@ -168,7 +168,7 @@ inner(::GeneralLinear, p, X, Y) = dot(X, Y) inverse_translate_diff(::GeneralLinear, p, q, X, ::LeftForwardAction) = X inverse_translate_diff(::GeneralLinear, p, q, X, ::RightBackwardAction) = p * X / p -function inverse_translate_diff!(G::GeneralLinear, Y, p, q, X, conv::ActionDirection) +function inverse_translate_diff!(G::GeneralLinear, Y, p, q, X, conv::ActionDirectionAndSide) return copyto!(Y, inverse_translate_diff(G, p, q, X, conv)) end @@ -278,6 +278,6 @@ end translate_diff(::GeneralLinear, p, q, X, ::LeftForwardAction) = X translate_diff(::GeneralLinear, p, q, X, ::RightBackwardAction) = p \ X * p -function translate_diff!(G::GeneralLinear, Y, p, q, X, conv::ActionDirection) +function translate_diff!(G::GeneralLinear, Y, p, q, X, conv::ActionDirectionAndSide) return copyto!(Y, translate_diff(G, p, q, X, conv)) end diff --git a/src/groups/general_unitary_groups.jl b/src/groups/general_unitary_groups.jl index 64d2026f68..40c56a179d 100644 --- a/src/groups/general_unitary_groups.jl +++ b/src/groups/general_unitary_groups.jl @@ -169,7 +169,7 @@ function inverse_translate_diff( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) return translate_diff(G, inv(G, p), q, X, conv) end @@ -179,7 +179,7 @@ function inverse_translate_diff!( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) return copyto!(Y, inverse_translate_diff(G, p, q, X, conv)) end diff --git a/src/groups/group.jl b/src/groups/group.jl index b91587236b..ed54b34796 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -46,27 +46,30 @@ abstract type AbstractInvarianceTrait <: AbstractTrait end """ HasLeftInvariantMetric <: AbstractInvarianceTrait -Specify that a certain the metric of a [`GroupManifold`](@ref) is a left-invariant metric +Specify that the default metric functions for the left-invariant metric on a [`GroupManifold`](@ref) +are to be used. """ struct HasLeftInvariantMetric <: AbstractInvarianceTrait end -direction(::HasLeftInvariantMetric) = LeftForwardAction() -direction(::Type{HasLeftInvariantMetric}) = LeftForwardAction() +direction_and_side(::HasLeftInvariantMetric) = LeftForwardAction() +direction_and_side(::Type{HasLeftInvariantMetric}) = LeftForwardAction() """ HasRightInvariantMetric <: AbstractInvarianceTrait -Specify that a certain the metric of a [`GroupManifold`](@ref) is a right-invariant metric +Specify that the default metric functions for the right-invariant metric on a [`GroupManifold`](@ref) +are to be used. """ struct HasRightInvariantMetric <: AbstractInvarianceTrait end -direction(::HasRightInvariantMetric) = RightBackwardAction() -direction(::Type{HasRightInvariantMetric}) = RightBackwardAction() +direction_and_side(::HasRightInvariantMetric) = RightBackwardAction() +direction_and_side(::Type{HasRightInvariantMetric}) = RightBackwardAction() """ HasBiinvariantMetric <: AbstractInvarianceTrait -Specify that a certain the metric of a [`GroupManifold`](@ref) is a bi-invariant metric +Specify that the default metric functions for the bi-invariant metric on a [`GroupManifold`](@ref) +are to be used. """ struct HasBiinvariantMetric <: AbstractInvarianceTrait end function parent_trait(::HasBiinvariantMetric) @@ -107,39 +110,23 @@ base_group(M::AbstractDecoratorManifold) = M """ ActionDirection -Direction of action on a manifold, either [`LeftForwardAction`](@ref), -[`LeftBackwardAction`](@ref), [`RightForwardAction`](@ref) or [`RightBackwardAction`](@ref). +Direction of action on a manifold, either [`LeftAction`](@ref) or [`RightAction`](@ref). """ abstract type ActionDirection end @doc raw""" - LeftForwardAction() + LeftAction() -Left action of a group on a manifold. For an action ``α: G × X → X`` it is characterized by +Left action of a group on a manifold. For a forward action ``α: G × X → X`` it is characterized by ```math α(g, α(h, x)) = α(gh, x) ``` for all ``g, h ∈ G`` and ``x ∈ X``. """ -struct LeftForwardAction <: ActionDirection end - -@doc raw""" - LeftBackwardAction() - -Left action of a group on a manifold. For an action ``α: X × G → X`` it is characterized by -```math -α(α(x, h), g) = α(x, gh) -``` -for all ``g, h ∈ G`` and ``x ∈ X``. - -Note that a left action may still act from the right side in an expression. -""" -struct LeftBackwardAction <: ActionDirection end - -const LeftAction = LeftForwardAction +struct LeftAction <: ActionDirection end """ - RightForwardAction() + RightAction() Right action of a group on a manifold. For an action ``α: G × X → X`` it is characterized by ```math @@ -149,70 +136,60 @@ for all ``g, h ∈ G`` and ``x ∈ X``. Note that a right action may still act from the left side in an expression. """ -struct RightForwardAction <: ActionDirection end +struct RightAction <: ActionDirection end """ - RightBackwardAction() - -Right action of a group on a manifold. For an action ``α: X × G → X`` it is characterized by -```math -α(α(x, h), g) = α(x, hg) -``` -for all ``g, h ∈ G`` and ``x ∈ X``. + GroupActionSide -Note that a right action may still act from the left side in an expression. +Side of action on a manifold, either [`LeftSide`](@ref) or [`RightSide`](@ref). """ -struct RightBackwardAction <: ActionDirection end - -const RightAction = RightBackwardAction - -abstract type AbstractDirectionSwitchType end +abstract type GroupActionSide end """ - struct LeftRightSwitch <: AbstractDirectionSwitchType end + LeftSide() -Switch between left and right action, maintaining forward/backward direction. +An action of a group on a manifold that acts from the left side, i.e. ``α: G × X → X``. """ -struct LeftRightSwitch <: AbstractDirectionSwitchType end +struct LeftSide <: GroupActionSide end """ - struct ForwardBackwardSwitch <: AbstractDirectionSwitchType end + RightSide() -Switch between forward and backward action, maintaining left/right direction. +An action of a group on a manifold that acts from the right side, i.e. ``α: X × G→ X``. """ -struct ForwardBackwardSwitch <: AbstractDirectionSwitchType end +struct RightSide <: GroupActionSide end """ - struct LeftRightSwitch <: AbstractDirectionSwitchType end + switch_direction(::ActionDirection) -Simultaneously switch left/right and forward/backward directions. +Returns type of action between left and right. +This function does not affect side of action, see [`switch_side`](@ref). """ -struct SimultaneousSwitch <: AbstractDirectionSwitchType end +switch_direction(::ActionDirection) +switch_direction(::LeftAction) = RightAction() +switch_direction(::RightAction) = LeftAction() """ - switch_direction(::ActionDirection, type::AbstractDirectionSwitchType = SimultaneousSwitch()) + switch_side(::GroupActionSide) -Returns type of action between left and right, forward or backward, or both at the same type, -depending on `type`, which is either of `LeftRightSwitch`, `ForwardBackwardSwitch` or -`SimultaneousSwitch`. +Returns side of action between left and right. +This function does not affect the action being left or right, see [`switch_direction`](@ref). """ -switch_direction(::ActionDirection, type::AbstractDirectionSwitchType) -switch_direction(AD::ActionDirection) = switch_direction(AD, SimultaneousSwitch()) +switch_side(::GroupActionSide) +switch_side(::LeftSide) = RightSide() +switch_side(::RightSide) = LeftSide() -switch_direction(::LeftForwardAction, ::LeftRightSwitch) = RightForwardAction() -switch_direction(::LeftBackwardAction, ::LeftRightSwitch) = RightBackwardAction() -switch_direction(::RightForwardAction, ::LeftRightSwitch) = LeftForwardAction() -switch_direction(::RightBackwardAction, ::LeftRightSwitch) = LeftBackwardAction() +const ActionDirectionAndSide = Tuple{ActionDirection,GroupActionSide} -switch_direction(::LeftForwardAction, ::ForwardBackwardSwitch) = LeftBackwardAction() -switch_direction(::LeftBackwardAction, ::ForwardBackwardSwitch) = LeftForwardAction() -switch_direction(::RightForwardAction, ::ForwardBackwardSwitch) = RightBackwardAction() -switch_direction(::RightBackwardAction, ::ForwardBackwardSwitch) = RightForwardAction() +const LeftForwardAction = Tuple{LeftAction,LeftSide} +const LeftBackwardAction = Tuple{LeftAction,RightSide} +const RightForwardAction = Tuple{RightAction,LeftSide} +const RightBackwardAction = Tuple{RightAction,RightSide} -switch_direction(::LeftForwardAction, ::SimultaneousSwitch) = RightBackwardAction() -switch_direction(::LeftBackwardAction, ::SimultaneousSwitch) = RightForwardAction() -switch_direction(::RightForwardAction, ::SimultaneousSwitch) = LeftBackwardAction() -switch_direction(::RightBackwardAction, ::SimultaneousSwitch) = LeftForwardAction() +LeftForwardAction() = (LeftAction(), LeftSide()) +LeftBackwardAction() = (LeftAction(), RightSide()) +RightForwardAction() = (RightAction(), LeftSide()) +RightBackwardAction() = (RightAction(), RightSide()) @doc raw""" Identity{O<:AbstractGroupOperation} @@ -742,7 +719,7 @@ _action_order(BG::AbstractDecoratorManifold, p, q, ::RightForwardAction) = (inv( _action_order(BG::AbstractDecoratorManifold, p, q, ::RightBackwardAction) = (q, p) @doc raw""" - translate(G::AbstractDecoratorManifold, p, q, conv::ActionDirection=LeftForwardAction()]) + translate(G::AbstractDecoratorManifold, p, q, conv::ActionDirectionAndSide=LeftForwardAction()]) Translate group element $q$ by $p$ with the translation $τ_p$ with the specified `conv`ention, either left forward ($L_p$), left backward ($R'_p$), right backward ($R_p$) @@ -761,14 +738,14 @@ translate(::AbstractDecoratorManifold, ::Any...) G::AbstractDecoratorManifold, p, q, - conv::ActionDirection=LeftForwardAction(), + conv::ActionDirectionAndSide=LeftForwardAction(), ) function translate( ::TraitList{<:IsGroupManifold}, G::AbstractDecoratorManifold, p, q, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) BG = base_group(G) return compose(BG, _action_order(BG, p, q, conv)...) @@ -779,7 +756,7 @@ end X, p, q, - conv::ActionDirection=LeftForwardAction(), + conv::ActionDirectionAndSide=LeftForwardAction(), ) function translate!( ::TraitList{<:IsGroupManifold}, @@ -787,14 +764,14 @@ function translate!( X, p, q, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) BG = base_group(G) return compose!(BG, X, _action_order(BG, p, q, conv)...) end @doc raw""" - inverse_translate(G::AbstractDecoratorManifold, p, q, conv::ActionDirection=LeftForwardAction()) + inverse_translate(G::AbstractDecoratorManifold, p, q, conv::ActionDirectionAndSide=LeftForwardAction()) Inverse translate group element $q$ by $p$ with the inverse translation $τ_p^{-1}$ with the specified `conv`ention, either left ($L_p^{-1}$) or right ($R_p^{-1}$), defined as @@ -810,14 +787,14 @@ inverse_translate(::AbstractDecoratorManifold, ::Any...) G::AbstractDecoratorManifold, p, q, - conv::ActionDirection=LeftForwardAction(), + conv::ActionDirectionAndSide=LeftForwardAction(), ) function inverse_translate( ::TraitList{<:IsGroupManifold}, G::AbstractDecoratorManifold, p, q, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) BG = base_group(G) return translate(BG, inv(BG, p), q, conv) @@ -828,7 +805,7 @@ end X, p, q, - conv::ActionDirection=LeftForwardAction(), + conv::ActionDirectionAndSide=LeftForwardAction(), ) function inverse_translate!( ::TraitList{<:IsGroupManifold}, @@ -836,14 +813,14 @@ function inverse_translate!( X, p, q, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) BG = base_group(G) return translate!(BG, X, inv(BG, p), q, conv) end @doc raw""" - translate_diff(G::AbstractDecoratorManifold, p, q, X, conv::ActionDirection=LeftForwardAction()) + translate_diff(G::AbstractDecoratorManifold, p, q, X, conv::ActionDirectionAndSide=LeftForwardAction()) For group elements $p, q ∈ \mathcal{G}$ and tangent vector $X ∈ T_q \mathcal{G}$, compute the action of the differential of the translation $τ_p$ by $p$ on $X$, with the specified @@ -858,7 +835,7 @@ translate_diff(::AbstractDecoratorManifold, ::Any...) p, q, X, - conv::ActionDirection=LeftForwardAction(), + conv::ActionDirectionAndSide=LeftForwardAction(), ) function translate_diff( ::TraitList{<:IsGroupManifold}, @@ -866,7 +843,7 @@ function translate_diff( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) Y = allocate_result(G, translate_diff, X, p, q) BG = base_group(G) @@ -879,11 +856,11 @@ end p, q, X, - conv::ActionDirection=LeftForwardAction(), + conv::ActionDirectionAndSide=LeftForwardAction(), ) @doc raw""" - inverse_translate_diff(G::AbstractDecoratorManifold, p, q, X, conv::ActionDirection=LeftForwardAction()) + inverse_translate_diff(G::AbstractDecoratorManifold, p, q, X, conv::ActionDirectionAndSide=LeftForwardAction()) For group elements $p, q ∈ \mathcal{G}$ and tangent vector $X ∈ T_q \mathcal{G}$, compute the action on $X$ of the differential of the inverse translation $τ_p$ by $p$, with the @@ -898,7 +875,7 @@ inverse_translate_diff(::AbstractDecoratorManifold, ::Any...) p, q, X, - conv::ActionDirection=LeftForwardAction(), + conv::ActionDirectionAndSide=LeftForwardAction(), ) function inverse_translate_diff( ::TraitList{<:IsGroupManifold}, @@ -906,7 +883,7 @@ function inverse_translate_diff( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) BG = base_group(G) return translate_diff(BG, inv(BG, p), q, X, conv) @@ -918,7 +895,7 @@ end p, q, X, - conv::ActionDirection=LeftForwardAction(), + conv::ActionDirectionAndSide=LeftForwardAction(), ) function inverse_translate_diff!( ::TraitList{<:IsGroupManifold}, @@ -927,7 +904,7 @@ function inverse_translate_diff!( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) BG = base_group(G) return translate_diff!(BG, Y, inv(BG, p), q, X, conv) @@ -1048,7 +1025,7 @@ function log_lie!( end """ - GroupExponentialRetraction{D<:ActionDirection} <: AbstractRetractionMethod + GroupExponentialRetraction{D<:ActionDirectionAndSide} <: AbstractRetractionMethod Retraction using the group exponential [`exp_lie`](@ref) "translated" to any point on the manifold. @@ -1058,16 +1035,16 @@ For more details, see # Constructor - GroupExponentialRetraction(conv::ActionDirection = LeftForwardAction()) + GroupExponentialRetraction(conv::ActionDirectionAndSide = LeftAction()) """ -struct GroupExponentialRetraction{D<:ActionDirection} <: AbstractRetractionMethod end +struct GroupExponentialRetraction{D<:ActionDirectionAndSide} <: AbstractRetractionMethod end -function GroupExponentialRetraction(conv::ActionDirection=LeftForwardAction()) +function GroupExponentialRetraction(conv::ActionDirectionAndSide=LeftForwardAction()) return GroupExponentialRetraction{typeof(conv)}() end """ - GroupLogarithmicInverseRetraction{D<:ActionDirection} <: AbstractInverseRetractionMethod + GroupLogarithmicInverseRetraction{D<:ActionDirectionAndSide} <: AbstractInverseRetractionMethod Retraction using the group logarithm [`log_lie`](@ref) "translated" to any point on the manifold. @@ -1077,24 +1054,24 @@ For more details, see # Constructor - GroupLogarithmicInverseRetraction(conv::ActionDirection = LeftForwardAction()) + GroupLogarithmicInverseRetraction(conv::ActionDirectionAndSide = LeftForwardAction()) """ -struct GroupLogarithmicInverseRetraction{D<:ActionDirection} <: +struct GroupLogarithmicInverseRetraction{D<:ActionDirectionAndSide} <: AbstractInverseRetractionMethod end -function GroupLogarithmicInverseRetraction(conv::ActionDirection=LeftForwardAction()) +function GroupLogarithmicInverseRetraction(conv::ActionDirectionAndSide=LeftForwardAction()) return GroupLogarithmicInverseRetraction{typeof(conv)}() end -direction(::GroupExponentialRetraction{D}) where {D} = D() -direction(::GroupLogarithmicInverseRetraction{D}) where {D} = D() +direction_and_side(::GroupExponentialRetraction{D}) where {D} = D() +direction_and_side(::GroupLogarithmicInverseRetraction{D}) where {D} = D() @doc raw""" retract( G::AbstractDecoratorManifold, p, X, - method::GroupExponentialRetraction{<:ActionDirection}, + method::GroupExponentialRetraction, ) Compute the retraction using the group exponential [`exp_lie`](@ref) "translated" to any @@ -1117,7 +1094,7 @@ function retract( X, method::GroupExponentialRetraction, ) - conv = direction(method) + conv = direction_and_side(method) Xₑ = inverse_translate_diff(G, p, p, X, conv) pinvq = exp_lie(G, Xₑ) q = translate(G, p, pinvq, conv) @@ -1142,7 +1119,7 @@ function retract!( X, method::GroupExponentialRetraction, ) - conv = direction(method) + conv = direction_and_side(method) Xₑ = inverse_translate_diff(G, p, p, X, conv) pinvq = exp_lie(G, Xₑ) return translate!(G, q, p, pinvq, conv) @@ -1164,7 +1141,7 @@ end G::AbstractDecoratorManifold, p, X, - method::GroupLogarithmicInverseRetraction{<:ActionDirection}, + method::GroupLogarithmicInverseRetraction, ) Compute the inverse retraction using the group logarithm [`log_lie`](@ref) "translated" @@ -1187,7 +1164,7 @@ function inverse_retract( q, method::GroupLogarithmicInverseRetraction, ) - conv = direction(method) + conv = direction_and_side(method) pinvq = inverse_translate(G, p, q, conv) Xₑ = log_lie(G, pinvq) return translate_diff(G, p, Identity(G), Xₑ, conv) @@ -1201,7 +1178,7 @@ function inverse_retract!( q, method::GroupLogarithmicInverseRetraction, ) - conv = direction(method) + conv = direction_and_side(method) pinvq = inverse_translate(G, p, q, conv) Xₑ = log_lie(G, pinvq) return translate_diff!(G, X, p, Identity(G), Xₑ, conv) diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index bd63c21939..1d74946582 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -64,9 +64,9 @@ Apply action `a` to the point `p` with the rule specified by `A`. The result is saved in `q`. """ apply!(A::AbstractGroupAction, q, a, p) -function apply!(A::AbstractGroupAction{RightForwardAction}, q, a, p) +function apply!(A::AbstractGroupAction{RightAction}, q, a, p) ainv = inv(base_group(A), a) - apply!(switch_direction(A, LeftRightSwitch()), q, ainv, p) + apply!(switch_direction(A), q, ainv, p) return q end @@ -150,35 +150,17 @@ function inverse_apply_diff!(A::AbstractGroupAction, Y, a, p, X) return apply_diff!(A, Y, inv(base_group(A), a), p, X) end -function compose( - A::AbstractGroupAction{<:Union{LeftForwardAction,LeftBackwardAction}}, - a, - b, -) +function compose(A::AbstractGroupAction{LeftAction}, a, b) return compose(base_group(A), a, b) end -function compose( - A::AbstractGroupAction{<:Union{RightForwardAction,RightBackwardAction}}, - a, - b, -) +function compose(A::AbstractGroupAction{RightAction}, a, b) return compose(base_group(A), b, a) end -function compose!( - A::AbstractGroupAction{<:Union{LeftForwardAction,LeftBackwardAction}}, - q, - a, - b, -) +function compose!(A::AbstractGroupAction{LeftAction}, q, a, b) return compose!(base_group(A), q, a, b) end -function compose!( - A::AbstractGroupAction{<:Union{RightForwardAction,RightBackwardAction}}, - q, - a, - b, -) +function compose!(A::AbstractGroupAction{RightAction}, q, a, b) return compose!(base_group(A), q, b, a) end diff --git a/src/groups/group_operation_action.jl b/src/groups/group_operation_action.jl index 3963d5135b..8a8fb19c68 100644 --- a/src/groups/group_operation_action.jl +++ b/src/groups/group_operation_action.jl @@ -1,21 +1,49 @@ @doc raw""" - GroupOperationAction(group::AbstractDecoratorManifold, AD::ActionDirection = LeftForwardAction()) + GroupOperationAction{AD<:ActionDirection,AS<:GroupActionSide,G<:AbstractDecoratorManifold} <: AbstractGroupAction{AD} Action of a group upon itself via left or right translation. + +# Constructor + + GroupOperationAction(group::AbstractDecoratorManifold, AD::ActionDirectionAndSide = LeftForwardAction()) """ -struct GroupOperationAction{G,AD} <: AbstractGroupAction{AD} +struct GroupOperationAction{ + AD<:ActionDirection, + AS<:GroupActionSide, + G<:AbstractDecoratorManifold, +} <: AbstractGroupAction{AD} group::G end function GroupOperationAction( G::TM, ::TAD=LeftForwardAction(), -) where {TM<:AbstractDecoratorManifold,TAD<:ActionDirection} - return GroupOperationAction{TM,TAD}(G) +) where {TM<:AbstractDecoratorManifold,TAD<:ActionDirectionAndSide} + return GroupOperationAction{TAD.parameters[1],TAD.parameters[2],TM}(G) +end + +""" + action_side(A::GroupOperationAction) + +Return whether [`GroupOperationAction`](@ref) `A` acts on the [`LeftSide`](@ref) or +[`RightSide`](@ref). +""" +action_side(::GroupOperationAction{AD,AS}) where {AD<:ActionDirection,AS<:GroupActionSide} = + AS() + +function direction_and_side( + ::GroupOperationAction{AD,AS}, +) where {AD<:ActionDirection,AS<:GroupActionSide} + return (AD(), AS()) +end +function reverse_direction_and_side( + ::GroupOperationAction{AD,AS}, +) where {AD<:ActionDirection,AS<:GroupActionSide} + return (switch_direction(AD()), switch_side(AS())) end function Base.show(io::IO, A::GroupOperationAction) - return print(io, "GroupOperationAction($(A.group), $(direction(A)))") + return print(io, "GroupOperationAction($(A.group), $(direction_and_side(A)))") end base_group(A::GroupOperationAction) = A.group @@ -23,84 +51,68 @@ base_group(A::GroupOperationAction) = A.group group_manifold(A::GroupOperationAction) = A.group function switch_direction(A::GroupOperationAction) - return GroupOperationAction(A.group, switch_direction(direction(A))) + return GroupOperationAction(A.group, (switch_direction(direction(A)), action_side(A))) end -function adjoint_apply_diff_group( - A::GroupOperationAction{<:AbstractDecoratorManifold,AD}, - a, - X, - p, -) where {AD<:ActionDirection} +function switch_direction_and_side(A::GroupOperationAction) + return GroupOperationAction(A.group, reverse_direction_and_side(A)) +end + +function adjoint_apply_diff_group(A::GroupOperationAction, a, X, p) G = base_group(A) - return inverse_translate_diff(G, a, p, X, switch_direction(AD())) + return inverse_translate_diff(G, a, p, X, reverse_direction_and_side(A)) end -function adjoint_apply_diff_group!( - A::GroupOperationAction{<:AbstractDecoratorManifold,AD}, - Y, - a, - X, - p, -) where {AD<:ActionDirection} +function adjoint_apply_diff_group!(A::GroupOperationAction, Y, a, X, p) G = base_group(A) - return inverse_translate_diff!(G, Y, a, p, X, switch_direction(AD())) + return inverse_translate_diff!(G, Y, a, p, X, reverse_direction_and_side(A)) end -apply(A::GroupOperationAction, a, p) = translate(A.group, a, p, direction(A)) +apply(A::GroupOperationAction, a, p) = translate(A.group, a, p, direction_and_side(A)) -apply!(A::GroupOperationAction, q, a, p) = translate!(A.group, q, a, p, direction(A)) +function apply!(A::GroupOperationAction, q, a, p) + return translate!(A.group, q, a, p, direction_and_side(A)) +end function inverse_apply(A::GroupOperationAction, a, p) - return inverse_translate(A.group, a, p, direction(A)) + return inverse_translate(A.group, a, p, direction_and_side(A)) end function inverse_apply!(A::GroupOperationAction, q, a, p) - return inverse_translate!(A.group, q, a, p, direction(A)) + return inverse_translate!(A.group, q, a, p, direction_and_side(A)) end function apply_diff(A::GroupOperationAction, a, p, X) - return translate_diff(A.group, a, p, X, direction(A)) + return translate_diff(A.group, a, p, X, direction_and_side(A)) end function apply_diff!(A::GroupOperationAction, Y, a, p, X) - return translate_diff!(A.group, Y, a, p, X, direction(A)) + return translate_diff!(A.group, Y, a, p, X, direction_and_side(A)) end -function apply_diff_group( - A::GroupOperationAction{<:AbstractDecoratorManifold,AD}, - a, - X, - p, -) where {AD<:ActionDirection} +function apply_diff_group(A::GroupOperationAction, a, X, p) G = base_group(A) - return translate_diff(G, p, a, X, switch_direction(AD())) + return translate_diff(G, p, a, X, reverse_direction_and_side(A)) end -function apply_diff_group!( - A::GroupOperationAction{<:AbstractDecoratorManifold,AD}, - Y, - a, - X, - p, -) where {AD<:ActionDirection} +function apply_diff_group!(A::GroupOperationAction, Y, a, X, p) G = base_group(A) - return translate_diff!(G, Y, p, a, X, switch_direction(AD())) + return translate_diff!(G, Y, p, a, X, reverse_direction_and_side(A)) end function inverse_apply_diff(A::GroupOperationAction, a, p, X) - return inverse_translate_diff(A.group, a, p, X, direction(A)) + return inverse_translate_diff(A.group, a, p, X, direction_and_side(A)) end function inverse_apply_diff!(A::GroupOperationAction, Y, a, p, X) - return inverse_translate_diff!(A.group, Y, a, p, X, direction(A)) + return inverse_translate_diff!(A.group, Y, a, p, X, direction_and_side(A)) end function optimal_alignment(A::GroupOperationAction, p, q) - return inverse_apply(switch_direction(A), p, q) + return inverse_apply(switch_direction_and_side(A), p, q) end function optimal_alignment!(A::GroupOperationAction, x, p, q) - return inverse_apply!(switch_direction(A), x, p, q) + return inverse_apply!(switch_direction_and_side(A), x, p, q) end function center_of_orbit( @@ -110,9 +122,9 @@ function center_of_orbit( mean_method::AbstractEstimationMethod, ) μ = mean(A.group, pts, mean_method) - return inverse_apply(switch_direction(A), p, μ) + return inverse_apply(switch_direction_and_side(A), p, μ) end function center_of_orbit(A::GroupOperationAction, pts::AbstractVector, p) μ = mean(A.group, pts) - return inverse_apply(switch_direction(A), p, μ) + return inverse_apply(switch_direction_and_side(A), p, μ) end diff --git a/src/groups/heisenberg.jl b/src/groups/heisenberg.jl index de94e14bf0..2c5793cae3 100644 --- a/src/groups/heisenberg.jl +++ b/src/groups/heisenberg.jl @@ -26,11 +26,7 @@ function HeisenbergGroup(n::Int; parameter::Symbol=:type) end function active_traits(f, ::HeisenbergGroup, args...) - return merge_traits( - IsGroupManifold(MultiplicationOperation()), - IsEmbeddedManifold(), - HasLeftInvariantMetric(), - ) + return merge_traits(IsGroupManifold(MultiplicationOperation()), IsEmbeddedManifold()) end function _heisenberg_a_view(M::HeisenbergGroup, p) @@ -420,6 +416,6 @@ end translate_diff(::HeisenbergGroup, p, q, X, ::LeftForwardAction) = X translate_diff(::HeisenbergGroup, p, q, X, ::RightBackwardAction) = p \ X * p -function translate_diff!(G::HeisenbergGroup, Y, p, q, X, conv::ActionDirection) +function translate_diff!(G::HeisenbergGroup, Y, p, q, X, conv::ActionDirectionAndSide) return copyto!(Y, translate_diff(G, p, q, X, conv)) end diff --git a/src/groups/metric.jl b/src/groups/metric.jl index 19a946dfd9..f9aa547caf 100644 --- a/src/groups/metric.jl +++ b/src/groups/metric.jl @@ -5,7 +5,7 @@ X, Y, qs::AbstractVector, - conv::ActionDirection = LeftForwardAction(); + conv::ActionDirectionAndSide = LeftForwardAction(); kwargs..., ) -> Bool @@ -22,14 +22,21 @@ This is necessary but not sufficient for invariance. Optionally, `kwargs` passed to `isapprox` may be provided. """ -has_approx_invariant_metric(::AbstractDecoratorManifold, p, X, Y, qs, ::ActionDirection) +has_approx_invariant_metric( + ::AbstractDecoratorManifold, + p, + X, + Y, + qs, + ::ActionDirectionAndSide, +) @trait_function has_approx_invariant_metric( M::AbstractDecoratorManifold, p, X, Y, qs, - conv::ActionDirection=LeftForwardAction(); + conv::ActionDirectionAndSide=LeftForwardAction(); kwargs..., ) function has_approx_invariant_metric( @@ -39,7 +46,7 @@ function has_approx_invariant_metric( X, Y, qs, - conv::ActionDirection; + conv::ActionDirectionAndSide; kwargs..., ) gpXY = inner(M, p, X, Y) @@ -55,17 +62,33 @@ end """ direction(::AbstractDecoratorManifold) -> AD -Get the direction of the action a certain Lie group with its implicit metric has +Get the direction of the action a certain Lie group with its implicit metric has. """ direction(::AbstractDecoratorManifold) @trait_function direction(M::AbstractDecoratorManifold) function direction(::TraitList{HasLeftInvariantMetric}, ::AbstractDecoratorManifold) - return LeftForwardAction() + return LeftAction() end function direction(::TraitList{HasRightInvariantMetric}, ::AbstractDecoratorManifold) + return RightAction() +end + +@trait_function direction_and_side(M::AbstractDecoratorManifold) + +function direction_and_side( + ::TraitList{HasLeftInvariantMetric}, + ::AbstractDecoratorManifold, +) + return LeftForwardAction() +end + +function direction_and_side( + ::TraitList{HasRightInvariantMetric}, + ::AbstractDecoratorManifold, +) return RightBackwardAction() end @@ -139,7 +162,7 @@ function get_coordinates( X, B::AbstractBasis, ) where {IT<:AbstractInvarianceTrait} - conv = direction(t, M) + conv = direction_and_side(t, M) Xₑ = inverse_translate_diff(M, p, p, X, conv) return get_coordinates_lie(next_trait(t), M, Xₑ, B) end @@ -151,7 +174,7 @@ function get_coordinates!( X, B::AbstractBasis, ) where {IT<:AbstractInvarianceTrait} - conv = direction(t, M) + conv = direction_and_side(t, M) Xₑ = inverse_translate_diff(M, p, p, X, conv) return get_coordinates_lie!(next_trait(t), M, c, Xₑ, B) end @@ -163,7 +186,7 @@ function get_vector( c, B::AbstractBasis, ) where {IT<:AbstractInvarianceTrait} - conv = direction(t, M) + conv = direction_and_side(t, M) Xₑ = get_vector_lie(next_trait(t), M, c, B) return translate_diff(M, p, Identity(M), Xₑ, conv) end @@ -175,15 +198,18 @@ function get_vector!( c, B::AbstractBasis, ) where {IT<:AbstractInvarianceTrait} - conv = direction(t, M) + conv = direction_and_side(t, M) Xₑ = get_vector_lie(next_trait(t), M, c, B) return translate_diff!(M, X, p, Identity(M), Xₑ, conv) end -@trait_function has_invariant_metric(M::AbstractDecoratorManifold, op::ActionDirection) +@trait_function has_invariant_metric( + M::AbstractDecoratorManifold, + op::ActionDirectionAndSide, +) # Fallbacks / false -has_invariant_metric(::AbstractManifold, op::ActionDirection) = false +has_invariant_metric(::AbstractManifold, op::ActionDirectionAndSide) = false function has_invariant_metric( ::TraitList{<:HasLeftInvariantMetric}, ::AbstractDecoratorManifold, @@ -217,7 +243,7 @@ function inner( X, Y, ) where {IT<:AbstractInvarianceTrait} - conv = direction(t, M) + conv = direction_and_side(t, M) Xₑ = inverse_translate_diff(M, p, p, X, conv) Yₑ = inverse_translate_diff(M, p, p, Y, conv) return inner(next_trait(t), M, Identity(M), Xₑ, Yₑ) @@ -238,7 +264,7 @@ function inverse_translate_diff( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) return inverse_translate_diff(M.manifold, p, q, X, conv) end @@ -249,7 +275,7 @@ function inverse_translate_diff!( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) return inverse_translate_diff!(M.manifold, Y, p, q, X, conv) end @@ -296,7 +322,7 @@ function LinearAlgebra.norm( p, X, ) where {IT<:AbstractInvarianceTrait} - conv = direction(t, M) + conv = direction_and_side(t, M) Xₑ = inverse_translate_diff(M, p, p, X, conv) return norm(next_trait(t), M, Identity(M), Xₑ) end @@ -315,7 +341,7 @@ function translate_diff( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) return translate_diff(M.manifold, p, q, X, conv) end @@ -326,7 +352,7 @@ function translate_diff!( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) return translate_diff!(M.manifold, Y, p, q, X, conv) end diff --git a/src/groups/multiplication_operation.jl b/src/groups/multiplication_operation.jl index 689a134c1b..de21633021 100644 --- a/src/groups/multiplication_operation.jl +++ b/src/groups/multiplication_operation.jl @@ -155,7 +155,7 @@ function inverse_translate!( x, p, q, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) return copyto!(x, inverse_translate(G, p, q, conv)) end diff --git a/src/groups/power_group.jl b/src/groups/power_group.jl index 235d65780c..d454e7e63e 100644 --- a/src/groups/power_group.jl +++ b/src/groups/power_group.jl @@ -154,10 +154,10 @@ function _compose!(M::PowerManifoldNestedReplacing, x, p, q) return x end -function translate!(G::PowerGroup, x, p, q, conv::ActionDirection) +function translate!(G::PowerGroup, x, p, q, conv::ActionDirectionAndSide) return translate!(G.manifold, x, p, q, conv) end -function translate!(M::AbstractPowerManifold, x, p, q, conv::ActionDirection) +function translate!(M::AbstractPowerManifold, x, p, q, conv::ActionDirectionAndSide) N = M.manifold rep_size = representation_size(N) for i in get_iterator(M) @@ -171,7 +171,7 @@ function translate!(M::AbstractPowerManifold, x, p, q, conv::ActionDirection) end return x end -function translate!(M::PowerManifoldNestedReplacing, x, p, q, conv::ActionDirection) +function translate!(M::PowerManifoldNestedReplacing, x, p, q, conv::ActionDirectionAndSide) N = M.manifold rep_size = representation_size(N) for i in get_iterator(M) @@ -180,10 +180,10 @@ function translate!(M::PowerManifoldNestedReplacing, x, p, q, conv::ActionDirect return x end -function inverse_translate!(G::PowerGroup, x, p, q, conv::ActionDirection) +function inverse_translate!(G::PowerGroup, x, p, q, conv::ActionDirectionAndSide) return inverse_translate!(G.manifold, x, p, q, conv) end -function inverse_translate!(M::AbstractPowerManifold, x, p, q, conv::ActionDirection) +function inverse_translate!(M::AbstractPowerManifold, x, p, q, conv::ActionDirectionAndSide) N = M.manifold rep_size = representation_size(N) for i in get_iterator(M) @@ -197,7 +197,13 @@ function inverse_translate!(M::AbstractPowerManifold, x, p, q, conv::ActionDirec end return x end -function inverse_translate!(M::PowerManifoldNestedReplacing, x, p, q, conv::ActionDirection) +function inverse_translate!( + M::PowerManifoldNestedReplacing, + x, + p, + q, + conv::ActionDirectionAndSide, +) N = M.manifold rep_size = representation_size(N) for i in get_iterator(M) @@ -207,7 +213,7 @@ function inverse_translate!(M::PowerManifoldNestedReplacing, x, p, q, conv::Acti return x end -function translate_diff!(G::PowerGroup, Y, p, q, X, conv::ActionDirection) +function translate_diff!(G::PowerGroup, Y, p, q, X, conv::ActionDirectionAndSide) GM = G.manifold N = GM.manifold rep_size = representation_size(N) @@ -223,7 +229,14 @@ function translate_diff!(G::PowerGroup, Y, p, q, X, conv::ActionDirection) end return Y end -function translate_diff!(G::PowerGroupNestedReplacing, Y, p, q, X, conv::ActionDirection) +function translate_diff!( + G::PowerGroupNestedReplacing, + Y, + p, + q, + X, + conv::ActionDirectionAndSide, +) GM = G.manifold N = GM.manifold rep_size = representation_size(N) @@ -239,7 +252,7 @@ function translate_diff!(G::PowerGroupNestedReplacing, Y, p, q, X, conv::ActionD return Y end -function inverse_translate_diff!(G::PowerGroup, Y, p, q, X, conv::ActionDirection) +function inverse_translate_diff!(G::PowerGroup, Y, p, q, X, conv::ActionDirectionAndSide) GM = G.manifold N = GM.manifold rep_size = representation_size(N) @@ -261,7 +274,7 @@ function inverse_translate_diff!( p, q, X, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) GM = G.manifold N = GM.manifold diff --git a/src/groups/product_group.jl b/src/groups/product_group.jl index 19ef5dea1d..415c7dd259 100644 --- a/src/groups/product_group.jl +++ b/src/groups/product_group.jl @@ -130,7 +130,7 @@ function translate( M::ProductGroup, p::ArrayPartition, q::ArrayPartition, - conv::ActionDirection, + conv::ActionDirectionAndSide, ) return ArrayPartition( map( @@ -143,7 +143,7 @@ function translate( ) end -function translate!(M::ProductGroup, x, p, q, conv::ActionDirection) +function translate!(M::ProductGroup, x, p, q, conv::ActionDirectionAndSide) map( translate!, M.manifold.manifolds, @@ -155,7 +155,7 @@ function translate!(M::ProductGroup, x, p, q, conv::ActionDirection) return x end -function inverse_translate(G::ProductGroup, p, q, conv::ActionDirection) +function inverse_translate(G::ProductGroup, p, q, conv::ActionDirectionAndSide) M = G.manifold return ArrayPartition( map( @@ -168,7 +168,7 @@ function inverse_translate(G::ProductGroup, p, q, conv::ActionDirection) ) end -function inverse_translate!(G::ProductGroup, x, p, q, conv::ActionDirection) +function inverse_translate!(G::ProductGroup, x, p, q, conv::ActionDirectionAndSide) M = G.manifold map( inverse_translate!, @@ -181,7 +181,7 @@ function inverse_translate!(G::ProductGroup, x, p, q, conv::ActionDirection) return x end -function translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) +function translate_diff(G::ProductGroup, p, q, X, conv::ActionDirectionAndSide) M = G.manifold return ArrayPartition( map( @@ -195,7 +195,7 @@ function translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) ) end -function translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirection) +function translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirectionAndSide) M = G.manifold map( translate_diff!, @@ -209,7 +209,7 @@ function translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirection) return Y end -function inverse_translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) +function inverse_translate_diff(G::ProductGroup, p, q, X, conv::ActionDirectionAndSide) M = G.manifold return ArrayPartition( map( @@ -223,7 +223,7 @@ function inverse_translate_diff(G::ProductGroup, p, q, X, conv::ActionDirection) ) end -function inverse_translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirection) +function inverse_translate_diff!(G::ProductGroup, Y, p, q, X, conv::ActionDirectionAndSide) M = G.manifold map( inverse_translate_diff!, diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index 79f9771ddb..13c5cb50f6 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -2,13 +2,13 @@ RotationAction( M::AbstractManifold, SOn::SpecialOrthogonal, - AD::ActionDirection = LeftForwardAction(), + AD::ActionDirection = LeftAction(), ) Space of actions of the [`SpecialOrthogonal`](@ref) group $\mathrm{SO}(n)$ on a Euclidean-like manifold `M` of dimension `n`. """ -struct RotationAction{TM<:AbstractManifold,TSO<:SpecialOrthogonal,TAD<:ActionDirection} <: +struct RotationAction{TAD<:ActionDirection,TM<:AbstractManifold,TSO<:SpecialOrthogonal} <: AbstractGroupAction{TAD} manifold::TM SOn::TSO @@ -17,9 +17,9 @@ end function RotationAction( M::AbstractManifold, SOn::SpecialOrthogonal, - ::TAD=LeftForwardAction(), + ::TAD=LeftAction(), ) where {TAD<:ActionDirection} - return RotationAction{typeof(M),typeof(SOn),TAD}(M, SOn) + return RotationAction{TAD,typeof(M),typeof(SOn)}(M, SOn) end function Base.show(io::IO, A::RotationAction) @@ -27,71 +27,68 @@ function Base.show(io::IO, A::RotationAction) end const RotationActionOnVector{TAD,𝔽,TE,TSO} = RotationAction{ + TAD, <:Union{Euclidean{TE,𝔽},TranslationGroup{TE,𝔽}}, SpecialOrthogonal{TSO}, - TAD, } where {TAD<:ActionDirection,𝔽,TE,TSO} base_group(A::RotationAction) = A.SOn group_manifold(A::RotationAction) = A.manifold -function switch_direction( - A::RotationAction{TM,TSO,TAD}, - ::LeftRightSwitch=LeftRightSwitch(), -) where {TM<:AbstractManifold,TSO<:SpecialOrthogonal,TAD<:ActionDirection} - return RotationAction(A.manifold, A.SOn, switch_direction(TAD(), LeftRightSwitch())) +function switch_direction(A::RotationAction{TAD}) where {TAD<:ActionDirection} + return RotationAction(A.manifold, A.SOn, switch_direction(TAD())) end -apply(::RotationActionOnVector{LeftForwardAction}, a, p) = a * p -function apply(A::RotationActionOnVector{RightForwardAction}, a, p) +apply(::RotationActionOnVector{LeftAction}, a, p) = a * p +function apply(A::RotationActionOnVector{RightAction}, a, p) return inv(base_group(A), a) * p end -apply!(::RotationActionOnVector{LeftForwardAction}, q, a, p) = mul!(q, a, p) +apply!(::RotationActionOnVector{LeftAction}, q, a, p) = mul!(q, a, p) -function inverse_apply(A::RotationActionOnVector{LeftForwardAction}, a, p) +function inverse_apply(A::RotationActionOnVector{LeftAction}, a, p) return inv(base_group(A), a) * p end -inverse_apply(::RotationActionOnVector{RightForwardAction}, a, p) = a * p +inverse_apply(::RotationActionOnVector{RightAction}, a, p) = a * p -apply_diff(::RotationActionOnVector{LeftForwardAction}, a, p, X) = a * X +apply_diff(::RotationActionOnVector{LeftAction}, a, p, X) = a * X function apply_diff( - ::RotationActionOnVector{LeftForwardAction}, + ::RotationActionOnVector{LeftAction}, ::Identity{MultiplicationOperation}, p, X, ) return X end -function apply_diff(A::RotationActionOnVector{RightForwardAction}, a, p, X) +function apply_diff(A::RotationActionOnVector{RightAction}, a, p, X) return inv(base_group(A), a) * X end -function apply_diff!(::RotationActionOnVector{LeftForwardAction}, Y, a, p, X) +function apply_diff!(::RotationActionOnVector{LeftAction}, Y, a, p, X) return mul!(Y, a, X) end -function apply_diff!(A::RotationActionOnVector{RightForwardAction}, Y, a, p, X) +function apply_diff!(A::RotationActionOnVector{RightAction}, Y, a, p, X) return mul!(Y, inv(base_group(A), a), X) end -function apply_diff_group(::RotationActionOnVector{LeftForwardAction}, ::Identity, X, p) +function apply_diff_group(::RotationActionOnVector{LeftAction}, ::Identity, X, p) return X * p end -function apply_diff_group!(::RotationActionOnVector{LeftForwardAction}, Y, ::Identity, X, p) +function apply_diff_group!(::RotationActionOnVector{LeftAction}, Y, ::Identity, X, p) Y .= X * p return Y end -function inverse_apply_diff(A::RotationActionOnVector{LeftForwardAction}, a, p, X) +function inverse_apply_diff(A::RotationActionOnVector{LeftAction}, a, p, X) return inv(base_group(A), a) * X end -function inverse_apply_diff(A::RotationActionOnVector{RightForwardAction}, a, p, X) +function inverse_apply_diff(::RotationActionOnVector{RightAction}, a, p, X) return a * X end -function optimal_alignment(::RotationActionOnVector{LeftForwardAction}, p, q) +function optimal_alignment(::RotationActionOnVector{LeftAction}, p, q) Xmul = p * transpose(q) F = svd(Xmul) L = size(Xmul)[2] @@ -99,7 +96,7 @@ function optimal_alignment(::RotationActionOnVector{LeftForwardAction}, p, q) Ostar = det(UVt) ≥ 0 ? UVt : F.U * Diagonal([i < L ? 1 : -1 for i in 1:L]) * F.Vt return convert(typeof(Xmul), Ostar) end -function optimal_alignment(A::RotationActionOnVector{RightForwardAction}, p, q) +function optimal_alignment(A::RotationActionOnVector{RightAction}, p, q) return optimal_alignment(switch_direction(A), q, p) end @@ -108,8 +105,7 @@ end Space of actions of the circle group [`RealCircleGroup`](@ref) on $ℝ^3$ around given `axis`. """ -struct RotationAroundAxisAction{TA<:AbstractVector} <: - AbstractGroupAction{LeftForwardAction} +struct RotationAroundAxisAction{TA<:AbstractVector} <: AbstractGroupAction{LeftAction} axis::TA end @@ -150,9 +146,9 @@ end @doc raw""" RowwiseMultiplicationAction{ + TAD<:ActionDirection, TM<:AbstractManifold, TO<:GeneralUnitaryMultiplicationGroup, - TAD<:ActionDirection, } <: AbstractGroupAction{TAD} Action of the (special) unitary or orthogonal group [`GeneralUnitaryMultiplicationGroup`](@ref) @@ -163,13 +159,13 @@ of type `On` columns of points on a matrix manifold `M`. RowwiseMultiplicationAction( M::AbstractManifold, On::GeneralUnitaryMultiplicationGroup, - AD::ActionDirection = LeftForwardAction(), + AD::ActionDirection = LeftAction(), ) """ struct RowwiseMultiplicationAction{ + TAD<:ActionDirection, TM<:AbstractManifold, TO<:GeneralUnitaryMultiplicationGroup, - TAD<:ActionDirection, } <: AbstractGroupAction{TAD} manifold::TM On::TO @@ -178,15 +174,15 @@ end function RowwiseMultiplicationAction( M::AbstractManifold, On::GeneralUnitaryMultiplicationGroup, - ::TAD=LeftForwardAction(), + ::TAD=LeftAction(), ) where {TAD<:ActionDirection} - return RowwiseMultiplicationAction{typeof(M),typeof(On),TAD}(M, On) + return RowwiseMultiplicationAction{TAD,typeof(M),typeof(On)}(M, On) end const LeftRowwiseMultiplicationAction{ TM<:AbstractManifold, TO<:GeneralUnitaryMultiplicationGroup, -} = RowwiseMultiplicationAction{TM,TO,LeftForwardAction} +} = RowwiseMultiplicationAction{LeftAction,TM,TO} function apply(::LeftRowwiseMultiplicationAction, a, p) return (a * p')' @@ -224,13 +220,13 @@ of type `On` columns of points on a matrix manifold `M`. ColumnwiseMultiplicationAction( M::AbstractManifold, On::GeneralUnitaryMultiplicationGroup, - AD::ActionDirection = LeftForwardAction(), + AD::ActionDirection = LeftAction(), ) """ struct ColumnwiseMultiplicationAction{ + TAD<:ActionDirection, TM<:AbstractManifold, TO<:GeneralUnitaryMultiplicationGroup, - TAD<:ActionDirection, } <: AbstractGroupAction{TAD} manifold::TM On::TO @@ -239,15 +235,15 @@ end function ColumnwiseMultiplicationAction( M::AbstractManifold, On::GeneralUnitaryMultiplicationGroup, - ::TAD=LeftForwardAction(), + ::TAD=LeftAction(), ) where {TAD<:ActionDirection} - return ColumnwiseMultiplicationAction{typeof(M),typeof(On),TAD}(M, On) + return ColumnwiseMultiplicationAction{TAD,typeof(M),typeof(On)}(M, On) end const LeftColumnwiseMultiplicationAction{ TM<:AbstractManifold, TO<:GeneralUnitaryMultiplicationGroup, -} = ColumnwiseMultiplicationAction{TM,TO,LeftForwardAction} +} = ColumnwiseMultiplicationAction{LeftAction,TM,TO} function apply(::LeftColumnwiseMultiplicationAction, a, p) return a * p diff --git a/src/groups/rotation_translation_action.jl b/src/groups/rotation_translation_action.jl index c1a428b7c1..ba6627e83d 100644 --- a/src/groups/rotation_translation_action.jl +++ b/src/groups/rotation_translation_action.jl @@ -2,13 +2,13 @@ RotationTranslationAction( M::AbstractManifold, SOn::SpecialEuclidean, - AD::ActionDirection = LeftForwardAction(), + AD::ActionDirection = LeftAction(), ) Space of actions of the [`SpecialEuclidean`](@ref) group $\mathrm{SE}(n)$ on a Euclidean-like manifold `M` of dimension `n`. -Left forward action corresponds to active transformations while right forward actions +Left actions corresponds to active transformations while right actions can be identified with passive transformations for a particular choice of a basis. """ struct RotationTranslationAction{ @@ -23,7 +23,7 @@ end function RotationTranslationAction( M::AbstractManifold, SEn::SpecialEuclidean, - ::TAD=LeftForwardAction(), + ::TAD=LeftAction(), ) where {TAD<:ActionDirection} return RotationTranslationAction{TAD,typeof(M),typeof(SEn)}(M, SEn) end @@ -42,54 +42,38 @@ base_group(A::RotationTranslationAction) = A.SEn group_manifold(A::RotationTranslationAction) = A.manifold -function switch_direction( - A::RotationTranslationAction{TAD}, - ::LeftRightSwitch=LeftRightSwitch(), -) where {TAD<:ActionDirection} - return RotationTranslationAction( - A.manifold, - A.SEn, - switch_direction(TAD(), LeftRightSwitch()), - ) +function switch_direction(A::RotationTranslationAction{TAD}) where {TAD<:ActionDirection} + return RotationTranslationAction(A.manifold, A.SEn, switch_direction(TAD())) end -function apply(::RotationTranslationActionOnVector{LeftForwardAction}, a::ArrayPartition, p) +function apply(::RotationTranslationActionOnVector{LeftAction}, a::ArrayPartition, p) return a.x[2] * p + a.x[1] end function apply( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, a::SpecialEuclideanIdentity, p, ) return p end -function apply( - ::RotationTranslationActionOnVector{RightForwardAction}, - a::ArrayPartition, - p, -) +function apply(::RotationTranslationActionOnVector{RightAction}, a::ArrayPartition, p) return a.x[2] \ (p - a.x[1]) end function apply( - ::RotationTranslationActionOnVector{RightForwardAction}, + ::RotationTranslationActionOnVector{RightAction}, a::SpecialEuclideanIdentity, p, ) return p end -function apply!( - ::RotationTranslationActionOnVector{LeftForwardAction}, - q, - a::ArrayPartition, - p, -) +function apply!(::RotationTranslationActionOnVector{LeftAction}, q, a::ArrayPartition, p) mul!(q, a.x[2], p) q .+= a.x[1] return q end function apply!( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, q, a::SpecialEuclideanIdentity, p, @@ -99,14 +83,14 @@ function apply!( end function inverse_apply( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, a::ArrayPartition, p, ) return a.x[2] \ (p - a.x[1]) end function inverse_apply( - ::RotationTranslationActionOnVector{RightForwardAction}, + ::RotationTranslationActionOnVector{RightAction}, a::ArrayPartition, p, ) @@ -114,7 +98,7 @@ function inverse_apply( end function apply_diff( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, a::ArrayPartition, p, X, @@ -122,7 +106,7 @@ function apply_diff( return a.x[2] * X end function apply_diff( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, ::SpecialEuclideanIdentity, p, X, @@ -130,7 +114,7 @@ function apply_diff( return X end function apply_diff( - ::RotationTranslationActionOnVector{RightForwardAction}, + ::RotationTranslationActionOnVector{RightAction}, a::ArrayPartition, p, X, @@ -138,7 +122,7 @@ function apply_diff( return a.x[2] \ X end function apply_diff( - ::RotationTranslationActionOnVector{RightForwardAction}, + ::RotationTranslationActionOnVector{RightAction}, a::SpecialEuclideanIdentity, p, X, @@ -147,7 +131,7 @@ function apply_diff( end function apply_diff!( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, Y, a::ArrayPartition, p, @@ -156,7 +140,7 @@ function apply_diff!( return mul!(Y, a.x[2], X) end function apply_diff!( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, Y, a::SpecialEuclideanIdentity, p, @@ -165,7 +149,7 @@ function apply_diff!( return copyto!(Y, X) end function apply_diff!( - ::RotationTranslationActionOnVector{RightForwardAction}, + ::RotationTranslationActionOnVector{RightAction}, Y, a::ArrayPartition, p, @@ -175,7 +159,7 @@ function apply_diff!( return Y end function apply_diff!( - ::RotationTranslationActionOnVector{RightForwardAction}, + ::RotationTranslationActionOnVector{RightAction}, Y, a::SpecialEuclideanIdentity, p, @@ -185,7 +169,7 @@ function apply_diff!( end function apply_diff_group( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, ::SpecialEuclideanIdentity, X, p, @@ -194,7 +178,7 @@ function apply_diff_group( end function apply_diff_group!( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, Y, ::SpecialEuclideanIdentity, X::ArrayPartition, @@ -205,7 +189,7 @@ function apply_diff_group!( end function inverse_apply_diff( - ::RotationTranslationActionOnVector{LeftForwardAction}, + ::RotationTranslationActionOnVector{LeftAction}, a::ArrayPartition, p, X, @@ -213,7 +197,7 @@ function inverse_apply_diff( return a.x[2] \ X end function inverse_apply_diff( - ::RotationTranslationActionOnVector{RightForwardAction}, + ::RotationTranslationActionOnVector{RightAction}, a::ArrayPartition, p, X, @@ -238,13 +222,13 @@ of type `SE` columns of points on a matrix manifold `M`. ColumnwiseSpecialEuclideanAction( M::AbstractManifold, SE::SpecialEuclidean, - AD::ActionDirection = LeftForwardAction(), + AD::ActionDirection = LeftAction(), ) """ struct ColumnwiseSpecialEuclideanAction{ + TAD<:ActionDirection, TM<:AbstractManifold, TSE<:SpecialEuclidean, - TAD<:ActionDirection, } <: AbstractGroupAction{TAD} manifold::TM SE::TSE @@ -253,13 +237,13 @@ end function ColumnwiseSpecialEuclideanAction( M::AbstractManifold, SE::SpecialEuclidean, - ::TAD=LeftForwardAction(), + ::TAD=LeftAction(), ) where {TAD<:ActionDirection} - return ColumnwiseSpecialEuclideanAction{typeof(M),typeof(SE),TAD}(M, SE) + return ColumnwiseSpecialEuclideanAction{TAD,typeof(M),typeof(SE)}(M, SE) end const LeftColumnwiseSpecialEuclideanAction{TM<:AbstractManifold,TSE<:SpecialEuclidean} = - ColumnwiseSpecialEuclideanAction{TM,TSE,LeftForwardAction} + ColumnwiseSpecialEuclideanAction{LeftAction,TM,TSE} function apply(::LeftColumnwiseSpecialEuclideanAction, a::ArrayPartition, p) return a.x[2] * p .+ a.x[1] diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index e573066304..99018143c5 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -29,7 +29,7 @@ const SpecialEuclidean{T} = SemidirectProductGroup{ ℝ, TranslationGroup{T,ℝ}, SpecialOrthogonal{T}, - RotationAction{TranslationGroup{T,ℝ},SpecialOrthogonal{T},LeftForwardAction}, + RotationAction{LeftAction,TranslationGroup{T,ℝ},SpecialOrthogonal{T}}, } const SpecialEuclideanManifold{N} = @@ -43,7 +43,7 @@ function SpecialEuclidean(n; parameter::Symbol=:type) end const SpecialEuclideanOperation{N} = SemidirectProductOperation{ - RotationAction{TranslationGroup{N,ℝ},SpecialOrthogonal{N},LeftForwardAction}, + RotationAction{LeftAction,TranslationGroup{N,ℝ},SpecialOrthogonal{N}}, } const SpecialEuclideanIdentity{N} = Identity{SpecialEuclideanOperation{N}} diff --git a/src/groups/special_linear.jl b/src/groups/special_linear.jl index cc2ff3cdee..7ffb5fde1e 100644 --- a/src/groups/special_linear.jl +++ b/src/groups/special_linear.jl @@ -75,7 +75,7 @@ end inverse_translate_diff(::SpecialLinear, p, q, X, ::LeftForwardAction) = X inverse_translate_diff(::SpecialLinear, p, q, X, ::RightBackwardAction) = p * X / p -function inverse_translate_diff!(G::SpecialLinear, Y, p, q, X, conv::ActionDirection) +function inverse_translate_diff!(G::SpecialLinear, Y, p, q, X, conv::ActionDirectionAndSide) return copyto!(Y, inverse_translate_diff(G, p, q, X, conv)) end @@ -150,6 +150,6 @@ end translate_diff(::SpecialLinear, p, q, X, ::LeftForwardAction) = X translate_diff(::SpecialLinear, p, q, X, ::RightBackwardAction) = p \ X * p -function translate_diff!(G::SpecialLinear, Y, p, q, X, conv::ActionDirection) +function translate_diff!(G::SpecialLinear, Y, p, q, X, conv::ActionDirectionAndSide) return copyto!(Y, translate_diff(G, p, q, X, conv)) end diff --git a/src/groups/translation_action.jl b/src/groups/translation_action.jl index 53b493bee7..44daebc7c0 100644 --- a/src/groups/translation_action.jl +++ b/src/groups/translation_action.jl @@ -2,7 +2,7 @@ TranslationAction( M::AbstractManifold, Rn::TranslationGroup, - AD::ActionDirection = LeftForwardAction(), + AD::ActionDirection = LeftAction(), ) Space of actions of the [`TranslationGroup`](@ref) $\mathrm{T}(n)$ on a Euclidean-like @@ -10,7 +10,7 @@ manifold `M`. The left and right actions are equivalent. """ -struct TranslationAction{TM<:AbstractManifold,TRn<:TranslationGroup,TAD<:ActionDirection} <: +struct TranslationAction{TAD<:ActionDirection,TM<:AbstractManifold,TRn<:TranslationGroup} <: AbstractGroupAction{TAD} manifold::TM Rn::TRn @@ -19,9 +19,9 @@ end function TranslationAction( M::AbstractManifold, Rn::TranslationGroup, - ::TAD=LeftForwardAction(), + ::TAD=LeftAction(), ) where {TAD<:ActionDirection} - return TranslationAction{typeof(M),typeof(Rn),TAD}(M, Rn) + return TranslationAction{TAD,typeof(M),typeof(Rn)}(M, Rn) end function Base.show(io::IO, A::TranslationAction) @@ -32,11 +32,8 @@ base_group(A::TranslationAction) = A.Rn group_manifold(A::TranslationAction) = A.manifold -function switch_direction( - A::TranslationAction{TM,TSO,TAD}, - ::LeftRightSwitch=LeftRightSwitch(), -) where {TM<:AbstractManifold,TSO<:TranslationGroup,TAD<:ActionDirection} - return TranslationAction(A.manifold, A.Rn, switch_direction(TAD(), LeftRightSwitch())) +function switch_direction(A::TranslationAction{TAD}) where {TAD<:ActionDirection} + return TranslationAction(A.manifold, A.Rn, switch_direction(TAD())) end adjoint_apply_diff_group(::TranslationAction, a, X, p) = X @@ -48,7 +45,7 @@ end apply(::TranslationAction, a, p) = p + a apply!(::TranslationAction, q, a, p) = (q .= p .+ a) -function apply!(A::TranslationAction, q, e::Identity{AdditionOperation}, p) +function apply!(A::TranslationAction, q, ::Identity{AdditionOperation}, p) return copyto!(A.manifold, q, p) end @@ -65,17 +62,11 @@ function apply_diff!(A::TranslationAction, Y, a, p, X) return copyto!(A.manifold, Y, p, X) end -function apply_diff_group(::TranslationAction{N,F,LeftForwardAction}, a, X, p) where {N,F} +function apply_diff_group(::TranslationAction{LeftAction}, a, X, p) return X end -function apply_diff_group!( - A::TranslationAction{N,F,LeftForwardAction}, - Y, - a, - X, - p, -) where {N,F} +function apply_diff_group!(A::TranslationAction{LeftAction}, Y, a, X, p) copyto!(A.manifold, Y, p, X) return Y end diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index 550648b1cc..6f59d59154 100644 --- a/src/groups/translation_group.jl +++ b/src/groups/translation_group.jl @@ -26,7 +26,6 @@ end else return merge_traits( IsGroupManifold(M.op), - HasBiinvariantMetric(), IsDefaultMetric(EuclideanMetric()), active_traits(f, M.manifold, args...), IsExplicitDecorator(), #pass to Euclidean by default/last fallback @@ -36,6 +35,11 @@ end exp!(::TranslationGroup, q, ::Identity{AdditionOperation}, X) = copyto!(q, X) +has_biinvariant_metric(::TranslationGroup) = true + +has_invariant_metric(::TranslationGroup) = true +has_invariant_metric(::TranslationGroup, ::ActionDirectionAndSide) = true + identity_element!(::TranslationGroup, p) = fill!(p, 0) log(::TranslationGroup, ::Identity{AdditionOperation}, q) = q diff --git a/src/groups/validation_group.jl b/src/groups/validation_group.jl index 98ec45915d..fcef541e08 100644 --- a/src/groups/validation_group.jl +++ b/src/groups/validation_group.jl @@ -104,7 +104,7 @@ function compose!(M::ValidationManifold, x, p, q::Identity; kwargs...) return x end -function translate(M::ValidationManifold, p, q, conv::ActionDirection; kwargs...) +function translate(M::ValidationManifold, p, q, conv::ActionDirectionAndSide; kwargs...) is_point(M, p, true; kwargs...) is_point(M, q, true; kwargs...) x = array_point(translate(M.manifold, array_value(p), array_value(q), conv)) @@ -112,7 +112,7 @@ function translate(M::ValidationManifold, p, q, conv::ActionDirection; kwargs... return x end -function translate!(M::ValidationManifold, x, p, q, conv::ActionDirection; kwargs...) +function translate!(M::ValidationManifold, x, p, q, conv::ActionDirectionAndSide; kwargs...) is_point(M, p, true; kwargs...) is_point(M, q, true; kwargs...) translate!(M.manifold, array_value(x), array_value(p), array_value(q), conv) @@ -120,7 +120,13 @@ function translate!(M::ValidationManifold, x, p, q, conv::ActionDirection; kwarg return x end -function inverse_translate(M::ValidationManifold, p, q, conv::ActionDirection; kwargs...) +function inverse_translate( + M::ValidationManifold, + p, + q, + conv::ActionDirectionAndSide; + kwargs..., +) is_point(M, p, true; kwargs...) is_point(M, q, true; kwargs...) x = array_point(inverse_translate(M.manifold, array_value(p), array_value(q), conv)) @@ -133,7 +139,7 @@ function inverse_translate!( x, p, q, - conv::ActionDirection; + conv::ActionDirectionAndSide; kwargs..., ) is_point(M, p, true; kwargs...) @@ -143,7 +149,14 @@ function inverse_translate!( return x end -function translate_diff(M::ValidationManifold, p, q, X, conv::ActionDirection; kwargs...) +function translate_diff( + M::ValidationManifold, + p, + q, + X, + conv::ActionDirectionAndSide; + kwargs..., +) is_point(M, p, true; kwargs...) is_point(M, q, true; kwargs...) is_vector(M, q, X, true; kwargs...) @@ -161,7 +174,7 @@ function translate_diff!( p, q, X, - conv::ActionDirection; + conv::ActionDirectionAndSide; kwargs..., ) is_point(M, p, true; kwargs...) @@ -185,7 +198,7 @@ function inverse_translate_diff( p, q, X, - conv::ActionDirection; + conv::ActionDirectionAndSide; kwargs..., ) is_point(M, p, true; kwargs...) @@ -211,7 +224,7 @@ function inverse_translate_diff!( p, q, X, - conv::ActionDirection; + conv::ActionDirectionAndSide; kwargs..., ) is_point(M, p, true; kwargs...) diff --git a/test/groups/circle_group.jl b/test/groups/circle_group.jl index 48b6435d88..41f3b81469 100644 --- a/test/groups/circle_group.jl +++ b/test/groups/circle_group.jl @@ -1,9 +1,8 @@ include("../utils.jl") include("group_utils.jl") -# TODO: remove after bug in StaticArray is fixed -@inline Base.copy(a::SizedArray) = __copy(a) -@inline __copy(a::SizedArray{S,T}) where {S,T} = SizedArray{S,T}(copy(a.data)) +using Manifolds: + LeftForwardAction, LeftBackwardAction, RightForwardAction, RightBackwardAction @testset "Circle group" begin G = CircleGroup() diff --git a/test/groups/group_operation_action.jl b/test/groups/group_operation_action.jl index d32bddd68c..3992a90c5c 100644 --- a/test/groups/group_operation_action.jl +++ b/test/groups/group_operation_action.jl @@ -2,6 +2,9 @@ include("../utils.jl") include("group_utils.jl") +using Manifolds: + LeftForwardAction, LeftBackwardAction, RightForwardAction, RightBackwardAction + @testset "Group operation action" begin G = GroupManifold(NotImplementedManifold(), Manifolds.MultiplicationOperation()) A_left_fwd = GroupOperationAction(G) @@ -13,11 +16,9 @@ include("group_utils.jl") @test group_manifold(A_left_fwd) === G @test base_group(A_left_fwd) == G - @test repr(A_left_fwd) == "GroupOperationAction($(repr(G)), LeftForwardAction())" - @test repr(A_right_back) == "GroupOperationAction($(repr(G)), RightBackwardAction())" - - @test switch_direction(LeftForwardAction()) === RightBackwardAction() - @test switch_direction(RightBackwardAction()) === LeftForwardAction() + @test repr(A_left_fwd) == "GroupOperationAction($(repr(G)), (LeftAction(), LeftSide()))" + @test repr(A_right_back) == + "GroupOperationAction($(repr(G)), (RightAction(), RightSide()))" for type in types a_pts = convert.(type, [reshape(i:(i + 3), 2, 2) for i in 1:3]) @@ -32,7 +33,7 @@ include("group_utils.jl") test_optimal_alignment=false, test_diff=false, atol=atol, - test_switch_direction=Manifolds.SimultaneousSwitch(), + test_switch_direction=true, ) test_action( @@ -42,7 +43,7 @@ include("group_utils.jl") test_optimal_alignment=false, test_diff=false, atol=atol, - test_switch_direction=Manifolds.SimultaneousSwitch(), + test_switch_direction=true, ) end @@ -66,8 +67,9 @@ include("group_utils.jl") @test group_manifold(A_left_fwd) === G @test base_group(A_left_fwd) == G - @test repr(A_left_fwd) == "GroupOperationAction($(repr(G)), LeftForwardAction())" - @test repr(A_right_back) == "GroupOperationAction($(repr(G)), RightBackwardAction())" + @test repr(A_left_fwd) == "GroupOperationAction($(repr(G)), (LeftAction(), LeftSide()))" + @test repr(A_right_back) == + "GroupOperationAction($(repr(G)), (RightAction(), RightSide()))" test_action( A_left_fwd, @@ -76,7 +78,7 @@ include("group_utils.jl") X_pts; test_optimal_alignment=true, test_diff=true, - test_switch_direction=Manifolds.SimultaneousSwitch(), + test_switch_direction=true, ) test_action( @@ -86,7 +88,7 @@ include("group_utils.jl") X_pts; test_optimal_alignment=true, test_diff=true, - test_switch_direction=Manifolds.SimultaneousSwitch(), + test_switch_direction=true, ) test_action( @@ -96,7 +98,7 @@ include("group_utils.jl") X_pts; test_optimal_alignment=true, test_diff=true, - test_switch_direction=Manifolds.SimultaneousSwitch(), + test_switch_direction=true, ) test_action( @@ -106,7 +108,7 @@ include("group_utils.jl") X_pts; test_optimal_alignment=true, test_diff=true, - test_switch_direction=Manifolds.SimultaneousSwitch(), + test_switch_direction=true, ) @testset "apply_diff_group" begin diff --git a/test/groups/groups_general.jl b/test/groups/groups_general.jl index a243964ff5..1b65556f0c 100644 --- a/test/groups/groups_general.jl +++ b/test/groups/groups_general.jl @@ -4,6 +4,9 @@ using Base: decode_overlong include("../utils.jl") include("group_utils.jl") +using Manifolds: + LeftForwardAction, LeftBackwardAction, RightForwardAction, RightBackwardAction + @testset "General group tests" begin @testset "Not implemented operation" begin G = GroupManifold(NotImplementedManifold(), NotImplementedOperation()) @@ -126,34 +129,19 @@ include("group_utils.jl") end @testset "Action direction" begin - @test switch_direction(LeftBackwardAction()) === RightForwardAction() - @test switch_direction(LeftForwardAction()) === RightBackwardAction() - @test switch_direction(RightBackwardAction()) === LeftForwardAction() - @test switch_direction(RightForwardAction()) === LeftBackwardAction() - - @test switch_direction(LeftBackwardAction(), Manifolds.LeftRightSwitch()) === - RightBackwardAction() - @test switch_direction(LeftForwardAction(), Manifolds.LeftRightSwitch()) === - RightForwardAction() - @test switch_direction(RightBackwardAction(), Manifolds.LeftRightSwitch()) === - LeftBackwardAction() - @test switch_direction(RightForwardAction(), Manifolds.LeftRightSwitch()) === - LeftForwardAction() - - @test switch_direction(LeftBackwardAction(), Manifolds.ForwardBackwardSwitch()) === - LeftForwardAction() - @test switch_direction(LeftForwardAction(), Manifolds.ForwardBackwardSwitch()) === - LeftBackwardAction() - @test switch_direction(RightBackwardAction(), Manifolds.ForwardBackwardSwitch()) === - RightForwardAction() - @test switch_direction(RightForwardAction(), Manifolds.ForwardBackwardSwitch()) === - RightBackwardAction() + @test switch_direction(LeftAction()) === RightAction() + @test switch_direction(RightAction()) === LeftAction() G = GroupManifold(NotImplementedManifold(), NotImplementedOperation()) @test Manifolds._action_order(G, 1, 2, LeftForwardAction()) === (1, 2) @test Manifolds._action_order(G, 1, 2, RightBackwardAction()) === (2, 1) end + @testset "Action side" begin + @test switch_side(LeftSide()) === RightSide() + @test switch_side(RightSide()) === LeftSide() + end + @testset "Addition operation" begin G = GroupManifold(NotImplementedManifold(), Manifolds.AdditionOperation()) test_group( @@ -296,7 +284,7 @@ include("group_utils.jl") end end -struct NotImplementedAction <: AbstractGroupAction{LeftForwardAction} end +struct NotImplementedAction <: AbstractGroupAction{LeftAction} end @testset "General group action tests" begin @testset "Not implemented operations" begin diff --git a/test/groups/metric.jl b/test/groups/metric.jl index ea032a04d2..7a18ecf934 100644 --- a/test/groups/metric.jl +++ b/test/groups/metric.jl @@ -6,6 +6,9 @@ import Manifolds: local_metric using Manifolds: LeftInvariantMetric, RightInvariantMetric +using Manifolds: + LeftForwardAction, LeftBackwardAction, RightForwardAction, RightBackwardAction + struct TestInvariantMetricBase <: AbstractMetric end function active_traits( @@ -126,10 +129,10 @@ end end @testset "invariant metric direction" begin - @test direction(HasRightInvariantMetric()) === RightBackwardAction() - @test direction(HasLeftInvariantMetric()) === LeftForwardAction() - @test direction(HasRightInvariantMetric) === RightBackwardAction() - @test direction(HasLeftInvariantMetric) === LeftForwardAction() + @test direction_and_side(HasRightInvariantMetric()) === RightBackwardAction() + @test direction_and_side(HasLeftInvariantMetric()) === LeftForwardAction() + @test direction_and_side(HasRightInvariantMetric) === RightBackwardAction() + @test direction_and_side(HasLeftInvariantMetric) === LeftForwardAction() end @testset "invariant metrics on SE(3)" begin diff --git a/test/groups/rotation_action.jl b/test/groups/rotation_action.jl index 725b4a0f26..71be2acd73 100644 --- a/test/groups/rotation_action.jl +++ b/test/groups/rotation_action.jl @@ -6,12 +6,11 @@ include("group_utils.jl") M = Rotations(2) G = SpecialOrthogonal(2) A_left = RotationAction(Euclidean(2), G) - A_right = RotationAction(Euclidean(2), G, RightForwardAction()) + A_right = RotationAction(Euclidean(2), G, RightAction()) - @test repr(A_left) == - "RotationAction($(repr(Euclidean(2))), $(repr(G)), LeftForwardAction())" + @test repr(A_left) == "RotationAction($(repr(Euclidean(2))), $(repr(G)), LeftAction())" @test repr(A_right) == - "RotationAction($(repr(Euclidean(2))), $(repr(G)), RightForwardAction())" + "RotationAction($(repr(Euclidean(2))), $(repr(G)), RightAction())" types_a = [Matrix{Float64}] @@ -19,7 +18,7 @@ include("group_utils.jl") @test group_manifold(A_left) == Euclidean(2) @test base_group(A_left) == G - @test isa(A_left, AbstractGroupAction{<:LeftForwardAction}) + @test isa(A_left, AbstractGroupAction{LeftAction}) @test base_manifold(G) == M for (i, T_A, T_M) in zip(1:length(types_a), types_a, types_m) @@ -76,7 +75,7 @@ end @test group_manifold(A) == Euclidean(3) @test base_group(A) == G - @test isa(A, AbstractGroupAction{LeftForwardAction}) + @test isa(A, AbstractGroupAction{LeftAction}) @test base_manifold(G) == M for (i, T_A, T_M) in zip(1:length(types_a), types_a, types_m) diff --git a/test/groups/rotation_translation_action.jl b/test/groups/rotation_translation_action.jl index 4f31971721..7c2c8fb6a3 100644 --- a/test/groups/rotation_translation_action.jl +++ b/test/groups/rotation_translation_action.jl @@ -6,12 +6,12 @@ include("group_utils.jl") G = SpecialEuclidean(2) M = base_manifold(G) A_left = RotationTranslationAction(Euclidean(2), G) - A_right = RotationTranslationAction(Euclidean(2), G, RightForwardAction()) + A_right = RotationTranslationAction(Euclidean(2), G, RightAction()) @test repr(A_left) == - "RotationTranslationAction($(repr(Euclidean(2))), $(repr(G)), LeftForwardAction())" + "RotationTranslationAction($(repr(Euclidean(2))), $(repr(G)), LeftAction())" @test repr(A_right) == - "RotationTranslationAction($(repr(Euclidean(2))), $(repr(G)), RightForwardAction())" + "RotationTranslationAction($(repr(Euclidean(2))), $(repr(G)), RightAction())" types_a = [ArrayPartition{Float64,Tuple{Vector{Float64},Matrix{Float64}}}] @@ -19,7 +19,7 @@ include("group_utils.jl") @test group_manifold(A_left) == Euclidean(2) @test base_group(A_left) == G - @test isa(A_left, AbstractGroupAction{<:LeftForwardAction}) + @test isa(A_left, AbstractGroupAction{LeftAction}) @test base_manifold(G) == M for (i, T_A, T_M) in zip(1:length(types_a), types_a, types_m) diff --git a/test/groups/special_euclidean.jl b/test/groups/special_euclidean.jl index 31a917f7f4..57a58c4230 100644 --- a/test/groups/special_euclidean.jl +++ b/test/groups/special_euclidean.jl @@ -5,6 +5,9 @@ using ManifoldsBase: VeeOrthogonalBasis Random.seed!(10) +using Manifolds: + LeftForwardAction, LeftBackwardAction, RightForwardAction, RightBackwardAction + @testset "Special Euclidean group" begin for se_parameter in [:field, :type] @testset "SpecialEuclidean($n)" for n in (2, 3, 4) @@ -64,123 +67,112 @@ Random.seed!(10) @test isapprox(G, identity_element(G), Identity(G)) end - @testset "product repr" begin - pts = [ArrayPartition(tp...) for tp in tuple_pts] - X_pts = [ArrayPartition(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 = 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} - end - @test affine_matrix(G, Identity(G)) == SDiagonal{n,Float64}(I) + pts = [ArrayPartition(tp...) for tp in tuple_pts] + X_pts = [ArrayPartition(tX...) for tX in tuple_X] - 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 + @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, - 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, - ) + 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} + 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 + test_manifold( + G_TR, + pts; + is_mutating=true, + exp_log_atol_multiplier=50, + test_inner=false, + test_norm=false, + ) 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 + 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 end diff --git a/test/groups/translation_action.jl b/test/groups/translation_action.jl index a278f323e4..1348759b93 100644 --- a/test/groups/translation_action.jl +++ b/test/groups/translation_action.jl @@ -7,9 +7,9 @@ include("group_utils.jl") G = TranslationGroup(2, 3) A = TranslationAction(Euclidean(2, 3), G) - @test repr(A) == "TranslationAction($(repr(M)), $(repr(G)), LeftForwardAction())" + @test repr(A) == "TranslationAction($(repr(M)), $(repr(G)), LeftAction())" @test repr(switch_direction(A)) == - "TranslationAction($(repr(M)), $(repr(G)), RightForwardAction())" + "TranslationAction($(repr(M)), $(repr(G)), RightAction())" types_a = [Matrix{Float64}] From a326c632351150a8bce5623ca2f1600c51b69ee3 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Thu, 5 Oct 2023 20:40:10 +0200 Subject: [PATCH 36/81] adapting to moving some meta-manifolds to ManifoldsBase --- src/Manifolds.jl | 25 +- src/manifolds/Fiber.jl | 116 --- src/manifolds/FiberBundle.jl | 393 --------- src/manifolds/PowerManifold.jl | 69 -- src/manifolds/ProductManifold.jl | 1398 ++---------------------------- src/manifolds/VectorBundle.jl | 560 +----------- src/manifolds/VectorFiber.jl | 278 +----- src/product_representations.jl | 35 - src/utils.jl | 64 -- 9 files changed, 111 insertions(+), 2827 deletions(-) diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 445bafa139..ca36a37901 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -58,6 +58,7 @@ import ManifoldsBase: embed!, exp, exp!, + fiber_dimension, get_basis, get_basis_default, get_basis_diagonalizing, @@ -142,6 +143,9 @@ import ManifoldsBase: riemann_tensor, riemann_tensor!, set_component!, + submanifold, + submanifold_component, + submanifold_components, vector_space_dimension, vector_transport_along, # just specified in Euclidean - the next 5 as well vector_transport_along_diff, @@ -211,6 +215,7 @@ using ManifoldsBase: AbstractLinearVectorTransportMethod, ApproximateInverseRetraction, ApproximateRetraction, + BundleFibers, CachedBasis, CayleyRetraction, CayleyInverseRetraction, @@ -230,7 +235,12 @@ using ManifoldsBase: EmptyTrait, EuclideanMetric, ExponentialRetraction, + FiberBundleInverseProductRetraction, + FiberBundleProductRetraction, + FiberBundleProductVectorTransport, + FiberType, FVector, + InverseProductRetraction, IsIsometricEmbeddedManifold, IsEmbeddedManifold, IsEmbeddedSubmanifold, @@ -252,6 +262,10 @@ using ManifoldsBase: PowerManifold, PowerManifoldNested, PowerManifoldNestedReplacing, + ProductBasisData, + ProductManifold, + ProductMetric, + ProductRetraction, ProjectedOrthonormalBasis, ProjectionInverseRetraction, ProjectionRetraction, @@ -266,6 +280,9 @@ using ManifoldsBase: ShootingInverseRetraction, SoftmaxRetraction, SoftmaxInverseRetraction, + TangentBundle, + TangentBundleFibers, + TangentSpaceAtPoint, TangentSpaceType, TCoTSpaceType, TFVector, @@ -274,6 +291,10 @@ using ManifoldsBase: ValidationManifold, ValidationMPoint, ValidationTVector, + VectorBundle, + VectorBundleFibers, + VectorSpaceAtPoint, + VectorSpaceFiberType, VectorSpaceType, VeeOrthogonalBasis, @invoke_maker, @@ -284,6 +305,7 @@ using ManifoldsBase: get_parameter, merge_traits, next_trait, + number_of_components, number_system, real_dimension, rep_size_to_colons, @@ -291,7 +313,8 @@ using ManifoldsBase: shortest_geodesic!, size_to_tuple, trait, - wrap_type_parameter + wrap_type_parameter, + ziptuples using ManifoldDiff: ManifoldDiff using ManifoldDiff: default_differential_backend, diff --git a/src/manifolds/Fiber.jl b/src/manifolds/Fiber.jl index 0041e52a4b..8b13789179 100644 --- a/src/manifolds/Fiber.jl +++ b/src/manifolds/Fiber.jl @@ -1,117 +1 @@ -""" - abstract type FiberType end - -An abstract type for fiber types. -""" -abstract type FiberType end - -""" - BundleFibers(fiber::FiberType, M::AbstractManifold) - -Type representing a family of fibers of a fiber bundle over `M` -with vectorfiber of type `fiber`. In contrast with `FiberBundle`, operations -on `BundleFibers` expect point-like and fiber-like parts to be -passed separately instead of being bundled together. It can be thought of -as a representation of fibers from a fiber bundle but without -storing the point at which a fiber is attached (which is specified -separately in various functions). -""" -struct BundleFibers{TF<:FiberType,TM<:AbstractManifold} - fiber::TF - manifold::TM -end - -""" - FiberAtPoint{ - 𝔽, - TFiber<:BundleFibers{<:FiberType,<:AbstractManifold{𝔽}}, - TX, - } <: AbstractManifold{𝔽} - -A fiber of a [`FiberBundle`](@ref) at a point `p` on the manifold. -This is modelled using [`BundleFibers`](@ref) with only a fiber part -and fixing the point-like part to be just `p`. - -This fiber itself is also a `manifold`. For vector fibers it's by default flat and hence -isometric to the [`Euclidean`](@ref) manifold. - -# Constructor - - FiberAtPoint(fiber::BundleFibers, p) - -A fiber of type `fiber` at point `p` from the manifold `fiber.manifold`. -""" -struct FiberAtPoint{𝔽,TFiber<:BundleFibers{<:FiberType,<:AbstractManifold{𝔽}},TX} <: - AbstractManifold{𝔽} - fiber::TFiber - point::TX -end - -""" - allocate_result(B::BundleFibers, f, x...) - -Allocates an array for the result of function `f` that is -an element of the fiber space of type `B.fiber` on manifold `B.manifold` -and arguments `x...` for implementing the non-modifying operation -using the modifying operation. -""" -@inline function allocate_result(B::BundleFibers, f::TF, x...) where {TF} - if length(x) == 0 - # TODO: this may be incorrect when point and tangent vector representation are - # different for the manifold but there is no easy and generic way around that - return allocate_result(B.manifold, f) - else - T = allocate_result_type(B, f, x) - return allocate(x[1], T) - end -end - -""" - allocate_result_type(B::BundleFibers, f, args::NTuple{N,Any}) where N - -Return type of element of the array that will represent the result of -function `f` for representing an operation with result in the fiber `fiber` -for manifold `M` on given arguments (passed at a tuple). -""" -@inline function allocate_result_type( - ::BundleFibers, - f::TF, - args::NTuple{N,Any}, -) where {TF,N} - return typeof(mapreduce(eti -> one(number_eltype(eti)), +, args)) -end - -base_manifold(B::BundleFibers) = base_manifold(B.manifold) -base_manifold(B::FiberAtPoint) = base_manifold(B.fiber) - -function fiber_dimension(B::BundleFibers) - return fiber_dimension(B.manifold, B.fiber) -end - -function Base.show(io::IO, fiber::BundleFibers) - return print(io, "BundleFibers($(fiber.fiber), $(fiber.manifold))") -end -function Base.show(io::IO, ::MIME"text/plain", vs::FiberAtPoint) - summary(io, vs) - println(io, "\nFiber:") - pre = " " - sf = sprint(show, "text/plain", vs.fiber; context=io, sizehint=0) - sf = replace(sf, '\n' => "\n$(pre)") - println(io, pre, sf) - println(io, "Base point:") - sp = sprint(show, "text/plain", vs.point; context=io, sizehint=0) - sp = replace(sp, '\n' => "\n$(pre)") - return print(io, pre, sp) -end - -""" - zero_vector(B::BundleFibers, p) - -Compute the zero vector from the vector space of type `B.fiber` at point `p` -from manifold `B.manifold`. -""" -function zero_vector(B::BundleFibers, p) - X = allocate_result(B, zero_vector, p) - return zero_vector!(B, X, p) -end diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index c9317254c3..8b13789179 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -1,394 +1 @@ -@doc raw""" - FiberBundleProductVectorTransport{ - TMP<:AbstractVectorTransportMethod, - TMV<:AbstractVectorTransportMethod, - } <: AbstractVectorTransportMethod - -Vector transport type on [`FiberBundle`](@ref). `method_point` is used for vector transport -of the point part and `method_fiber` is used for transport of the fiber part. - -The vector transport is derived as a product manifold-style vector transport. The considered -product manifold is the product between the manifold $\mathcal M$ and the topological vector -space isometric to the fiber. - -# Constructor - - FiberBundleProductVectorTransport( - method_point::AbstractVectorTransportMethod, - method_fiber::AbstractVectorTransportMethod, - ) - FiberBundleProductVectorTransport() - -By default both methods are set to `ParallelTransport`. -""" -struct FiberBundleProductVectorTransport{ - TMP<:AbstractVectorTransportMethod, - TMV<:AbstractVectorTransportMethod, -} <: AbstractVectorTransportMethod - method_point::TMP - method_fiber::TMV -end - -function FiberBundleProductVectorTransport() - return FiberBundleProductVectorTransport(ParallelTransport(), ParallelTransport()) -end -function FiberBundleProductVectorTransport(M::AbstractManifold) - m = default_vector_transport_method(M) - return FiberBundleProductVectorTransport(m, m) -end - -""" - FiberBundle{𝔽,TVS<:FiberType,TM<:AbstractManifold{𝔽},TVT<:FiberBundleProductVectorTransport} <: AbstractManifold{𝔽} - -Fiber bundle on a [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M` -of type [`FiberType`](@ref). Examples include vector bundles, principal bundles or unit tangent bundles. - -# Constructor - - FiberBundle(M::AbstractManifold, type::FiberType) -""" -struct FiberBundle{ - 𝔽, - TF<:FiberType, - TM<:AbstractManifold{𝔽}, - TVT<:FiberBundleProductVectorTransport, -} <: AbstractManifold{𝔽} - type::TF - manifold::TM - fiber::BundleFibers{TF,TM} - vector_transport::TVT -end - -vector_bundle_transport(::FiberType, M::AbstractManifold) = ParallelTransport() - -function FiberBundle( - fiber::TVS, - M::TM, - vtm::FiberBundleProductVectorTransport, -) where {TVS<:FiberType,TM<:AbstractManifold{𝔽}} where {𝔽} - return FiberBundle{𝔽,TVS,TM,typeof(vtm)}(fiber, M, BundleFibers(fiber, M), vtm) -end -function FiberBundle(fiber::FiberType, M::AbstractManifold) - vtmm = vector_bundle_transport(fiber, M) - vtbm = FiberBundleProductVectorTransport(vtmm, vtmm) - return FiberBundle(fiber, M, vtbm) -end - -struct FiberBundleBasisData{BBasis<:CachedBasis,TBasis<:CachedBasis} - base_basis::BBasis - fiber_basis::TBasis -end - -@doc raw""" - struct FiberBundleInverseProductRetraction <: AbstractInverseRetractionMethod end - -Inverse retraction of the point `y` at point `p` from vector bundle `B` over manifold -`B.fiber` (denoted $\mathcal M$). The inverse retraction is derived as a product manifold-style -approximation to the logarithmic map in the Sasaki metric. The considered product manifold -is the product between the manifold $\mathcal M$ and the topological vector space isometric -to the fiber. - -Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - Similarly, $q = (x_q, V_q)$. - -The inverse retraction is calculated as - -$\operatorname{retr}^{-1}_p q = (\operatorname{retr}^{-1}_{x_p}(x_q), V_{\operatorname{retr}^{-1}} - V_p)$ - -where $V_{\operatorname{retr}^{-1}}$ is the result of vector transport of $V_q$ to the point $x_p$. -The difference $V_{\operatorname{retr}^{-1}} - V_p$ corresponds to the logarithmic map in -the vector space $F$. - -See also [`FiberBundleProductRetraction`](@ref). -""" -struct FiberBundleInverseProductRetraction <: AbstractInverseRetractionMethod end - -@doc raw""" - struct FiberBundleProductRetraction <: AbstractRetractionMethod end - -Product retraction map of tangent vector $X$ at point $p$ from vector bundle `B` over -manifold `B.fiber` (denoted $\mathcal M$). The retraction is derived as a product manifold-style -approximation to the exponential map in the Sasaki metric. The considered product manifold -is the product between the manifold $\mathcal M$ and the topological vector space isometric -to the fiber. - -Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - * The tangent vector $X = (V_{X,M}, V_{X,F}) ∈ T_pB$ where - $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}\mathcal M$ and - $V_{X,F}$ is a tangent vector from the tangent space $T_{V_p}F$ (isomorphic to $F$). - -The retraction is calculated as - -$\operatorname{retr}_p(X) = (\exp_{x_p}(V_{X,M}), V_{\exp})$ - -where $V_{\exp}$ is the result of vector transport of $V_p + V_{X,F}$ -to the point $\exp_{x_p}(V_{X,M})$. -The sum $V_p + V_{X,F}$ corresponds to the exponential map in the vector space $F$. - -See also [`FiberBundleInverseProductRetraction`](@ref). -""" -struct FiberBundleProductRetraction <: AbstractRetractionMethod end - -base_manifold(B::FiberBundle) = base_manifold(B.manifold) - -""" - bundle_projection(B::FiberBundle, 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 -of `p` is attached. -""" -bundle_projection(B::FiberBundle, p) = submanifold_component(B.manifold, p, Val(1)) - -function get_basis(M::FiberBundle, p, B::AbstractBasis) - xp1 = submanifold_component(p, Val(1)) - base_basis = get_basis(M.manifold, xp1, B) - fiber_basis = get_basis(M.fiber, xp1, B) - return CachedBasis(B, FiberBundleBasisData(base_basis, fiber_basis)) -end -function get_basis(M::FiberBundle, p, B::CachedBasis) - return invoke(get_basis, Tuple{AbstractManifold,Any,CachedBasis}, M, p, B) -end - -function get_basis(M::FiberBundle, p, B::DiagonalizingOrthonormalBasis) - xp1 = submanifold_component(p, Val(1)) - bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(1))) - b1 = get_basis(M.manifold, xp1, bv1) - bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(2))) - b2 = get_basis(M.fiber, xp1, bv2) - return CachedBasis(B, FiberBundleBasisData(b1, b2)) -end - -function get_coordinates(M::FiberBundle, p, X, B::AbstractBasis) - px, Vx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - n = manifold_dimension(M.manifold) - return vcat( - get_coordinates(M.manifold, px, VXM, B), - get_coordinates(M.fiber, px, VXF, B), - ) -end - -function get_coordinates!(M::FiberBundle, Y, p, X, B::AbstractBasis) - px, Vx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - n = manifold_dimension(M.manifold) - get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B) - get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B) - return Y -end - -function get_coordinates( - M::FiberBundle, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, -) where {𝔽} - px, Vx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - return vcat( - get_coordinates(M.manifold, px, VXM, B.data.base_basis), - get_coordinates(M.fiber, px, VXF, B.data.fiber_basis), - ) -end - -function get_coordinates!( - M::FiberBundle, - Y, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, -) where {𝔽} - px, Vx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - n = manifold_dimension(M.manifold) - get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B.data.base_basis) - get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B.data.fiber_basis) - return Y -end - -function get_vector(M::FiberBundle, p, X, B::AbstractBasis) - n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - return ArrayPartition( - get_vector(M.manifold, xp1, X[1:n], B), - get_vector(M.fiber, xp1, X[(n + 1):end], B), - ) -end - -function get_vector!(M::FiberBundle, Y, p, X, B::AbstractBasis) - n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - get_vector!(M.manifold, submanifold_component(Y, Val(1)), xp1, X[1:n], B) - get_vector!(M.fiber, submanifold_component(Y, Val(2)), xp1, X[(n + 1):end], B) - return Y -end - -function get_vector( - M::FiberBundle, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, -) where {𝔽} - n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - 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.fiber_basis), - ) -end - -function get_vector!( - M::FiberBundle, - Y, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, -) where {𝔽} - n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - get_vector!( - M.manifold, - submanifold_component(Y, Val(1)), - xp1, - X[1:n], - B.data.base_basis, - ) - get_vector!( - M.fiber, - submanifold_component(Y, Val(2)), - xp1, - X[(n + 1):end], - B.data.fiber_basis, - ) - return Y -end - -function get_vectors( - M::FiberBundle, - p::ArrayPartition, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:Manifolds.FiberBundleBasisData}, -) where {𝔽} - xp1 = submanifold_component(p, Val(1)) - zero_m = zero_vector(M.manifold, xp1) - zero_f = zero_vector(M.fiber, xp1) - vs = typeof(ArrayPartition(zero_m, zero_f))[] - for bv in get_vectors(M.manifold, xp1, B.data.base_basis) - push!(vs, ArrayPartition(bv, zero_f)) - end - for bv in get_vectors(M.fiber, xp1, B.data.fiber_basis) - push!(vs, ArrayPartition(zero_m, bv)) - end - return vs -end -function get_vectors(M::BundleFibers, p, B::CachedBasis) - return get_vectors(M.manifold, p, B) -end - -""" - getindex(p::ArrayPartition, M::FiberBundle, s::Symbol) - p[M::FiberBundle, s] - -Access the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` by -using the symbols `:point` and `:vector` or `:fiber` for the base and vector or fiber -component, respectively. -""" -@inline function Base.getindex(p::ArrayPartition, M::FiberBundle, s::Symbol) - (s === :point) && return p.x[1] - (s === :vector || s === :fiber) && return p.x[2] - return throw(DomainError(s, "unknown component $s on $M.")) -end - -function _isapprox(B::FiberBundle, p, q; kwargs...) - xp, Vp = submanifold_components(B.manifold, p) - xq, Vq = submanifold_components(B.manifold, q) - return isapprox(B.manifold, xp, xq; kwargs...) && - isapprox(FiberAtPoint(B.fiber, xp), Vp, Vq; kwargs...) -end -function _isapprox(B::FiberBundle, p, X, Y; kwargs...) - px, Vx = submanifold_components(B.manifold, p) - VXM, VXF = submanifold_components(B.manifold, X) - VYM, VYF = submanifold_components(B.manifold, Y) - return isapprox(B.manifold, VXM, VYM; kwargs...) && - isapprox(FiberAtPoint(B.fiber, px), VXF, VYF; kwargs...) -end - -function manifold_dimension(B::FiberBundle) - return manifold_dimension(B.manifold) + fiber_dimension(B.fiber) -end - -function Random.rand!(rng::AbstractRNG, M::FiberBundle, pX; vector_at=nothing) - pXM, pXF = submanifold_components(M.manifold, pX) - if vector_at === nothing - rand!(rng, M.manifold, pXM) - rand!(rng, FiberAtPoint(M.fiber, pXM), pXF) - else - vector_atM, vector_atF = submanifold_components(M.manifold, vector_at) - rand!(rng, M.manifold, pXM; vector_at=vector_atM) - rand!(rng, FiberAtPoint(M.fiber, pXM), pXF; vector_at=vector_atF) - end - return pX -end - -""" - setindex!(p::ArrayPartition, val, M::FiberBundle, s::Symbol) - p[M::VectorBundle, s] = val - -Set the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` to `val` by -using the symbols `:point` and `:fiber` or `:vector` for the base and fiber or vector -component, respectively. - -!!! note - - The *content* of element of `p` is replaced, not the element itself. -""" -@inline function Base.setindex!(x::ArrayPartition, val, M::FiberBundle, s::Symbol) - if s === :point - return copyto!(x.x[1], val) - elseif s === :vector || s === :fiber - return copyto!(x.x[2], val) - else - throw(DomainError(s, "unknown component $s on $M.")) - end -end - -@inline function Base.view(x::ArrayPartition, M::FiberBundle, s::Symbol) - (s === :point) && return x.x[1] - (s === :vector || s === :fiber) && return x.x[2] - throw(DomainError(s, "unknown component $s on $M.")) -end - -@doc raw""" - zero_vector(B::FiberBundle, p) - -Zero tangent vector at point `p` from the fiber bundle `B` -over manifold `B.fiber` (denoted $\mathcal M$). The zero vector belongs to the space $T_{p}B$ - -Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - -The zero vector is calculated as - -$\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)$ - -where $\mathbf{0}_{x_p}$ is the zero tangent vector from $T_{x_p}\mathcal M$ and -$\mathbf{0}_F$ is the zero element of the vector space $F$. -""" -zero_vector(::FiberBundle, ::Any...) - -function zero_vector!(B::FiberBundle, X, p) - xp, Vp = submanifold_components(B.manifold, p) - VXM, VXF = submanifold_components(B.manifold, X) - zero_vector!(B.manifold, VXM, xp) - zero_vector!(B.fiber, VXF, Vp) - return X -end diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index eae6d3e4ba..ed3b1ab516 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -91,48 +91,6 @@ function allocate_result(M::PowerManifoldNestedReplacing, f, ::Identity, x...) return allocate_result(M, f, x...) end -""" - change_representer(M::AbstractPowerManifold, ::AbstractMetric, p, X) - -Since the metric on a power manifold decouples, the change of a representer can be done elementwise -""" -change_representer(::AbstractPowerManifold, ::AbstractMetric, ::Any, ::Any) - -function change_representer!(M::AbstractPowerManifold, Y, G::AbstractMetric, p, X) - rep_size = representation_size(M.manifold) - for i in get_iterator(M) - change_representer!( - M.manifold, - _write(M, rep_size, Y, i), - G, - _read(M, rep_size, p, i), - _read(M, rep_size, X, i), - ) - end - return Y -end - -""" - change_metric(M::AbstractPowerManifold, ::AbstractMetric, p, X) - -Since the metric on a power manifold decouples, the change of metric can be done elementwise. -""" -change_metric(M::AbstractPowerManifold, ::AbstractMetric, ::Any, ::Any) - -function change_metric!(M::AbstractPowerManifold, Y, G::AbstractMetric, p, X) - rep_size = representation_size(M.manifold) - for i in get_iterator(M) - change_metric!( - M.manifold, - _write(M, rep_size, Y, i), - G, - _read(M, rep_size, p, i), - _read(M, rep_size, X, i), - ) - end - return Y -end - @doc raw""" flat(M::AbstractPowerManifold, p, X) @@ -320,10 +278,6 @@ end Distributions.support(tvd::PowerFVectorDistribution) = FVectorSupport(tvd.type, tvd.point) Distributions.support(d::PowerPointDistribution) = MPointSupport(d.manifold) -function vector_bundle_transport(fiber::VectorSpaceType, M::PowerManifold) - return ParallelTransport() -end - @doc raw""" volume_density(M::PowerManifold, p, X) @@ -341,29 +295,6 @@ function volume_density(M::PowerManifold, p, X) return density end -@doc raw""" - Y = Weingarten(M::AbstractPowerManifold, p, X, V) - Weingarten!(M::AbstractPowerManifold, Y, p, X, V) - -Since the metric decouples, also the computation of the Weingarten map -``\mathcal W_p`` can be computed elementwise on the single elements of the [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds/#sec-power-manifold) `M`. -""" -Weingarten(::AbstractPowerManifold, p, X, V) - -function Weingarten!(M::AbstractPowerManifold, Y, p, X, V) - rep_size = representation_size(M.manifold) - for i in get_iterator(M) - Weingarten!( - M.manifold, - _write(M, rep_size, Y, i), - _read(M, rep_size, p, i), - _read(M, rep_size, X, i), - _read(M, rep_size, V, i), - ) - end - return Y -end - @inline function _write( ::PowerManifoldMultidimensional, rep_size::Tuple, diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index fcbf086d24..5814f2953e 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -1,65 +1,12 @@ -@doc raw""" - ProductManifold{𝔽,TM<:Tuple} <: AbstractManifold{𝔽} - -Product manifold $M_1 × M_2 × … × M_n$ with product geometry. - -# Constructor - - ProductManifold(M_1, M_2, ..., M_n) -generates the product manifold $M_1 × M_2 × … × M_n$. -Alternatively, the same manifold can be contructed using the `×` operator: -`M_1 × M_2 × M_3`. -""" -struct ProductManifold{𝔽,TM<:Tuple} <: AbstractDecoratorManifold{𝔽} - manifolds::TM -end - -function ProductManifold(manifolds::AbstractManifold...) - 𝔽 = ManifoldsBase._unify_number_systems((number_system.(manifolds))...) - return ProductManifold{𝔽,typeof(manifolds)}(manifolds) +function active_traits(f, ::ProductManifold, args...) + return merge_traits(IsDefaultMetric(ProductMetric())) end -""" - getindex(M::ProductManifold, i) - M[i] - -access the `i`th manifold component from the [`ProductManifold`](@ref) `M`. -""" -@inline Base.getindex(M::ProductManifold, i::Integer) = M.manifolds[i] - -ProductManifold() = throw(MethodError("No method matching ProductManifold().")) - -const PRODUCT_BASIS_LIST = [ - VeeOrthogonalBasis, - DefaultBasis, - DefaultBasis{<:Any,TangentSpaceType}, - DefaultOrthogonalBasis, - DefaultOrthogonalBasis{<:Any,TangentSpaceType}, - DefaultOrthonormalBasis, - DefaultOrthonormalBasis{<:Any,TangentSpaceType}, - ProjectedOrthonormalBasis{:gram_schmidt,ℝ}, - ProjectedOrthonormalBasis{:svd,ℝ}, -] - -""" - ProductBasisData - -A typed tuple to store tuples of data of stored/precomputed bases for a [`ProductManifold`](@ref). -""" -struct ProductBasisData{T<:Tuple} - parts::T +function allocate_coordinates(::ProductManifold, p, T, n::Int) + return allocate(submanifold_component(p, 1), T, n) end -const PRODUCT_BASIS_LIST_CACHED = [CachedBasis] - -""" - ProductMetric <: AbstractMetric - -A type to represent the product of metrics for a [`ProductManifold`](@ref). -""" -struct ProductMetric <: AbstractMetric end - """ ProductFVectorDistribution([type::VectorBundleFibers], [x], distrs...) @@ -91,58 +38,6 @@ struct ProductPointDistribution{ distributions::TD end -""" - ProductRetraction(retractions::AbstractRetractionMethod...) - -Product retraction of `retractions`. Works on [`ProductManifold`](@ref). -""" -struct ProductRetraction{TR<:Tuple} <: AbstractRetractionMethod - retractions::TR -end - -function ProductRetraction(retractions::AbstractRetractionMethod...) - return ProductRetraction{typeof(retractions)}(retractions) -end - -""" - InverseProductRetraction(retractions::AbstractInverseRetractionMethod...) - -Product inverse retraction of `inverse retractions`. Works on [`ProductManifold`](@ref). -""" -struct InverseProductRetraction{TR<:Tuple} <: AbstractInverseRetractionMethod - inverse_retractions::TR -end - -function InverseProductRetraction(inverse_retractions::AbstractInverseRetractionMethod...) - return InverseProductRetraction{typeof(inverse_retractions)}(inverse_retractions) -end - -@inline function allocate_result(M::ProductManifold, f) - return ArrayPartition(map(N -> allocate_result(N, f), M.manifolds)) -end - -function allocation_promotion_function(M::ProductManifold, f, args::Tuple) - apfs = map(MM -> allocation_promotion_function(MM, f, args), M.manifolds) - return reduce(combine_allocation_promotion_functions, apfs) -end - -""" - ProductVectorTransport(methods::AbstractVectorTransportMethod...) - -Product vector transport type of `methods`. Works on [`ProductManifold`](@ref). -""" -struct ProductVectorTransport{TR<:Tuple} <: AbstractVectorTransportMethod - methods::TR -end - -function ProductVectorTransport(methods::AbstractVectorTransportMethod...) - return ProductVectorTransport{typeof(methods)}(methods) -end - -function active_traits(f, ::ProductManifold, args...) - return merge_traits(IsDefaultMetric(ProductMetric())) -end - function adjoint_Jacobi_field( M::ProductManifold, p::ArrayPartition, @@ -177,166 +72,6 @@ function adjoint_Jacobi_field!(M::ProductManifold, Y, p, q, t, X, β::Tβ) where return Y end -function allocate_coordinates(M::AbstractManifold, p::ArrayPartition, T, n::Int) - return allocate_coordinates(M, p.x[1], T, n) -end - -""" - change_representer(M::ProductManifold, ::AbstractMetric, p, X) - -Since the metric on a product manifold decouples, the change of a representer can be done elementwise -""" -change_representer(::ProductManifold, ::AbstractMetric, ::Any, ::Any) - -function change_representer!(M::ProductManifold, Y, G::AbstractMetric, p, X) - map( - (m, y, P, x) -> change_representer!(m, y, G, P, x), - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, X), - ) - return Y -end - -""" - change_metric(M::ProductManifold, ::AbstractMetric, p, X) - -Since the metric on a product manifold decouples, the change of metric can be done elementwise. -""" -change_metric(::ProductManifold, ::AbstractMetric, ::Any, ::Any) - -function change_metric!(M::ProductManifold, Y, G::AbstractMetric, p, X) - map( - (m, y, P, x) -> change_metric!(m, y, G, P, x), - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, X), - ) - return Y -end - -""" - check_point(M::ProductManifold, p; kwargs...) - -Check whether `p` is a valid point on the [`ProductManifold`](@ref) `M`. -If `p` is not a point on `M` a [`CompositeManifoldError`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions.html#ManifoldsBase.CompositeManifoldError).consisting of all error messages of the -components, for which the tests fail is returned. - -The tolerance for the last test can be set using the `kwargs...`. -""" -function check_point(M::ProductManifold, p::ArrayPartition; kwargs...) - ts = ziptuples(Tuple(1:length(M.manifolds)), M.manifolds, submanifold_components(M, p)) - e = [(t[1], check_point(t[2:end]...; kwargs...)) for t in ts] - errors = filter((x) -> !(x[2] === nothing), e) - cerr = [ComponentManifoldError(er...) for er in errors] - (length(errors) > 1) && return CompositeManifoldError(cerr) - (length(errors) == 1) && return cerr[1] - return nothing -end -function check_point(M::ProductManifold, p; kwargs...) - return DomainError( - typeof(p), - "The point $p is not a point on $M, since currently only ArrayPartition is supported as type for points on arbitrary product manifolds", - ) -end - -""" - check_size(M::ProductManifold, p; kwargs...) - -Check whether `p` is of valid size on the [`ProductManifold`](@ref) `M`. -If `p` has components of wrong size a [`CompositeManifoldError`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions.html#ManifoldsBase.CompositeManifoldError).consisting of all error messages of the -components, for which the tests fail is returned. - -The tolerance for the last test can be set using the `kwargs...`. -""" -function check_size(M::ProductManifold, p::ArrayPartition) - ts = ziptuples(Tuple(1:length(M.manifolds)), M.manifolds, submanifold_components(M, p)) - e = [(t[1], check_size(t[2:end]...)) for t in ts] - errors = filter((x) -> !(x[2] === nothing), e) - cerr = [ComponentManifoldError(er...) for er in errors] - (length(errors) > 1) && return CompositeManifoldError(cerr) - (length(errors) == 1) && return cerr[1] - return nothing -end -function check_size(M::ProductManifold, p; kwargs...) - return DomainError( - typeof(p), - "The point $p is not a point on $M, since currently only ArrayPartition is supported as type for points on arbitrary product manifolds", - ) -end -function check_size(M::ProductManifold, p::ArrayPartition, X::ArrayPartition) - ts = ziptuples( - Tuple(1:length(M.manifolds)), - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - ) - e = [(t[1], check_size(t[2:end]...)) for t in ts] - errors = filter(x -> !(x[2] === nothing), e) - cerr = [ComponentManifoldError(er...) for er in errors] - (length(errors) > 1) && return CompositeManifoldError(cerr) - (length(errors) == 1) && return cerr[1] - return nothing -end -function check_size(M::ProductManifold, p, X; kwargs...) - return DomainError( - typeof(X), - "The vector $X is not a tangent vector to any tangent space on $M, since currently only ArrayPartition is a supported type for tangent vectors on arbitrary product manifolds", - ) -end -""" - check_vector(M::ProductManifold, p, X; kwargs... ) - -Check whether `X` is a tangent vector to `p` on the [`ProductManifold`](@ref) -`M`, i.e. all projections to base manifolds must be respective tangent vectors. -If `X` is not a tangent vector to `p` on `M` a [`CompositeManifoldError`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions.html#ManifoldsBase.CompositeManifoldError).consisting -of all error messages of the components, for which the tests fail is returned. - -The tolerance for the last test can be set using the `kwargs...`. -""" -function check_vector(M::ProductManifold, p::ArrayPartition, X::ArrayPartition; kwargs...) - ts = ziptuples( - Tuple(1:length(M.manifolds)), - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - ) - e = [(t[1], check_vector(t[2:end]...; kwargs...)) for t in ts] - errors = filter(x -> !(x[2] === nothing), e) - cerr = [ComponentManifoldError(er...) for er in errors] - (length(errors) > 1) && return CompositeManifoldError(cerr) - (length(errors) == 1) && return cerr[1] - return nothing -end -function check_vector(M::ProductManifold, p, X; kwargs...) - return DomainError( - typeof(X), - "The vector $X is not a tangent vector to any tangent space on $M, since currently only ArrayPartition is a supported type for tangent vectors on arbitrary product manifolds", - ) -end - -function copyto!(M::ProductManifold, q::ArrayPartition, p::ArrayPartition) - map(copyto!, M.manifolds, submanifold_components(q), submanifold_components(p)) - return q -end -function copyto!( - M::ProductManifold, - Y::ArrayPartition, - p::ArrayPartition, - X::ArrayPartition, -) - map( - copyto!, - M.manifolds, - submanifold_components(Y), - submanifold_components(p), - submanifold_components(X), - ) - return Y -end - @doc raw""" cross(M, N) cross(M1, M2, M3,...) @@ -362,759 +97,102 @@ function LinearAlgebra.cross(M1::ProductManifold, M2::ProductManifold) return ProductManifold(M1.manifolds..., M2.manifolds...) end -function default_retraction_method(M::ProductManifold) - return ProductRetraction(map(default_retraction_method, M.manifolds)...) -end -function default_retraction_method(M::ProductManifold, ::Type{T}) where {T<:ArrayPartition} - return ProductRetraction( - map(default_retraction_method, M.manifolds, T.parameters[2].parameters)..., - ) -end - -function default_inverse_retraction_method(M::ProductManifold) - return InverseProductRetraction(map(default_inverse_retraction_method, M.manifolds)...) -end -function default_inverse_retraction_method( - M::ProductManifold, - ::Type{T}, -) where {T<:ArrayPartition} - return InverseProductRetraction( - map(default_inverse_retraction_method, M.manifolds, T.parameters[2].parameters)..., - ) -end - -function default_vector_transport_method(M::ProductManifold) - return ProductVectorTransport(map(default_vector_transport_method, M.manifolds)...) -end -function default_vector_transport_method( - M::ProductManifold, - ::Type{T}, -) where {T<:ArrayPartition} - return ProductVectorTransport( - map(default_vector_transport_method, M.manifolds, T.parameters[2].parameters)..., - ) -end - @doc raw""" - distance(M::ProductManifold, p, q) + flat(M::ProductManifold, p, X::FVector{TangentSpaceType}) -Compute the distance between two points `p` and `q` on the [`ProductManifold`](@ref) `M`, which is -the 2-norm of the elementwise distances on the internal manifolds that build `M`. +use the musical isomorphism to transform the tangent vector `X` from the tangent space at +`p` on the [`ProductManifold`](@ref) `M` to a cotangent vector. +This can be done elementwise for every entry of `X` (with respect to the corresponding +entry in `p`) separately. """ -function distance(M::ProductManifold, p, q) - return sqrt( - sum( - map( - distance, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - ) .^ 2, - ), - ) -end - -@doc raw""" - exp(M::ProductManifold, p, X) +flat(::ProductManifold, ::Any...) -compute the exponential map from `p` in the direction of `X` on the [`ProductManifold`](@ref) `M`, -which is the elementwise exponential map on the internal manifolds that build `M`. -""" -exp(::ProductManifold, ::Any...) -function Base.exp(M::ProductManifold, p::ArrayPartition, X::ArrayPartition) - return ArrayPartition( - map( - exp, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - )..., - ) -end -function Base.exp(M::ProductManifold, p::ArrayPartition, X::ArrayPartition, t::Number) +function jacobi_field( + M::ProductManifold, + p::ArrayPartition, + q::ArrayPartition, + t, + X::ArrayPartition, + β::Tβ, +) where {Tβ} return ArrayPartition( map( - (N, pc, Xc) -> exp(N, pc, Xc, t), + jacobi_field, M.manifolds, submanifold_components(M, p), + submanifold_components(M, q), + ntuple(_ -> t, length(M.manifolds)), submanifold_components(M, X), + ntuple(_ -> β, length(M.manifolds)), )..., ) end - -function exp!(M::ProductManifold, q, p, X) +function jacobi_field!(M::ProductManifold, Y, p, q, t, X, β::Tβ) where {Tβ} map( - exp!, + jacobi_field!, M.manifolds, - submanifold_components(M, q), + submanifold_components(M, Y), submanifold_components(M, p), - submanifold_components(M, X), - ) - return q -end -function exp!(M::ProductManifold, q, p, X, t::Number) - map( - (N, qc, pc, Xc) -> exp!(N, qc, pc, Xc, t), - M.manifolds, submanifold_components(M, q), - submanifold_components(M, p), + ntuple(_ -> t, length(M.manifolds)), submanifold_components(M, X), + ntuple(_ -> β, length(M.manifolds)), ) - return q -end - -@doc raw""" - flat(M::ProductManifold, p, X::FVector{TangentSpaceType}) - -use the musical isomorphism to transform the tangent vector `X` from the tangent space at -`p` on the [`ProductManifold`](@ref) `M` to a cotangent vector. -This can be done elementwise for every entry of `X` (with respect to the corresponding -entry in `p`) separately. -""" -flat(::ProductManifold, ::Any...) - -function get_basis(M::ProductManifold, p, B::AbstractBasis) - parts = map(t -> get_basis(t..., B), ziptuples(M.manifolds, submanifold_components(p))) - return CachedBasis(B, ProductBasisData(parts)) -end -function get_basis(M::ProductManifold, p, B::CachedBasis) - return invoke(get_basis, Tuple{AbstractManifold,Any,CachedBasis}, M, p, B) -end -function get_basis(M::ProductManifold, p, B::DiagonalizingOrthonormalBasis) - vs = map( - ziptuples( - M.manifolds, - submanifold_components(p), - submanifold_components(B.frame_direction), - ), - ) do t - return get_basis(t[1], t[2], DiagonalizingOrthonormalBasis(t[3])) - end - return CachedBasis(B, ProductBasisData(vs)) + return Y end """ - get_component(M::ProductManifold, p, i) + manifold_volume(M::ProductManifold) -Get the `i`th component of a point `p` on a [`ProductManifold`](@ref) `M`. +Return the volume of [`ProductManifold`](@ref) `M`, i.e. product of volumes of the +manifolds `M` is constructed from. """ -function get_component(M::ProductManifold, p, i) - return submanifold_component(M, p, i) -end +manifold_volume(M::ProductManifold) = mapreduce(manifold_volume, *, M.manifolds) -function get_coordinates(M::ProductManifold, p, X, B::AbstractBasis) - reps = map( - t -> get_coordinates(t..., B), - ziptuples(M.manifolds, submanifold_components(M, p), submanifold_components(M, X)), - ) - return vcat(reps...) -end -function get_coordinates( - M::ProductManifold, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:ProductBasisData}, -) where {𝔽} - reps = map( - get_coordinates, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - B.data.parts, +function ProductFVectorDistribution( + type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, + p::Union{AbstractArray,AbstractManifoldPoint}, + distributions::FVectorDistribution..., +) + return ProductFVectorDistribution{typeof(type),typeof(distributions),typeof(p)}( + type, + p, + distributions, ) - return vcat(reps...) end - -function get_coordinates!(M::ProductManifold, Xⁱ, p, X, B::AbstractBasis) - dim = manifold_dimension(M) - @assert length(Xⁱ) == dim - i = one(dim) - ts = ziptuples(M.manifolds, submanifold_components(M, p), submanifold_components(M, X)) - for t in ts - SM = first(t) - dim = manifold_dimension(SM) - tXⁱ = @inbounds view(Xⁱ, i:(i + dim - 1)) - get_coordinates!(SM, tXⁱ, Base.tail(t)..., B) - i += dim - end - return Xⁱ +function ProductFVectorDistribution( + type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, + distributions::FVectorDistribution..., +) + p = ArrayPartition(map(d -> support(d).point, distributions)) + return ProductFVectorDistribution(type, p, distributions...) end -function get_coordinates!( - M::ProductManifold, - Xⁱ, - p, - X, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:ProductBasisData}, -) where {𝔽} - dim = manifold_dimension(M) - @assert length(Xⁱ) == dim - i = one(dim) - ts = ziptuples( - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - B.data.parts, - ) - for t in ts - SM = first(t) - dim = manifold_dimension(SM) - tXⁱ = @inbounds view(Xⁱ, i:(i + dim - 1)) - get_coordinates!(SM, tXⁱ, Base.tail(t)...) - i += dim +function ProductFVectorDistribution(distributions::FVectorDistribution...) + M = ProductManifold(map(d -> support(d).space.manifold, distributions)...) + fiber = support(distributions[1]).space.fiber + if !all(d -> support(d).space.fiber == fiber, distributions) + error( + "Not all distributions have support in vector spaces of the same type, which is currently not supported", + ) end - return Xⁱ + # Probably worth considering sum spaces in the future? + x = ArrayPartition(map(d -> support(d).point, distributions)...) + return ProductFVectorDistribution(VectorBundleFibers(fiber, M), x, distributions...) end -function _get_dim_ranges(dims::NTuple{N,Any}) where {N} - dims_acc = accumulate(+, vcat(1, SVector(dims))) - return ntuple(i -> (dims_acc[i]:(dims_acc[i] + dims[i] - 1)), Val(N)) +function ProductPointDistribution(M::ProductManifold, distributions::MPointDistribution...) + return ProductPointDistribution{typeof(M),typeof(distributions)}(M, distributions) +end +function ProductPointDistribution(distributions::MPointDistribution...) + M = ProductManifold(map(d -> support(d).manifold, distributions)...) + return ProductPointDistribution(M, distributions...) end -function get_vector( - M::ProductManifold, - p::ArrayPartition, - Xⁱ, - B::AbstractBasis{𝔽,TangentSpaceType}, -) where {𝔽} - dims = map(manifold_dimension, M.manifolds) - @assert length(Xⁱ) == sum(dims) - dim_ranges = _get_dim_ranges(dims) - tXⁱ = map(dr -> (@inbounds view(Xⁱ, dr)), dim_ranges) - ts = ziptuples(M.manifolds, submanifold_components(M, p), tXⁱ) - return ArrayPartition(map((@inline t -> get_vector(t..., B)), ts)) +function Random.rand(rng::AbstractRNG, d::ProductPointDistribution) + return ArrayPartition(map(d -> rand(rng, d), d.distributions)...) end -function get_vector( - M::ProductManifold, - p::ArrayPartition, - Xⁱ, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:ProductBasisData}, -) where {𝔽} - dims = map(manifold_dimension, M.manifolds) - @assert length(Xⁱ) == sum(dims) - dim_ranges = _get_dim_ranges(dims) - tXⁱ = map(dr -> (@inbounds view(Xⁱ, dr)), dim_ranges) - ts = ziptuples(M.manifolds, submanifold_components(M, p), tXⁱ, B.data.parts) - return ArrayPartition(map((@inline t -> get_vector(t...)), ts)) -end - -function get_vector!(M::ProductManifold, X, p, Xⁱ, B::AbstractBasis) - dims = map(manifold_dimension, M.manifolds) - @assert length(Xⁱ) == sum(dims) - dim_ranges = _get_dim_ranges(dims) - tXⁱ = map(dr -> (@inbounds view(Xⁱ, dr)), dim_ranges) - ts = ziptuples( - M.manifolds, - submanifold_components(M, X), - submanifold_components(M, p), - tXⁱ, - ) - map(ts) do t - return get_vector!(t..., B) - end - return X -end -function get_vector!( - M::ProductManifold, - X, - p, - Xⁱ, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:ProductBasisData}, -) where {𝔽} - dims = map(manifold_dimension, M.manifolds) - @assert length(Xⁱ) == sum(dims) - dim_ranges = _get_dim_ranges(dims) - tXⁱ = map(dr -> (@inbounds view(Xⁱ, dr)), dim_ranges) - ts = ziptuples( - M.manifolds, - submanifold_components(M, X), - submanifold_components(M, p), - tXⁱ, - B.data.parts, - ) - map(ts) do t - return get_vector!(t...) - end - return X -end - -function get_vectors( - M::ProductManifold, - p::ArrayPartition, - B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:ProductBasisData}, -) where {𝔽} - N = number_of_components(M) - xparts = submanifold_components(p) - BVs = map(t -> get_vectors(t...), ziptuples(M.manifolds, xparts, B.data.parts)) - zero_tvs = map(t -> zero_vector(t...), ziptuples(M.manifolds, xparts)) - vs = typeof(ArrayPartition(zero_tvs...))[] - for i in 1:N, k in 1:length(BVs[i]) - push!( - vs, - ArrayPartition(zero_tvs[1:(i - 1)]..., BVs[i][k], zero_tvs[(i + 1):end]...), - ) - end - return vs -end - -""" - getindex(p, M::ProductManifold, i::Union{Integer,Colon,AbstractVector}) - p[M::ProductManifold, i] - -Access the element(s) at index `i` of a point `p` on a [`ProductManifold`](@ref) `M` by -linear indexing. -See also [Array Indexing](https://docs.julialang.org/en/v1/manual/arrays/#man-array-indexing-1) in Julia. -""" -Base.@propagate_inbounds function Base.getindex( - p::ArrayPartition, - M::ProductManifold, - i::Union{Integer,Colon,AbstractVector,Val}, -) - return get_component(M, p, i) -end - -@doc raw""" - injectivity_radius(M::ProductManifold) - injectivity_radius(M::ProductManifold, x) - -Compute the injectivity radius on the [`ProductManifold`](@ref), which is the -minimum of the factor manifolds. -""" -injectivity_radius(::ProductManifold, ::Any...) -function injectivity_radius(M::ProductManifold, p) - return min(map(injectivity_radius, M.manifolds, submanifold_components(M, p))...) -end -function injectivity_radius(M::ProductManifold, p, m::AbstractRetractionMethod) - return min( - map( - (lM, lp) -> injectivity_radius(lM, lp, m), - M.manifolds, - submanifold_components(M, p), - )..., - ) -end -function injectivity_radius(M::ProductManifold, p, m::ProductRetraction) - return min( - map( - (lM, lp, lm) -> injectivity_radius(lM, lp, lm), - M.manifolds, - submanifold_components(M, p), - m.retractions, - )..., - ) -end -injectivity_radius(M::ProductManifold) = min(map(injectivity_radius, M.manifolds)...) -function injectivity_radius(M::ProductManifold, m::AbstractRetractionMethod) - return min(map(manif -> injectivity_radius(manif, m), M.manifolds)...) -end -function injectivity_radius(M::ProductManifold, m::ProductRetraction) - return min(map((lM, lm) -> injectivity_radius(lM, lm), M.manifolds, m.retractions)...) -end - -@doc raw""" - inner(M::ProductManifold, p, X, Y) - -compute the inner product of two tangent vectors `X`, `Y` from the tangent space -at `p` on the [`ProductManifold`](@ref) `M`, which is just the sum of the -internal manifolds that build `M`. -""" -function inner(M::ProductManifold, p, X, Y) - subproducts = map( - inner, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, Y), - ) - return sum(subproducts) -end - -@doc raw""" - inverse_retract(M::ProductManifold, p, q, m::InverseProductRetraction) - -Compute the inverse retraction from `p` with respect to `q` on the [`ProductManifold`](@ref) -`M` using an [`InverseProductRetraction`](@ref), which by default encapsulates a inverse -retraction for each manifold of the product. Then this method is performed elementwise, -so the encapsulated inverse retraction methods have to be available per factor. -""" -inverse_retract(::ProductManifold, ::Any, ::Any, ::Any, ::InverseProductRetraction) - -function inverse_retract( - M::ProductManifold, - p::ArrayPartition, - q::ArrayPartition, - method::InverseProductRetraction, -) - return ArrayPartition( - map( - inverse_retract, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - method.inverse_retractions, - ), - ) -end - -function inverse_retract!(M::ProductManifold, Y, p, q, method::InverseProductRetraction) - map( - inverse_retract!, - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, q), - method.inverse_retractions, - ) - return Y -end - -function _isapprox(M::ProductManifold, p, q; kwargs...) - return all( - t -> isapprox(t...; kwargs...), - ziptuples(M.manifolds, submanifold_components(M, p), submanifold_components(M, q)), - ) -end -function _isapprox(M::ProductManifold, p, X, Y; kwargs...) - return all( - t -> isapprox(t...; kwargs...), - ziptuples( - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, Y), - ), - ) -end - -""" - is_flat(::ProductManifold) - -Return true if and only if all component manifolds of [`ProductManifold`](@ref) `M` are flat. -""" -function is_flat(M::ProductManifold) - return all(is_flat, M.manifolds) -end - -@doc raw""" - log(M::ProductManifold, p, q) - -Compute the logarithmic map from `p` to `q` on the [`ProductManifold`](@ref) `M`, -which can be computed using the logarithmic maps of the manifolds elementwise. -""" -log(::ProductManifold, ::Any...) - -function Base.log(M::ProductManifold, p::ArrayPartition, q::ArrayPartition) - return ArrayPartition( - map( - log, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - )..., - ) -end - -function jacobi_field( - M::ProductManifold, - p::ArrayPartition, - q::ArrayPartition, - t, - X::ArrayPartition, - β::Tβ, -) where {Tβ} - return ArrayPartition( - map( - jacobi_field, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, q), - ntuple(_ -> t, length(M.manifolds)), - submanifold_components(M, X), - ntuple(_ -> β, length(M.manifolds)), - )..., - ) -end -function jacobi_field!(M::ProductManifold, Y, p, q, t, X, β::Tβ) where {Tβ} - map( - jacobi_field!, - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, q), - ntuple(_ -> t, length(M.manifolds)), - submanifold_components(M, X), - ntuple(_ -> β, length(M.manifolds)), - ) - return Y -end - -function log!(M::ProductManifold, X, p, q) - map( - log!, - M.manifolds, - submanifold_components(M, X), - submanifold_components(M, p), - submanifold_components(M, q), - ) - return X -end - -@doc raw""" - manifold_dimension(M::ProductManifold) - -Return the manifold dimension of the [`ProductManifold`](@ref), which is the sum of the -manifold dimensions the product is made of. -""" -manifold_dimension(M::ProductManifold) = mapreduce(manifold_dimension, +, M.manifolds) - -""" - manifold_dimension(M::ProductManifold) - -Return the volume of [`ProductManifold`](@ref) `M`, i.e. product of volumes of the -manifolds `M` is constructed from. -""" -manifold_volume(M::ProductManifold) = mapreduce(manifold_volume, *, M.manifolds) - -function mid_point!(M::ProductManifold, q, p1, p2) - map( - mid_point!, - M.manifolds, - submanifold_components(M, q), - submanifold_components(M, p1), - submanifold_components(M, p2), - ) - return q -end - -@doc raw""" - norm(M::ProductManifold, p, X) - -Compute the norm of `X` from the tangent space of `p` on the [`ProductManifold`](@ref), -i.e. from the element wise norms the 2-norm is computed. -""" -function LinearAlgebra.norm(M::ProductManifold, p, X) - norms_squared = ( - map( - norm, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - ) .^ 2 - ) - return sqrt(sum(norms_squared)) -end - -""" - number_of_components(M::ProductManifold{<:NTuple{N,Any}}) where {N} - -Calculate the number of manifolds multiplied in the given [`ProductManifold`](@ref) `M`. -""" -number_of_components(::ProductManifold{𝔽,<:NTuple{N,Any}}) where {𝔽,N} = N - -function ProductFVectorDistribution( - type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, - p::Union{AbstractArray,AbstractManifoldPoint}, - distributions::FVectorDistribution..., -) - return ProductFVectorDistribution{typeof(type),typeof(distributions),typeof(p)}( - type, - p, - distributions, - ) -end -function ProductFVectorDistribution( - type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, - distributions::FVectorDistribution..., -) - p = ArrayPartition(map(d -> support(d).point, distributions)) - return ProductFVectorDistribution(type, p, distributions...) -end -function ProductFVectorDistribution(distributions::FVectorDistribution...) - M = ProductManifold(map(d -> support(d).space.manifold, distributions)...) - fiber = support(distributions[1]).space.fiber - if !all(d -> support(d).space.fiber == fiber, distributions) - error( - "Not all distributions have support in vector spaces of the same type, which is currently not supported", - ) - end - # Probably worth considering sum spaces in the future? - x = ArrayPartition(map(d -> support(d).point, distributions)...) - return ProductFVectorDistribution(VectorBundleFibers(fiber, M), x, distributions...) -end - -function ProductPointDistribution(M::ProductManifold, distributions::MPointDistribution...) - return ProductPointDistribution{typeof(M),typeof(distributions)}(M, distributions) -end -function ProductPointDistribution(distributions::MPointDistribution...) - M = ProductManifold(map(d -> support(d).manifold, distributions)...) - return ProductPointDistribution(M, distributions...) -end - -function parallel_transport_direction( - M::ProductManifold, - p::ArrayPartition, - X::ArrayPartition, - d::ArrayPartition, -) - return ArrayPartition( - map( - parallel_transport_direction, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, d), - ), - ) -end -function parallel_transport_direction!(M::ProductManifold, Y, p, X, d) - map( - parallel_transport_direction!, - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, d), - ) - return Y -end - -function parallel_transport_to( - M::ProductManifold, - p::ArrayPartition, - X::ArrayPartition, - q::ArrayPartition, -) - return ArrayPartition( - map( - parallel_transport_to, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, q), - ), - ) -end -function parallel_transport_to!(M::ProductManifold, Y, p, X, q) - map( - parallel_transport_to!, - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, q), - ) - return Y -end - -function project(M::ProductManifold, p::ArrayPartition) - return ArrayPartition(map(project, M.manifolds, submanifold_components(M, p))...) -end - -function project!(M::ProductManifold, q, p) - map(project!, M.manifolds, submanifold_components(M, q), submanifold_components(M, p)) - return q -end - -function project(M::ProductManifold, p::ArrayPartition, X::ArrayPartition) - return ArrayPartition( - map( - project, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - )..., - ) -end - -function project!(M::ProductManifold, Y, p, X) - map( - project!, - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, X), - ) - return Y -end - -function Random.rand(rng::AbstractRNG, d::ProductPointDistribution) - return ArrayPartition(map(d -> rand(rng, d), d.distributions)...) -end -function Random.rand(rng::AbstractRNG, d::ProductFVectorDistribution) - return ArrayPartition(map(d -> rand(rng, d), d.distributions)...) -end - -@doc raw""" - rand(M::ProductManifold; parts_kwargs = map(_ -> (;), M.manifolds)) - -Return a random point on [`ProductManifold`](@ref) `M`. `parts_kwargs` is -a tuple of keyword arguments for `rand` on each manifold in `M.manifolds`. -""" -function Random.rand( - M::ProductManifold; - vector_at=nothing, - parts_kwargs=map(_ -> (;), M.manifolds), -) - if vector_at === nothing - return ArrayPartition( - map((N, kwargs) -> rand(N; kwargs...), M.manifolds, parts_kwargs)..., - ) - else - return ArrayPartition( - map( - (N, p, kwargs) -> rand(N; vector_at=p, kwargs...), - M.manifolds, - submanifold_components(M, vector_at), - parts_kwargs, - )..., - ) - end -end -function Random.rand( - rng::AbstractRNG, - M::ProductManifold; - vector_at=nothing, - parts_kwargs=map(_ -> (;), M.manifolds), -) - if vector_at === nothing - return ArrayPartition( - map((N, kwargs) -> rand(rng, N; kwargs...), M.manifolds, parts_kwargs)..., - ) - else - return ArrayPartition( - map( - (N, p, kwargs) -> rand(rng, N; vector_at=p, kwargs...), - M.manifolds, - submanifold_components(M, vector_at), - parts_kwargs, - )..., - ) - end -end - -function Random.rand!( - rng::AbstractRNG, - M::ProductManifold, - pX; - vector_at=nothing, - parts_kwargs=map(_ -> (;), M.manifolds), -) - if vector_at === nothing - map( - (N, q, kwargs) -> rand!(rng, N, q; kwargs...), - M.manifolds, - submanifold_components(M, pX), - parts_kwargs, - ) - else - map( - (N, X, p, kwargs) -> rand!(rng, N, X; vector_at=p, kwargs...), - M.manifolds, - submanifold_components(M, pX), - submanifold_components(M, vector_at), - parts_kwargs, - ) - end - return pX +function Random.rand(rng::AbstractRNG, d::ProductFVectorDistribution) + return ArrayPartition(map(d -> rand(rng, d), d.distributions)...) end function Distributions._rand!( @@ -1156,50 +234,6 @@ function Distributions._rand!( return X end -@doc raw""" - retract(M::ProductManifold, p, X, m::ProductRetraction) - -Compute the retraction from `p` with tangent vector `X` on the [`ProductManifold`](@ref) `M` -using an [`ProductRetraction`](@ref), which by default encapsulates retractions of the -base manifolds. Then this method is performed elementwise, so the encapsulated retractions -method has to be one that is available on the manifolds. -""" -retract(::ProductManifold, ::Any...) - -function _retract( - M::ProductManifold, - p::ArrayPartition, - X::ArrayPartition, - t::Number, - method::ProductRetraction, -) - return ArrayPartition( - map( - (N, pc, Xc, rm) -> retract(N, pc, Xc, t, rm), - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - method.retractions, - ), - ) -end - -function _retract!(M::ProductManifold, q, p, X, t::Number, method::ProductRetraction) - map( - (N, qc, pc, Xc, rm) -> retract!(N, qc, pc, Xc, t, rm), - M.manifolds, - submanifold_components(M, q), - submanifold_components(M, p), - submanifold_components(M, X), - method.retractions, - ) - return q -end - -function representation_size(M::ProductManifold) - return (mapreduce(m -> prod(representation_size(m)), +, M.manifolds),) -end - @doc raw""" Y = riemannian_Hessian(M::ProductManifold, p, G, H, X) riemannian_Hessian!(M::ProductManifold, Y, p, G, H, X) @@ -1225,71 +259,6 @@ function riemannian_Hessian!(M::ProductManifold, Y, p, G, H, X) return Y end -@doc raw""" - riemann_tensor(M::ProductManifold, p, X, Y, Z) - -Compute the Riemann tensor at point from `p` with tangent vectors `X`, `Y` and `Z` on -the [`ProductManifold`](@ref) `M`. -""" -riemann_tensor(M::ProductManifold, p, X, Y, X) - -function riemann_tensor( - M::ProductManifold, - p::ArrayPartition, - X::ArrayPartition, - Y::ArrayPartition, - Z::ArrayPartition, -) - return ArrayPartition( - map( - riemann_tensor, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, Y), - submanifold_components(M, Z), - ), - ) -end - -function riemann_tensor!(M::ProductManifold, Xresult, p, X, Y, Z) - map( - riemann_tensor!, - M.manifolds, - submanifold_components(M, Xresult), - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, Y), - submanifold_components(M, Z), - ) - return Xresult -end - -""" - set_component!(M::ProductManifold, q, p, i) - -Set the `i`th component of a point `q` on a [`ProductManifold`](@ref) `M` to `p`, where `p` is a point on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) this factor of the product manifold consists of. -""" -function set_component!(M::ProductManifold, q, p, i) - return copyto!(submanifold_component(M, q, i), p) -end - -""" - setindex!(q, p, M::ProductManifold, i::Union{Integer,Colon,AbstractVector}) - q[M::ProductManifold,i...] = p - -set the element `[i...]` of a point `q` on a [`ProductManifold`](@ref) by linear indexing to `q`. -See also [Array Indexing](https://docs.julialang.org/en/v1/manual/arrays/#man-array-indexing-1) in Julia. -""" -Base.@propagate_inbounds function Base.setindex!( - q::ArrayPartition, - p, - M::ProductManifold, - i::Union{Integer,Colon,AbstractVector,Val}, -) - return set_component!(M, q, p, i) -end - @doc raw""" sharp(M::ProductManifold, p, ξ::FVector{CotangentSpaceType}) @@ -1299,96 +268,6 @@ This can be done elementwise for every entry of `ξ` (and `p`) separately """ sharp(::ProductManifold, ::Any...) -function _show_submanifold(io::IO, M::AbstractManifold; pre="") - sx = sprint(show, "text/plain", M, context=io, sizehint=0) - if occursin('\n', sx) - sx = sprint(show, M, context=io, sizehint=0) - end - sx = replace(sx, '\n' => "\n$(pre)") - print(io, pre, sx) - return nothing -end - -function _show_submanifold_range(io::IO, Ms, range; pre="") - for i in range - M = Ms[i] - print(io, '\n') - _show_submanifold(io, M; pre=pre) - end - return nothing -end - -function _show_product_manifold_no_header(io::IO, M) - n = length(M.manifolds) - sz = displaysize(io) - screen_height, screen_width = sz[1] - 4, sz[2] - half_height = div(screen_height, 2) - inds = 1:n - pre = " " - if n > screen_height - inds = [1:half_height; (n - div(screen_height - 1, 2) + 1):n] - end - if n ≤ screen_height - _show_submanifold_range(io, M.manifolds, 1:n; pre=pre) - else - _show_submanifold_range(io, M.manifolds, 1:half_height; pre=pre) - print(io, "\n$(pre)⋮") - _show_submanifold_range( - io, - M.manifolds, - (n - div(screen_height - 1, 2) + 1):n; - pre=pre, - ) - end - return nothing -end - -function Base.show(io::IO, ::MIME"text/plain", M::ProductManifold) - n = length(M.manifolds) - print(io, "ProductManifold with $(n) submanifold$(n == 1 ? "" : "s"):") - return _show_product_manifold_no_header(io, M) -end - -function Base.show(io::IO, M::ProductManifold) - return print(io, "ProductManifold(", join(M.manifolds, ", "), ")") -end - -function Base.show( - io::IO, - mime::MIME"text/plain", - B::CachedBasis{𝔽,T,D}, -) where {𝔽,T<:AbstractBasis{𝔽},D<:ProductBasisData} - println(io, "$(T) for a product manifold") - for (i, cb) in enumerate(B.data.parts) - println(io, "Basis for component $i:") - show(io, mime, cb) - println(io) - end - return nothing -end - -""" - submanifold(M::ProductManifold, i::Integer) - -Extract the `i`th factor of the product manifold `M`. -""" -submanifold(M::ProductManifold, i::Integer) = M.manifolds[i] - -""" - submanifold(M::ProductManifold, i::Val) - submanifold(M::ProductManifold, i::AbstractVector) - -Extract the factor of the product manifold `M` indicated by indices in `i`. -For example, for `i` equal to `Val((1, 3))` the product manifold constructed -from the first and the third factor is returned. - -The version with `AbstractVector` is not type-stable, for better preformance use `Val`. -""" -function submanifold(M::ProductManifold, i::Val) - return ProductManifold(select_from_tuple(M.manifolds, i)...) -end -submanifold(M::ProductManifold, i::AbstractVector) = submanifold(M, Val(tuple(i...))) - Distributions.support(d::ProductPointDistribution) = MPointSupport(d.manifold) function Distributions.support(tvd::ProductFVectorDistribution) return FVectorSupport( @@ -1407,118 +286,6 @@ function uniform_distribution(M::ProductManifold, p) ) end -function vector_bundle_transport(::FiberType, M::ProductManifold) - return ProductVectorTransport(map(_ -> ParallelTransport(), M.manifolds)) -end - -function vector_transport_direction( - M::ProductManifold, - p::ArrayPartition, - X::ArrayPartition, - d::ArrayPartition, - m::ProductVectorTransport, -) - return ArrayPartition( - map( - vector_transport_direction, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, d), - m.methods, - ), - ) -end -function vector_transport_direction!( - M::ProductManifold, - Y, - p, - X, - d, - m::ProductVectorTransport, -) - map( - vector_transport_direction!, - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, d), - m.methods, - ) - return Y -end - -@doc raw""" - vector_transport_to(M::ProductManifold, p, X, q, m::ProductVectorTransport) - -Compute the vector transport the tangent vector `X`at `p` to `q` on the -[`ProductManifold`](@ref) `M` using an [`ProductVectorTransport`](@ref) `m`. -This method is performed elementwise, i.e. the method `m` has to be implemented on the -base manifold. -""" -vector_transport_to(::ProductManifold, ::Any, ::Any, ::Any, ::ProductVectorTransport) - -function vector_transport_to( - M::ProductManifold, - p::ArrayPartition, - X::ArrayPartition, - q::ArrayPartition, - m::ProductVectorTransport, -) - return ArrayPartition( - map( - vector_transport_to, - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, q), - m.methods, - ), - ) -end -function vector_transport_to( - M::ProductManifold, - p::ArrayPartition, - X::ArrayPartition, - q::ArrayPartition, - m::ParallelTransport, -) - return ArrayPartition( - map( - (iM, ip, iX, id) -> vector_transport_to(iM, ip, iX, id, m), - M.manifolds, - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, q), - ), - ) -end - -function vector_transport_to!(M::ProductManifold, Y, p, X, q, m::ProductVectorTransport) - map( - vector_transport_to!, - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, q), - m.methods, - ) - return Y -end -function vector_transport_to!(M::ProductManifold, Y, p, X, q, m::ParallelTransport) - map( - (iM, iY, ip, iX, id) -> vector_transport_to!(iM, iY, ip, iX, id, m), - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, q), - ), - return Y -end - @doc raw""" volume_density(M::ProductManifold, p, X) @@ -1534,34 +301,3 @@ function volume_density(M::ProductManifold, p, X) ) return prod(dens) end - -@doc raw""" - Y = Weingarten(M::ProductManifold, p, X, V) - Weingarten!(M::ProductManifold, Y, p, X, V) - -Since the metric decouples, also the computation of the Weingarten map -``\mathcal W_p`` can be computed elementwise on the single elements of the [`ProductManifold`](@ref) `M`. -""" -Weingarten(::ProductManifold, p, X, V) - -function Weingarten!(M::ProductManifold, Y, p, X, V) - map( - Weingarten!, - M.manifolds, - submanifold_components(M, Y), - submanifold_components(M, p), - submanifold_components(M, X), - submanifold_components(M, V), - ) - return Y -end - -function zero_vector!(M::ProductManifold, X, p) - map( - zero_vector!, - M.manifolds, - submanifold_components(M, X), - submanifold_components(M, p), - ) - return X -end diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 2c6be0e4ba..1c52a4cbbc 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -1,86 +1,4 @@ -@doc raw""" - struct SasakiRetraction <: AbstractRetractionMethod end - -Exponential map on [`TangentBundle`](@ref) computed via Euler integration as described -in [MuralidharanFlecther:2012](@cite). The system of equations for $\gamma : ℝ \to T\mathcal M$ such that -$\gamma(1) = \exp_{p,X}(X_M, X_F)$ and $\gamma(0)=(p, X)$ reads - -```math -\dot{\gamma}(t) = (\dot{p}(t), \dot{X}(t)) = (R(X(t), \dot{X}(t))\dot{p}(t), 0) -``` - -where $R$ is the Riemann curvature tensor (see [`riemann_tensor`](@ref)). - -# Constructor - - SasakiRetraction(L::Int) - -In this constructor `L` is the number of integration steps. -""" -struct SasakiRetraction <: AbstractRetractionMethod - L::Int -end - -""" - const VectorBundleVectorTransport = FiberBundleProductVectorTransport - -Deprecated: an alias for `FiberBundleProductVectorTransport`. -""" -const VectorBundleVectorTransport = FiberBundleProductVectorTransport - -""" - VectorBundle{𝔽,TVS,TM,TVT} - -Alias for [`FiberBundle`](@ref) when fiber type is a `TVS` of type -[`VectorSpaceType`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases.html#ManifoldsBase.VectorSpaceType). - -`VectorSpaceFiberType` is used to encode vector spaces as fiber types. -""" -const VectorBundle{𝔽,TVS,TM,TVT} = FiberBundle{ - 𝔽, - VectorSpaceFiberType{TVS}, - TM, - TVT, -} where { - TVS<:VectorSpaceType, - TM<:AbstractManifold{𝔽}, - TVT<:FiberBundleProductVectorTransport, -} - -function VectorBundle( - vst::VectorSpaceType, - M::AbstractManifold, - vtm::FiberBundleProductVectorTransport, -) - return FiberBundle(VectorSpaceFiberType(vst), M, vtm) -end -function VectorBundle(vst::VectorSpaceType, M::AbstractManifold) - return FiberBundle(VectorSpaceFiberType(vst), M) -end - -""" - TangentBundle{𝔽,M} = VectorBundle{𝔽,TangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} - -Tangent bundle for manifold of type `M`, as a manifold with the Sasaki metric [Sasaki:1958](@cite). - -Exact retraction and inverse retraction can be approximated using [`FiberBundleProductRetraction`](@ref), -[`FiberBundleInverseProductRetraction`](@ref) and [`SasakiRetraction`](@ref). -[`FiberBundleProductVectorTransport`](@ref) can be used as a vector transport. - -# Constructors - - TangentBundle(M::AbstractManifold) - TangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) -""" -const TangentBundle{𝔽,M} = - VectorBundle{𝔽,TangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} - -TangentBundle(M::AbstractManifold) = VectorBundle(TangentSpace, M) -function TangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) - return VectorBundle(TangentSpace, M, vtm) -end - const CotangentBundle{𝔽,M} = VectorBundle{𝔽,CotangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} @@ -89,332 +7,22 @@ function CotangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTrans return VectorBundle(CotangentSpace, M, vtm) end -function default_inverse_retraction_method(::TangentBundle) - return FiberBundleInverseProductRetraction() -end - -function default_retraction_method(::TangentBundle) - return FiberBundleProductRetraction() -end - -function default_vector_transport_method(B::TangentBundle) - default_vt_m = default_vector_transport_method(B.manifold) - return FiberBundleProductVectorTransport(default_vt_m, default_vt_m) -end - """ - distance(B::BundleFibers, p, X, Y) - -Distance between vectors `X` and `Y` from the vector space at point `p` -from the manifold `B.manifold`, that is the base manifold of `M`. -""" -distance(B::VectorBundleFibers, p, X, Y) = norm(B, p, X - Y) - -function get_basis(M::TangentBundleFibers, p, B::AbstractBasis{<:Any,TangentSpaceType}) - return get_basis(M.manifold, p, B) -end - -function get_coordinates(M::TangentBundleFibers, p, X, B::AbstractBasis) - return get_coordinates(M.manifold, p, X, B) -end - -function get_coordinates!(M::TangentBundleFibers, Y, p, X, B::AbstractBasis) - return get_coordinates!(M.manifold, Y, p, X, B) -end - -function get_vector(M::TangentBundleFibers, p, X, B::AbstractBasis) - return get_vector(M.manifold, p, X, B) -end - -function get_vector!(M::TangentBundleFibers, Y, p, X, B::AbstractBasis) - return get_vector!(M.manifold, Y, p, X, B) -end - -""" - injectivity_radius(M::TangentBundle) - -Injectivity radius of [`TangentBundle`](@ref) manifold is infinite if the base manifold -is flat and 0 otherwise. -See [https://mathoverflow.net/questions/94322/injectivity-radius-of-the-sasaki-metric](https://mathoverflow.net/questions/94322/injectivity-radius-of-the-sasaki-metric). -""" -function injectivity_radius(M::TangentBundle) - if is_flat(M.manifold) - return Inf - else - return 0.0 - end -end - -""" - inner(B::BundleFibers, p, X, Y) - -Inner product of vectors `X` and `Y` from the vector space of type `B.fiber` -at point `p` from manifold `B.manifold`. -""" -inner(B::BundleFibers, p, X, Y) - -inner(B::TangentBundleFibers, p, X, Y) = inner(B.manifold, p, X, Y) -function inner(B::CotangentBundleFibers, p, X, Y) - return inner(B.manifold, p, sharp(B.manifold, p, X), sharp(B.manifold, p, Y)) -end -@doc raw""" - inner(B::VectorBundle, p, X, Y) - -Inner product of tangent vectors `X` and `Y` at point `p` from the -vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). - -Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - * The tangent vector $v = (V_{X,M}, V_{X,F}) ∈ T_{x}B$ where - $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}\mathcal M$ and - $V_{X,F}$ is a tangent vector from the tangent space $T_{V_p}F$ (isomorphic to $F$). - Similarly for the other tangent vector $w = (V_{Y,M}, V_{Y,F}) ∈ T_{x}B$. - -The inner product is calculated as - -$⟨X, Y⟩_p = ⟨V_{X,M}, V_{Y,M}⟩_{x_p} + ⟨V_{X,F}, V_{Y,F}⟩_{V_p}.$ -""" -function inner(B::FiberBundle, p, X, Y) - px, Vx = submanifold_components(B.manifold, p) - VXM, VXF = submanifold_components(B.manifold, X) - VYM, VYF = submanifold_components(B.manifold, Y) - # for tangent bundle Vx is discarded by the method of inner for TangentSpaceAtPoint - # and px is actually used as the base point - return inner(B.manifold, px, VXM, VYM) + inner(FiberAtPoint(B.fiber, px), Vx, VXF, VYF) -end - -function _inverse_retract(M::FiberBundle, p, q, ::FiberBundleInverseProductRetraction) - return inverse_retract_product(M, p, q) -end - -function _inverse_retract!(M::FiberBundle, X, p, q, ::FiberBundleInverseProductRetraction) - return inverse_retract_product!(M, X, p, q) -end - -""" - inverse_retract_product(M::VectorBundle, p, q) - -Compute the allocating variant of the [`FiberBundleInverseProductRetraction`](@ref), -which by default allocates and calls `inverse_retract_product!`. -""" -function inverse_retract_product(M::VectorBundle, p, q) - X = allocate_result(M, inverse_retract, p, q) - return inverse_retract_product!(M, X, p, q) -end - -function inverse_retract_product!(B::VectorBundle, X, p, q) - px, Vx = submanifold_components(B.manifold, p) - py, Vy = submanifold_components(B.manifold, q) - VXM, VXF = submanifold_components(B.manifold, X) - log!(B.manifold, VXM, px, py) - vector_transport_to!(B.fiber, VXF, py, Vy, px, B.vector_transport.method_fiber) - copyto!(VXF, VXF - Vx) - return X -end - -""" - is_flat(::VectorBundle) - -Return true if the underlying manifold of [`VectorBundle`](@ref) `M` is flat. -""" -is_flat(M::VectorBundle) = is_flat(M.manifold) - -""" - norm(B::BundleFibers, p, q) - -Norm of the vector `X` from the vector space of type `B.fiber` -at point `p` from manifold `B.manifold`. -""" -LinearAlgebra.norm(B::VectorBundleFibers, p, X) = sqrt(inner(B, p, X, X)) -LinearAlgebra.norm(B::TangentBundleFibers, p, X) = norm(B.manifold, p, X) - -@doc raw""" - project(B::VectorBundle, p) - -Project the point `p` from the ambient space of the vector bundle `B` -over manifold `B.fiber` (denoted $\mathcal M$) to the vector bundle. - -Notation: - * The point $p = (x_p, V_p)$ where $x_p$ belongs to the ambient space of $\mathcal M$ - and $V_p$ belongs to the ambient space of the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - -The projection is calculated by projecting the point $x_p$ to the manifold $\mathcal M$ -and then projecting the vector $V_p$ to the tangent space $T_{x_p}\mathcal M$. -""" -project(::VectorBundle, ::Any) - -function project!(B::VectorBundle, q, p) - px, pVx = submanifold_components(B.manifold, p) - qx, qVx = submanifold_components(B.manifold, q) - project!(B.manifold, qx, px) - project!(B.manifold, qVx, qx, pVx) - return q -end - -@doc raw""" - project(B::VectorBundle, p, X) - -Project the element `X` of the ambient space of the tangent space $T_p B$ -to the tangent space $T_p B$. - -Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - * The vector $x = (V_{X,M}, V_{X,F})$ where $x_p$ belongs to the ambient space of $T_{x_p}\mathcal M$ - and $V_{X,F}$ belongs to the ambient space of the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - -The projection is calculated by projecting $V_{X,M}$ to tangent space $T_{x_p}\mathcal M$ -and then projecting the vector $V_{X,F}$ to the fiber $F$. -""" -project(::VectorBundle, ::Any, ::Any) - -function project!(B::VectorBundle, Y, p, X) - px, Vx = submanifold_components(B.manifold, p) - VXM, VXF = submanifold_components(B.manifold, X) - VYM, VYF = submanifold_components(B.manifold, Y) - project!(B.manifold, VYM, px, VXM) - project!(B.manifold, VYF, px, VXF) - return Y -end - -""" - project(B::BundleFibers, p, X) - -Project vector `X` from the vector space of type `B.fiber` at point `p`. -""" -function project(B::BundleFibers, p, X) - Y = allocate_result(B, project, p, X) - return project!(B, Y, p, X) -end - -function project!(B::TangentBundleFibers, Y, p, X) - return project!(B.manifold, Y, p, X) -end - -function _retract(M::VectorBundle, p, X, t::Number, ::FiberBundleProductRetraction) - return retract_product(M, p, X, t) -end - -function _retract!(M::VectorBundle, q, p, X, t::Number, ::FiberBundleProductRetraction) - return retract_product!(M, q, p, X, t) -end - -""" - retract_product(M::VectorBundle, p, q, t::Number) - -Compute the allocating variant of the [`FiberBundleProductRetraction`](@ref), -which by default allocates and calls `retract_product!`. -""" -function retract_product(M::VectorBundle, p, X, t::Number) - q = allocate_result(M, retract, p, X) - return retract_product!(M, q, p, X, t) -end - -function retract_product!(B::VectorBundle, q, p, X, t::Number) - tX = t * X - xp, Xp = submanifold_components(B.manifold, p) - xq, Xq = submanifold_components(B.manifold, q) - VXM, VXF = submanifold_components(B.manifold, tX) - # this temporary avoids overwriting `p` when `q` and `p` occupy the same memory - xqt = exp(B.manifold, xp, VXM) - vector_transport_direction!( - B.manifold, - Xq, - xp, - Xp + VXF, - VXM, - B.vector_transport.method_point, - ) - copyto!(B.manifold, xq, xqt) - return q -end - -function _retract(M::AbstractManifold, p, X, t::Number, m::SasakiRetraction) - return retract_sasaki(M, p, X, t, m) -end - -function _retract!(M::AbstractManifold, q, p, X, t::Number, m::SasakiRetraction) - return retract_sasaki!(M, q, p, X, t, m) -end - -""" - retract_sasaki(M::AbstractManifold, p, X, t::Number, m::SasakiRetraction) + const VectorBundleVectorTransport = FiberBundleProductVectorTransport -Compute the allocating variant of the [`SasakiRetraction`](@ref), -which by default allocates and calls `retract_sasaki!`. +Deprecated: an alias for `FiberBundleProductVectorTransport`. """ -function retract_sasaki(M::AbstractManifold, p, X, t::Number, m::SasakiRetraction) - q = allocate_result(M, retract, p, X) - return retract_sasaki!(M, q, p, X, t, m) -end +const VectorBundleVectorTransport = FiberBundleProductVectorTransport -function retract_sasaki!(B::TangentBundle, q, p, X, t::Number, m::SasakiRetraction) - tX = t * X - xp, Xp = submanifold_components(B.manifold, p) - xq, Xq = submanifold_components(B.manifold, q) - VXM, VXF = submanifold_components(B.manifold, tX) - p_k = allocate(B.manifold, xp) - copyto!(B.manifold, p_k, xp) - X_k = allocate(B.manifold, Xp) - copyto!(B.manifold, X_k, Xp) - Y_k = allocate(B.manifold, VXM) - copyto!(B.manifold, Y_k, VXM) - Z_k = allocate(B.manifold, VXF) - copyto!(B.manifold, Z_k, VXF) - ϵ = 1 / m.L - for k in 1:(m.L) - p_kp1 = exp(B.manifold, p_k, ϵ * Y_k) - X_kp1 = parallel_transport_to(B.manifold, p_k, X_k .+ ϵ .* Z_k, p_kp1) - Y_kp1 = parallel_transport_to( - B.manifold, - p_k, - Y_k .+ ϵ .* riemann_tensor(B.manifold, p_k, X_k, Z_k, Y_k), - p_kp1, - ) - Z_kp1 = parallel_transport_to(B.manifold, p_k, Z_k, p_kp1) - copyto!(B.manifold, p_k, p_kp1) - copyto!(B.manifold, X_k, p_kp1, X_kp1) - copyto!(B.manifold, Y_k, p_kp1, Y_kp1) - copyto!(B.manifold, Z_k, p_kp1, Z_kp1) - end - copyto!(B.manifold, xq, p_k) - copyto!(B.manifold, Xq, xq, X_k) - return q -end +const CotangentBundle{𝔽,M} = + VectorBundle{𝔽,CotangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} function representation_size(B::CotangentBundleFibers) return representation_size(B.manifold) end -function representation_size(B::TangentBundleFibers) - return representation_size(B.manifold) -end -function Base.show(io::IO, tpt::TensorProductType) - return print(io, "TensorProductType(", join(tpt.spaces, ", "), ")") -end -function Base.show(io::IO, vb::VectorBundle) - return print(io, "VectorBundle($(vb.type.fiber), $(vb.manifold))") -end -function Base.show(io::IO, vbf::VectorBundleFibers) - return print(io, "VectorBundleFibers($(vbf.fiber.fiber), $(vbf.manifold))") -end -Base.show(io::IO, vb::TangentBundle) = print(io, "TangentBundle($(vb.manifold))") Base.show(io::IO, vb::CotangentBundle) = print(io, "CotangentBundle($(vb.manifold))") -@inline function allocate_result(B::TangentBundleFibers, f::typeof(zero_vector), x...) - return allocate_result(B.manifold, f, x...) -end -@inline function allocate_result(M::VectorBundle, f::TF) where {TF} - return ArrayPartition(allocate_result(M.manifold, f), allocate_result(M.fiber, f)) -end - """ fiber_bundle_transport(fiber::FiberType, M::AbstractManifold) @@ -423,161 +31,3 @@ Determine the vector tranport used for [`exp`](@ref exp(::FiberBundle, ::Any...) `fiber` and manifold `M`. """ fiber_bundle_transport(::VectorSpaceType, ::AbstractManifold) = ParallelTransport() - -function vector_space_dimension(M::AbstractManifold, V::TensorProductType) - dim = 1 - for space in V.spaces - dim *= fiber_dimension(M, space) - end - return dim -end - -function vector_space_dimension(B::TangentBundleFibers) - return manifold_dimension(B.manifold) -end -function vector_space_dimension(B::CotangentBundleFibers) - return manifold_dimension(B.manifold) -end -function vector_space_dimension(B::VectorBundleFibers) - return vector_space_dimension(B.manifold, B.fiber.fiber) -end - -function vector_transport_direction(M::VectorBundle, p, X, d) - return vector_transport_direction(M, p, X, d, M.vector_transport) -end -function vector_transport_direction( - M::TangentBundleFibers, - p, - X, - d, - m::AbstractVectorTransportMethod, -) - return vector_transport_direction(M.manifold, p, X, d, m) -end - -function _vector_transport_direction( - M::VectorBundle, - p, - X, - d, - m::FiberBundleProductVectorTransport, -) - px, pVx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - dx, dVx = submanifold_components(M.manifold, d) - return ArrayPartition( - vector_transport_direction(M.manifold, px, VXM, dx, m.method_point), - vector_transport_direction(M.fiber, px, VXF, dx, m.method_fiber), - ) -end - -function vector_transport_direction!(M::VectorBundle, Y, p, X, d) - return vector_transport_direction!(M, Y, p, X, d, M.vector_transport) -end - -function _vector_transport_direction!( - M::VectorBundle, - Y, - p, - X, - d, - m::FiberBundleProductVectorTransport, -) - VYM, VYF = submanifold_components(M.manifold, Y) - px, pVx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - dx, dVx = submanifold_components(M.manifold, d) - vector_transport_direction!(M.manifold, VYM, px, VXM, dx, m.method_point), - vector_transport_direction!(M.manifold, VYF, px, VXF, dx, m.method_fiber), - return Y -end - -@doc raw""" - vector_transport_to(M::VectorBundle, p, X, q, m::FiberBundleProductVectorTransport) - -Compute the vector transport the tangent vector `X`at `p` to `q` on the -[`VectorBundle`](@ref) `M` using the [`FiberBundleProductVectorTransport`](@ref) `m`. -""" -vector_transport_to( - ::VectorBundle, - ::Any, - ::Any, - ::Any, - ::FiberBundleProductVectorTransport, -) - -function _vector_transport_to( - M::VectorBundle, - p, - X, - q, - m::FiberBundleProductVectorTransport, -) - px, pVx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - qx, qVx = submanifold_components(M.manifold, q) - return ArrayPartition( - vector_transport_to(M.manifold, px, VXM, qx, m.method_point), - vector_transport_to(M.manifold, px, VXF, qx, m.method_fiber), - ) -end - -function vector_transport_to(M::VectorBundle, p, X, q) - return vector_transport_to(M, p, X, q, M.vector_transport) -end - -function vector_transport_to!(M::VectorBundle, Y, p, X, q) - return vector_transport_to!(M, Y, p, X, q, M.vector_transport) -end -function vector_transport_to!( - M::TangentBundle, - Y, - p, - X, - q, - m::FiberBundleProductVectorTransport, -) - px, pVx = submanifold_components(M.manifold, p) - VXM, VXF = submanifold_components(M.manifold, X) - VYM, VYF = submanifold_components(M.manifold, Y) - qx, qVx = submanifold_components(M.manifold, q) - vector_transport_to!(M.manifold, VYM, px, VXM, qx, m.method_point) - vector_transport_to!(M.manifold, VYF, px, VXF, qx, m.method_fiber) - return Y -end -function vector_transport_to!( - M::TangentBundleFibers, - Y, - p, - X, - q, - m::AbstractVectorTransportMethod, -) - vector_transport_to!(M.manifold, Y, p, X, q, m) - return Y -end - -""" - zero_vector!(B::BundleFibers, X, p) - -Save the zero vector from the vector space of type `B.fiber` at point `p` -from manifold `B.manifold` to `X`. -""" -zero_vector!(B::BundleFibers, X, p) - -function zero_vector!(B::TangentBundleFibers, X, p) - return zero_vector!(B.manifold, X, p) -end - -@doc raw""" - Y = Weingarten(M::TangentSpaceAtPoint, p, X, V) - Weingarten!(M::TangentSpaceAtPoint, Y, p, X, V) - -Compute the Weingarten map ``\mathcal W_p`` at `p` on the [`TangentSpaceAtPoint`](@ref) `M` with respect to the -tangent vector ``X \in T_p\mathcal M`` and the normal vector ``V \in N_p\mathcal M``. - -Since this a flat space by itself, the result is always the zero tangent vector. -""" -Weingarten(::TangentSpaceAtPoint, p, X, V) - -Weingarten!(::TangentSpaceAtPoint, Y, p, X, V) = fill!(Y, 0) diff --git a/src/manifolds/VectorFiber.jl b/src/manifolds/VectorFiber.jl index b601d72e0a..cf40944e8f 100644 --- a/src/manifolds/VectorFiber.jl +++ b/src/manifolds/VectorFiber.jl @@ -1,100 +1,12 @@ -""" - TensorProductType(spaces::VectorSpaceType...) - -Vector space type corresponding to the tensor product of given vector space -types. -""" -struct TensorProductType{TS<:Tuple} <: VectorSpaceType - spaces::TS -end - -TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) - -""" - VectorSpaceFiberType{TVS<:VectorSpaceType} - -`FiberType` of a [`FiberBundle`](@ref) corresponding to [`VectorSpaceType`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases.html#ManifoldsBase.VectorSpaceType) -`TVS`. -""" -struct VectorSpaceFiberType{TVS<:VectorSpaceType} <: FiberType - fiber::TVS -end - -function Base.show(io::IO, vsf::VectorSpaceFiberType) - return print(io, "VectorSpaceFiberType($(vsf.fiber))") -end - -const TangentFiberType = VectorSpaceFiberType{TangentSpaceType} - const CotangentFiberType = VectorSpaceFiberType{CotangentSpaceType} -const TangentFiber = VectorSpaceFiberType{TangentSpaceType}(TangentSpace) -const CotangentFiber = VectorSpaceFiberType{CotangentSpaceType}(CotangentSpace) - -""" - VectorBundleFibers{TVS,TM} - -Alias for [`BundleFibers`](@ref) when the fiber is a vector space. -""" -const VectorBundleFibers{TVS,TM} = BundleFibers{ - VectorSpaceFiberType{TVS}, - TM, -} where {TVS<:VectorSpaceType,TM<:AbstractManifold} - -function VectorBundleFibers(fiber::VectorSpaceType, M::AbstractManifold) - return BundleFibers(VectorSpaceFiberType(fiber), M) -end -function VectorBundleFibers(fiber::VectorSpaceFiberType, M::AbstractManifold) - return BundleFibers(fiber, M) -end - -const TangentBundleFibers{M} = BundleFibers{TangentFiberType,M} where {M<:AbstractManifold} - -TangentBundleFibers(M::AbstractManifold) = BundleFibers(TangentFiber, M) - const CotangentBundleFibers{M} = BundleFibers{CotangentFiberType,M} where {M<:AbstractManifold} CotangentBundleFibers(M::AbstractManifold) = BundleFibers(CotangentFiber, M) -""" - VectorSpaceAtPoint{𝔽,M,TFiber} - -Alias for [`FiberAtPoint`](@ref) when the fiber is a vector space. -""" -const VectorSpaceAtPoint{𝔽,M,TSpaceType} = FiberAtPoint{ - 𝔽, - VectorBundleFibers{TSpaceType,M}, -} where {𝔽,M<:AbstractManifold{𝔽},TSpaceType<:VectorSpaceType} - -function VectorSpaceAtPoint(M::AbstractManifold, fiber::VectorSpaceFiberType, p) - return FiberAtPoint(BundleFibers(fiber, M), p) -end - -""" - TangentSpaceAtPoint{𝔽,M} - -Alias for [`VectorSpaceAtPoint`](@ref) for the tangent space at a point. -""" -const TangentSpaceAtPoint{𝔽,M} = - VectorSpaceAtPoint{𝔽,M,TangentSpaceType} where {𝔽,M<:AbstractManifold{𝔽}} - -""" - TangentSpaceAtPoint(M::AbstractManifold, p) - -Return an object of type [`VectorSpaceAtPoint`](@ref) representing tangent -space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. -""" -TangentSpaceAtPoint(M::AbstractManifold, p) = VectorSpaceAtPoint(M, TangentFiber, p) - -""" - TangentSpace(M::AbstractManifold, p) - -Return a [`TangentSpaceAtPoint`] representing tangent -space at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. -""" -TangentSpace(M::AbstractManifold, p) = TangentSpaceAtPoint(M, p) +const CotangentFiber = VectorSpaceFiberType{CotangentSpaceType}(CotangentSpace) const CotangentSpaceAtPoint{𝔽,M} = VectorSpaceAtPoint{𝔽,CotangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} @@ -109,188 +21,28 @@ function CotangentSpaceAtPoint(M::AbstractManifold, p) return VectorSpaceAtPoint(M, CotangentFiber, p) end -function allocate_result(M::TangentSpaceAtPoint, ::typeof(rand)) - return zero_vector(M.fiber.manifold, M.point) -end - -""" - distance(M::TangentSpaceAtPoint, p, q) - -Distance between vectors `p` and `q` from the vector space `M`. It is calculated as the norm -of their difference. -""" -function distance(M::TangentSpaceAtPoint, p, q) - return norm(M.fiber.manifold, M.point, q - p) -end - -function embed!(M::TangentSpaceAtPoint, q, p) - return embed!(M.fiber.manifold, q, M.point, p) -end -function embed!(M::TangentSpaceAtPoint, Y, p, X) - return embed!(M.fiber.manifold, Y, M.point, X) -end - -@doc raw""" - exp(M::TangentSpaceAtPoint, p, X) - -Exponential map of tangent vectors `X` and `p` from the tangent space `M`. It is -calculated as their sum. -""" -exp(::TangentSpaceAtPoint, ::Any, ::Any) - -function exp!(M::TangentSpaceAtPoint, q, p, X) - copyto!(M.fiber.manifold, q, p + X) - return q -end - -fiber_dimension(M::TangentBundleFibers) = manifold_dimension(M.manifold) fiber_dimension(M::CotangentBundleFibers) = manifold_dimension(M.manifold) -fiber_dimension(M::AbstractManifold, V::VectorSpaceFiberType) = fiber_dimension(M, V.fiber) -fiber_dimension(M::AbstractManifold, ::CotangentSpaceType) = manifold_dimension(M) -fiber_dimension(M::AbstractManifold, ::TangentSpaceType) = manifold_dimension(M) - -function get_basis(M::TangentSpaceAtPoint, p, B::CachedBasis) - return invoke( - get_basis, - Tuple{AbstractManifold,Any,CachedBasis}, - M.fiber.manifold, - M.point, - B, - ) -end -function get_basis(M::TangentSpaceAtPoint, p, B::AbstractBasis{<:Any,TangentSpaceType}) - return get_basis(M.fiber.manifold, M.point, B) -end - -function get_coordinates(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) - return get_coordinates(M.fiber.manifold, M.point, X, B) -end - -function get_coordinates!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) - return get_coordinates!(M.fiber.manifold, Y, M.point, X, B) -end - -function get_vector(M::TangentSpaceAtPoint, p, X, B::AbstractBasis) - return get_vector(M.fiber.manifold, M.point, X, B) -end - -function get_vector!(M::TangentSpaceAtPoint, Y, p, X, B::AbstractBasis) - return get_vector!(M.fiber.manifold, Y, M.point, X, B) -end - -function get_vectors(M::TangentSpaceAtPoint, p, B::CachedBasis) - return get_vectors(M.fiber.manifold, M.point, B) -end - -@doc raw""" - injectivity_radius(M::TangentSpaceAtPoint) - -Return the injectivity radius on the [`TangentSpaceAtPoint`](@ref) `M`, which is $∞$. -""" -injectivity_radius(::TangentSpaceAtPoint) = Inf - -""" - inner(M::TangentSpaceAtPoint, p, X, Y) - -Inner product of vectors `X` and `Y` from the tangent space at `M`. -""" -function inner(M::TangentSpaceAtPoint, p, X, Y) - return inner(M.fiber.manifold, M.point, X, Y) -end - -""" - is_flat(::TangentSpaceAtPoint) - -Return true. [`TangentSpaceAtPoint`](@ref) is a flat manifold. -""" -is_flat(::TangentSpaceAtPoint) = true - -function _isapprox(M::TangentSpaceAtPoint, X, Y; kwargs...) - return isapprox(M.fiber.manifold, M.point, X, Y; kwargs...) -end - -""" - log(M::TangentSpaceAtPoint, p, q) -Logarithmic map on the tangent space manifold `M`, calculated as the difference of tangent -vectors `q` and `p` from `M`. """ -log(::TangentSpaceAtPoint, ::Any...) -function log!(::TangentSpaceAtPoint, X, p, q) - copyto!(X, q - p) - return X -end - -function manifold_dimension(M::FiberAtPoint) - return fiber_dimension(M.fiber) -end - -LinearAlgebra.norm(M::VectorSpaceAtPoint, p, X) = norm(M.fiber.manifold, M.point, X) - -function parallel_transport_to!(M::TangentSpaceAtPoint, Y, p, X, q) - return copyto!(M.fiber.manifold, Y, p, X) -end - -@doc raw""" - project(M::TangentSpaceAtPoint, p) - -Project the point `p` from the tangent space `M`, that is project the vector `p` -tangent at `M.point`. -""" -project(::TangentSpaceAtPoint, ::Any) - -function project!(M::TangentSpaceAtPoint, q, p) - return project!(M.fiber.manifold, q, M.point, p) -end - -@doc raw""" - project(M::TangentSpaceAtPoint, p, X) + TensorProductType(spaces::VectorSpaceType...) -Project the vector `X` from the tangent space `M`, that is project the vector `X` -tangent at `M.point`. +Vector space type corresponding to the tensor product of given vector space +types. """ -project(::TangentSpaceAtPoint, ::Any, ::Any) - -function project!(M::TangentSpaceAtPoint, Y, p, X) - return project!(M.fiber.manifold, Y, M.point, X) -end - -function Random.rand!(rng::AbstractRNG, M::TangentSpaceAtPoint, X; vector_at=nothing) - rand!(rng, M.fiber.manifold, X; vector_at=M.point) - return X -end - -function representation_size(B::TangentSpaceAtPoint) - return representation_size(B.fiber.manifold) +struct TensorProductType{TS<:Tuple} <: VectorSpaceType + spaces::TS end -function Base.show(io::IO, ::MIME"text/plain", TpM::TangentSpaceAtPoint) - println(io, "Tangent space to the manifold $(base_manifold(TpM)) at point:") - pre = " " - sp = sprint(show, "text/plain", TpM.point; context=io, sizehint=0) - sp = replace(sp, '\n' => "\n$(pre)") - return print(io, pre, sp) -end +TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) -function vector_transport_to!( - M::TangentSpaceAtPoint, - Y, - p, - X, - q, - m::AbstractVectorTransportMethod, -) - return copyto!(M.fiber.manifold, Y, p, X) +function Base.show(io::IO, tpt::TensorProductType) + return print(io, "TensorProductType(", join(tpt.spaces, ", "), ")") end -@doc raw""" - zero_vector(M::TangentSpaceAtPoint, p) - -Zero tangent vector at point `p` from the tangent space `M`, that is the zero tangent vector -at point `M.point`. -""" -zero_vector(::TangentSpaceAtPoint, ::Any...) - -function zero_vector!(M::VectorSpaceAtPoint, X, p) - return zero_vector!(M.fiber.manifold, X, M.point) +function vector_space_dimension(M::AbstractManifold, V::TensorProductType) + dim = 1 + for space in V.spaces + dim *= fiber_dimension(M, space) + end + return dim end diff --git a/src/product_representations.jl b/src/product_representations.jl index 669d4b1066..8b13789179 100644 --- a/src/product_representations.jl +++ b/src/product_representations.jl @@ -1,36 +1 @@ -@doc raw""" - submanifold_component(M::AbstractManifold, p, i::Integer) - submanifold_component(M::AbstractManifold, p, ::Val{i}) where {i} - submanifold_component(p, i::Integer) - submanifold_component(p, ::Val{i}) where {i} - -Project the product array `p` on `M` to its `i`th component. A new array is returned. -""" -submanifold_component(::Any...) -@inline function submanifold_component(M::AbstractManifold, p, i::Integer) - return submanifold_component(M, p, Val(i)) -end -@inline submanifold_component(M::AbstractManifold, p, i::Val) = submanifold_component(p, i) -@inline submanifold_component(p::ArrayPartition, ::Val{I}) where {I} = p.x[I] -@inline submanifold_component(p, i::Integer) = submanifold_component(p, Val(i)) - -@doc raw""" - submanifold_components(M::AbstractManifold, p) - submanifold_components(p) - -Get the projected components of `p` on the submanifolds of `M`. The components are returned in a Tuple. -""" -submanifold_components(::Any...) -@inline submanifold_components(::AbstractManifold, p) = submanifold_components(p) -@inline submanifold_components(p::ArrayPartition) = p.x - -## ArrayPartition - -ManifoldsBase._get_vector_cache_broadcast(::ArrayPartition) = Val(false) - -allocate(a::AbstractArray{<:ArrayPartition}) = map(allocate, a) -allocate(x::ArrayPartition) = ArrayPartition(map(allocate, x.x)...) -function allocate(x::ArrayPartition, T::Type) - return ArrayPartition(map(t -> allocate(t, T), submanifold_components(x))...) -end diff --git a/src/utils.jl b/src/utils.jl index d934ce8c9e..d12937d426 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -207,20 +207,6 @@ unrealify!(Y, X, ::typeof(ℝ), args...) = copyto!(Y, X) @generated maybesize(s::Size{S}) where {S} = prod(S) > 100 ? S : :(s) -""" - select_from_tuple(t::NTuple{N, Any}, positions::Val{P}) - -Selects elements of tuple `t` at positions specified by the second argument. -For example `select_from_tuple(("a", "b", "c"), Val((3, 1, 1)))` returns -`("c", "a", "a")`. -""" -@generated function select_from_tuple(t::NTuple{N,Any}, positions::Val{P}) where {N,P} - for k in P - (k < 0 || k > N) && error("positions must be between 1 and $N") - end - return Expr(:tuple, [Expr(:ref, :t, k) for k in P]...) -end - @doc raw""" symmetrize!(Y, X) @@ -286,56 +272,6 @@ function isnormal(x; kwargs...) end isnormal(::LinearAlgebra.RealHermSymComplexHerm; kwargs...) = true -""" - ziptuples(a, b[, c[, d[, e]]]) - -Zips tuples `a`, `b`, and remaining in a fast, type-stable way. If they have different -lengths, the result is trimmed to the length of the shorter tuple. -""" -@generated function ziptuples(a::NTuple{N,Any}, b::NTuple{M,Any}) where {N,M} - ex = Expr(:tuple) - for i in 1:min(N, M) - push!(ex.args, :((a[$i], b[$i]))) - end - return ex -end -@generated function ziptuples( - a::NTuple{N,Any}, - b::NTuple{M,Any}, - c::NTuple{L,Any}, -) where {N,M,L} - ex = Expr(:tuple) - for i in 1:min(N, M, L) - push!(ex.args, :((a[$i], b[$i], c[$i]))) - end - return ex -end -@generated function ziptuples( - a::NTuple{N,Any}, - b::NTuple{M,Any}, - c::NTuple{L,Any}, - d::NTuple{K,Any}, -) where {N,M,L,K} - ex = Expr(:tuple) - for i in 1:min(N, M, L, K) - push!(ex.args, :((a[$i], b[$i], c[$i], d[$i]))) - end - return ex -end -@generated function ziptuples( - a::NTuple{N,Any}, - b::NTuple{M,Any}, - c::NTuple{L,Any}, - d::NTuple{K,Any}, - e::NTuple{J,Any}, -) where {N,M,L,K,J} - ex = Expr(:tuple) - for i in 1:min(N, M, L, K, J) - push!(ex.args, :((a[$i], b[$i], c[$i], d[$i], e[$i]))) - end - return ex -end - _eps_safe(::Type{T}) where {T<:Integer} = zero(T) _eps_safe(::Type{T}) where {T<:Real} = eps(T) _eps_safe(::Type{T}) where {T<:Number} = eps(real(T)) From 7632169dd290d75666f1b3171e9cd273a9430494 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 10 Oct 2023 22:38:15 +0200 Subject: [PATCH 37/81] some updates --- benchmark/benchmarks.jl | 12 +- docs/src/manifolds/vector_bundle.md | 8 +- docs/src/misc/notation.md | 2 +- ext/ManifoldsTestExt/tests_general.jl | 15 +- src/Manifolds.jl | 37 +-- src/atlases.jl | 2 +- src/distributions.jl | 11 +- src/manifolds/Fiber.jl | 70 +++++ src/manifolds/FiberBundle.jl | 419 ++++++++++++++++++++++++++ src/manifolds/PowerManifold.jl | 12 +- src/manifolds/ProductManifold.jl | 39 +-- src/manifolds/VectorBundle.jl | 396 +++++++++++++++++++++++- src/manifolds/VectorFiber.jl | 17 +- src/projected_distribution.jl | 28 +- test/differentiation.jl | 4 +- test/manifolds/fiber.jl | 14 +- test/manifolds/power_manifold.jl | 18 +- test/manifolds/product_manifold.jl | 4 +- test/manifolds/vector_bundle.jl | 31 +- test/metric.jl | 4 +- test/notation.jl | 2 +- 21 files changed, 948 insertions(+), 197 deletions(-) diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 8bf4cb3340..ec5b8722c8 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -203,13 +203,11 @@ function add_manifold_benchmarks() sphere_tv_dist = Manifolds.normal_tvector_distribution(Ms, (@MVector [1.0, 0.0, 0.0]), 1.0) power_s1_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Ms1), - rand(power_s1_pt_dist), + TangentSpace(Ms1, rand(power_s1_pt_dist)), sphere_tv_dist, ) power_s2_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Ms2), - rand(power_s2_pt_dist), + TangentSpace(Ms2, rand(power_s2_pt_dist)), sphere_tv_dist, ) @@ -224,13 +222,11 @@ function add_manifold_benchmarks() ) rotations_tv_dist = Manifolds.normal_tvector_distribution(Mr, MMatrix(id_rot), 1.0) power_r1_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Mr1), - rand(power_r1_pt_dist), + TangentSpace(Mr1, rand(power_r1_pt_dist)), rotations_tv_dist, ) power_r2_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Mr2), - rand(power_r2_pt_dist), + TangentSpace(Mr2, rand(power_r2_pt_dist)), rotations_tv_dist, ) diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index b996756a25..8bc8f208f7 100644 --- a/docs/src/manifolds/vector_bundle.md +++ b/docs/src/manifolds/vector_bundle.md @@ -5,12 +5,8 @@ Vector bundle $E$ is a special case of a [fiber bundle](@ref FiberBundleSection) Tangent bundle is a simple example of a vector bundle, where each fiber is the tangent space at the specified point $p$. An object representing a tangent bundle can be obtained using the constructor called `TangentBundle`. -Fibers of a vector bundle are represented by the type `VectorBundleFibers`. -The important difference between functions operating on `VectorBundle` and `VectorBundleFibers` is that in the first case both a point on the underlying manifold and the vector are represented together (by a single argument) while in the second case only the vector part is present, while the point is supplied in a different argument where needed. - -`VectorBundleFibers` refers to the whole set of fibers of a vector bundle. -There is also another type, [`VectorSpaceAtPoint`](@ref), that represents a specific fiber at a given point. -This distinction is made to reduce the need to repeatedly construct objects of type [`VectorSpaceAtPoint`](@ref) in certain usage scenarios. +There is also another type, [`VectorSpaceFiber`](@ref), that represents a specific fiber at a given point. +This distinction is made to reduce the need to repeatedly construct objects of type [`VectorSpaceFiber`](@ref) in certain usage scenarios. This is also considered a manifold. ## FVector diff --git a/docs/src/misc/notation.md b/docs/src/misc/notation.md index 3ea725d2f5..bcc046c85b 100644 --- a/docs/src/misc/notation.md +++ b/docs/src/misc/notation.md @@ -22,7 +22,7 @@ Within the documented functions, the utf8 symbols are used whenever possible, as | ``n`` | dimension (of a manifold) | ``n_1,n_2,\ldots,m, \dim(\mathcal M)``| for the real dimension sometimes also ``\dim_{\mathbb R}(\mathcal M)``| | ``d(\cdot,\cdot)`` | (Riemannian) distance | ``d_{\mathcal M}(\cdot,\cdot)`` | | | ``\exp_p X`` | exponential map at ``p \in \mathcal M`` of a vector ``X \in T_p \mathcal M`` | ``\exp_p(X)`` | | -| ``F`` | a fiber | | see [`BundleFibers`](@ref), [`VectorBundleFibers`](@ref) | +| ``F`` | a fiber | | see [`Fiber`](@ref) | | ``\mathbb F`` | a field, usually ``\mathbb F \in \{\mathbb R,\mathbb C, \mathbb H\}``, i.e. the real, complex, and quaternion numbers, respectively. | |field a manifold or a basis is based on | | ``\gamma`` | a geodesic | ``\gamma_{p;q}``, ``\gamma_{p,X}`` | connecting two points ``p,q`` or starting in ``p`` with velocity ``X``. | | ``\operatorname{grad} f(p)`` | (Riemannian) gradient of function ``f \colon \mathcal{M} \to \mathbb{R}`` at ``p \in \mathcal{M}`` | | | diff --git a/ext/ManifoldsTestExt/tests_general.jl b/ext/ManifoldsTestExt/tests_general.jl index 7e43010977..23824aaf67 100644 --- a/ext/ManifoldsTestExt/tests_general.jl +++ b/ext/ManifoldsTestExt/tests_general.jl @@ -156,12 +156,6 @@ function test_manifold( Test.@testset "dimension" begin Test.@test isa(manifold_dimension(M), expected_dimension_type) Test.@test manifold_dimension(M) ≥ 0 - Test.@test manifold_dimension(M) == vector_space_dimension( - Manifolds.VectorBundleFibers(Manifolds.TangentSpace, M), - ) - Test.@test manifold_dimension(M) == vector_space_dimension( - Manifolds.VectorBundleFibers(Manifolds.CotangentSpace, M), - ) end test_representation_size && Test.@testset "representation" begin @@ -174,9 +168,6 @@ function test_manifold( end test_repr(Manifolds.representation_size(M)) - for fiber in (Manifolds.TangentSpace, Manifolds.CotangentSpace) - test_repr(Manifolds.representation_size(Manifolds.VectorBundleFibers(fiber, M))) - end end test_injectivity_radius && Test.@testset "injectivity radius" begin @@ -396,7 +387,7 @@ function test_manifold( test_vector_spaces && Test.@testset "vector spaces tests" begin for p in pts X = zero_vector(M, p) - mts = Manifolds.VectorBundleFibers(Manifolds.TangentSpace, M) + mts = TangentSpace(M, p) Test.@test isapprox(M, p, X, zero_vector(mts, p)) if is_mutating zero_vector!(mts, X, p) @@ -819,11 +810,11 @@ function test_manifold( Test.@testset "tangent vector distributions" begin for tvd in tvector_distributions supp = Manifolds.support(tvd) - Test.@test supp isa Manifolds.FVectorSupport{TangentBundleFibers{typeof(M)}} + Test.@test supp isa Manifolds.FVectorSupport{<:TangentSpace{ℝ,typeof(M)}} for _ in 1:10 randtv = rand(tvd) atol = rand_tvector_atol_multiplier * find_eps(randtv) - Test.@test is_vector(M, supp.point, randtv, true; atol=atol) + Test.@test is_vector(M, supp.space.point, randtv, true; atol=atol) end end end diff --git a/src/Manifolds.jl b/src/Manifolds.jl index ca36a37901..7fdaa69cc9 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -172,9 +172,7 @@ import ManifoldsBase: Weingarten, Weingarten!, zero_vector, - zero_vector!, - CotangentSpace, - TangentSpace + zero_vector! import ManifoldDiff: adjoint_Jacobi_field, adjoint_Jacobi_field!, @@ -215,7 +213,6 @@ using ManifoldsBase: AbstractLinearVectorTransportMethod, ApproximateInverseRetraction, ApproximateRetraction, - BundleFibers, CachedBasis, CayleyRetraction, CayleyInverseRetraction, @@ -235,9 +232,7 @@ using ManifoldsBase: EmptyTrait, EuclideanMetric, ExponentialRetraction, - FiberBundleInverseProductRetraction, - FiberBundleProductRetraction, - FiberBundleProductVectorTransport, + Fiber, FiberType, FVector, InverseProductRetraction, @@ -275,14 +270,13 @@ using ManifoldsBase: QRRetraction, RealNumbers, RiemannianMetric, + SasakiRetraction, ScaledVectorTransport, SchildsLadderTransport, ShootingInverseRetraction, SoftmaxRetraction, SoftmaxInverseRetraction, - TangentBundle, - TangentBundleFibers, - TangentSpaceAtPoint, + TangentSpace, TangentSpaceType, TCoTSpaceType, TFVector, @@ -291,10 +285,7 @@ using ManifoldsBase: ValidationManifold, ValidationMPoint, ValidationTVector, - VectorBundle, - VectorBundleFibers, - VectorSpaceAtPoint, - VectorSpaceFiberType, + VectorSpaceFiber, VectorSpaceType, VeeOrthogonalBasis, @invoke_maker, @@ -386,7 +377,7 @@ METAMANIFOLDS = [ PowerManifoldNested, PowerManifoldNestedReplacing, ProductManifold, - TangentSpaceAtPoint, + TangentSpace, ValidationManifold, VectorBundle, ] @@ -512,14 +503,14 @@ This method employs [`is_point`](https://juliamanifolds.github.io/ManifoldsBase. Base.in(p, M::AbstractManifold; kwargs...) = is_point(M, p, false; kwargs...) @doc raw""" - Base.in(p, TpM::TangentSpaceAtPoint; kwargs...) - X ∈ TangentSpaceAtPoint(M,p) + Base.in(p, TpM::TangentSpace; kwargs...) + X ∈ TangentSpace(M, p) Check whether `X` is a tangent vector from (in) the tangent space $T_p\mathcal M$, i.e. -the [`TangentSpaceAtPoint`](@ref) at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. +the [`TangentSpace`](@ref) at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. This method uses [`is_vector`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions.html#ManifoldsBase.is_vector) deactivating the error throw option. """ -function Base.in(X, TpM::TangentSpaceAtPoint; kwargs...) +function Base.in(X, TpM::TangentSpace; kwargs...) return is_vector(base_manifold(TpM), TpM.point, X, false; kwargs...) end @@ -681,8 +672,7 @@ export AbstractDecoratorManifold export IsIsometricEmbeddedManifold, IsEmbeddedManifold, IsEmbeddedSubmanifold export IsDefaultMetric, IsDefaultConnection, IsMetricManifold, IsConnectionManifold export ValidationManifold, ValidationMPoint, ValidationTVector, ValidationCoTVector -export CotangentBundle, - CotangentSpaceAtPoint, CotangentBundleFibers, CotangentSpace, FVector +export CotangentBundle, CotangentSpace, FVector export AbstractPowerManifold, AbstractPowerRepresentation, ArrayPowerRepresentation, @@ -693,9 +683,8 @@ export AbstractPowerManifold, export ProductManifold, EmbeddedManifold export GraphManifold, GraphManifoldType, VertexManifold, EdgeManifold export ArrayPartition -export ProjectedPointDistribution, TangentBundle, TangentBundleFibers -export TangentSpace, TangentSpaceAtPoint, VectorSpaceAtPoint, VectorSpaceType, VectorBundle -export BundleFibers, VectorBundleFibers +export ProjectedPointDistribution, TangentBundle +export TangentSpace, VectorSpaceFiber, VectorSpaceType, VectorBundle export AbstractVectorTransportMethod, DifferentiatedRetractionVectorTransport, ParallelTransport, ProjectedPointDistribution export PoleLadderTransport, SchildsLadderTransport diff --git a/src/atlases.jl b/src/atlases.jl index 9b4bbfa110..5bb5f1ce4b 100644 --- a/src/atlases.jl +++ b/src/atlases.jl @@ -410,7 +410,7 @@ function dual_basis( ::Any, B::InducedBasis{𝔽,TangentSpaceType}, ) where {𝔽} - return induced_basis(M, B.A, B.i, CotangentSpace) + return induced_basis(M, B.A, B.i, CotangentSpaceType()) end function dual_basis( M::AbstractManifold{𝔽}, diff --git a/src/distributions.jl b/src/distributions.jl index f1ac656e62..78aee049c6 100644 --- a/src/distributions.jl +++ b/src/distributions.jl @@ -7,26 +7,25 @@ is a vector from a fiber of a vector bundle. struct FVectorvariate <: VariateForm end """ - FVectorSupport(space::AbstractManifold, VectorBundleFibers) + FVectorSupport(space::AbstractManifold, VectorSpaceFiber) Value support for vector bundle fiber-valued distributions (values from a fiber of a vector bundle at a `point` from the given manifold). For example used for tangent vector-valued distributions. """ -struct FVectorSupport{TSpace<:VectorBundleFibers,T} <: ValueSupport +struct FVectorSupport{TSpace<:VectorSpaceFiber} <: ValueSupport space::TSpace - point::T end """ - FVectorDistribution{TSpace<:VectorBundleFibers, T} + FVectorDistribution{TSpace<:VectorSpaceFiber, T} An abstract distribution for vector bundle fiber-valued distributions (values from a fiber of a vector bundle at point `x` from the given manifold). For example used for tangent vector-valued distributions. """ -abstract type FVectorDistribution{TSpace<:VectorBundleFibers,T} <: - Distribution{FVectorvariate,FVectorSupport{TSpace,T}} end +abstract type FVectorDistribution{TSpace<:VectorSpaceFiber} <: + Distribution{FVectorvariate,FVectorSupport{TSpace}} end """ MPointvariate diff --git a/src/manifolds/Fiber.jl b/src/manifolds/Fiber.jl index 8b13789179..069a3fac15 100644 --- a/src/manifolds/Fiber.jl +++ b/src/manifolds/Fiber.jl @@ -1 +1,71 @@ +""" + BundleFibers(fiber::FiberType, M::AbstractManifold) + +Type representing a family of fibers of a fiber bundle over `M` +with fiber of type `fiber`. In contrast with `FiberBundle`, operations +on `BundleFibers` expect point-like and fiber-like parts to be +passed separately instead of being bundled together. It can be thought of +as a representation of fibers from a fiber bundle but without +storing the point at which a fiber is attached (which is specified +separately in various functions). +""" +struct BundleFibers{TF<:FiberType,TM<:AbstractManifold} + fiber::TF + manifold::TM +end + +""" + allocate_result(B::BundleFibers, f, x...) + +Allocates an array for the result of function `f` that is +an element of the fiber space of type `B.fiber` on manifold `B.manifold` +and arguments `x...` for implementing the non-modifying operation +using the modifying operation. +""" +@inline function allocate_result(B::BundleFibers, f::TF, x...) where {TF} + if length(x) == 0 + # TODO: this may be incorrect when point and tangent vector representation are + # different for the manifold but there is no easy and generic way around that + return allocate_result(B.manifold, f) + else + T = allocate_result_type(B, f, x) + return allocate(x[1], T) + end +end + +""" + allocate_result_type(B::BundleFibers, f, args::NTuple{N,Any}) where N + +Return type of element of the array that will represent the result of +function `f` for representing an operation with result in the fiber `fiber` +for manifold `M` on given arguments (passed at a tuple). +""" +@inline function allocate_result_type( + ::BundleFibers, + f::TF, + args::NTuple{N,Any}, +) where {TF,N} + return typeof(mapreduce(eti -> one(number_eltype(eti)), +, args)) +end + +base_manifold(B::BundleFibers) = base_manifold(B.manifold) + +function fiber_dimension(B::BundleFibers) + return fiber_dimension(B.manifold, B.fiber) +end + +function Base.show(io::IO, fiber::BundleFibers) + return print(io, "BundleFibers($(fiber.fiber), $(fiber.manifold))") +end + +""" + zero_vector(B::BundleFibers, p) + +Compute the zero vector from the vector space of type `B.fiber` at point `p` +from manifold `B.manifold`. +""" +function zero_vector(B::BundleFibers, p) + X = allocate_result(B, zero_vector, p) + return zero_vector!(B, X, p) +end diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index 8b13789179..4f43a1ce92 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -1 +1,420 @@ +@doc raw""" + FiberBundleProductVectorTransport{ + TMP<:AbstractVectorTransportMethod, + TMV<:AbstractVectorTransportMethod, + } <: AbstractVectorTransportMethod + +Vector transport type on [`FiberBundle`](@ref). + +# Fields + +* `method_point` – vector transport method of the point part +* `method_fiber` – vector transport method of the fiber part. + +The vector transport is derived as a product manifold-style vector transport. +The considered product manifold is the product between the manifold ``\mathcal M`` +and the topological vector space isometric to the fiber. + +# Constructor + + FiberBundleProductVectorTransport( + M::AbstractManifold=DefaultManifold(); + vector_tansport_method_point = default_vector_transport_method(M), + vector_transport_method_fiber = default_vector_transport_method(M), + ) + +Construct the `FiberBundleProductVectorTransport` using the [`default_vector_transport_method`](@ref), +which uses [`ParallelTransport`](@ref) if no manifold is provied. +""" +struct FiberBundleProductVectorTransport{ + TMP<:AbstractVectorTransportMethod, + TMV<:AbstractVectorTransportMethod, +} <: AbstractVectorTransportMethod + method_point::TMP + method_fiber::TMV +end +function FiberBundleProductVectorTransport( + M::AbstractManifold=DefaultManifold(); + vector_tansport_method_point=default_vector_transport_method(M), + vector_transport_method_fiber=default_vector_transport_method(M), +) + return FiberBundleProductVectorTransport( + vector_tansport_method_point, + vector_transport_method_fiber, + ) +end + +""" + FiberBundle{𝔽,TVS<:FiberType,TM<:AbstractManifold{𝔽},TVT<:FiberBundleProductVectorTransport} <: AbstractManifold{𝔽} + +Fiber bundle on a [`AbstractManifold`](@ref) `M` of type [`FiberType`](@ref). +Examples include vector bundles, principal bundles or unit tangent bundles, see also [📖 Fiber Bundle](https://en.wikipedia.org/wiki/Fiber_bundle). + +# Fields +* `manifold` – the [`AbstractManifold`](@ref) manifold the Fiber bundle is defined on +* `type` – representing the type of fiber we use. + +# Constructor + + FiberBundle(M::AbstractManifold, type::FiberType) +""" +struct FiberBundle{ + 𝔽, + TF<:FiberType, + TM<:AbstractManifold{𝔽}, + TVT<:FiberBundleProductVectorTransport, +} <: AbstractManifold{𝔽} + type::TF + manifold::TM + fiber::BundleFibers{TF,TM} + vector_transport::TVT +end + +function FiberBundle( + fiber::TVS, + M::TM, + vtm::FiberBundleProductVectorTransport, +) where {TVS<:FiberType,TM<:AbstractManifold{𝔽}} where {𝔽} + return FiberBundle{𝔽,TVS,TM,typeof(vtm)}(fiber, M, BundleFibers(fiber, M), vtm) +end +function FiberBundle(fiber::FiberType, M::AbstractManifold) + vtmm = vector_bundle_transport(fiber, M) + vtbm = FiberBundleProductVectorTransport(vtmm, vtmm) + return FiberBundle(fiber, M, vtbm) +end + +@doc raw""" + struct FiberBundleInverseProductRetraction <: AbstractInverseRetractionMethod end + +Inverse retraction of the point `y` at point `p` from vector bundle `B` over manifold +`B.fiber` (denoted ``\mathcal M``). The inverse retraction is derived as a product manifold-style +approximation to the logarithmic map in the Sasaki metric. The considered product manifold +is the product between the manifold ``\mathcal M`` and the topological vector space isometric +to the fiber. + +## Notation +The point ``p = (x_p, V_p)`` where ``x_p ∈ \mathcal M`` and ``V_p`` belongs to +the fiber ``F=π^{-1}(\{x_p\})`` of the vector bundle ``B`` where ``π`` is the canonical +projection of that vector bundle ``B``. Similarly, ``q = (x_q, V_q)``. + +The inverse retraction is calculated as + +```math +\operatorname{retr}^{-1}_p q = (\operatorname{retr}^{-1}_{x_p}(x_q), V_{\operatorname{retr}^{-1}} - V_p) +``` + +where ``V_{\operatorname{retr}^{-1}}`` is the result of vector transport of ``V_q`` to the point ``x_p``. +The difference ``V_{\operatorname{retr}^{-1}} - V_p`` corresponds to the logarithmic map in +the vector space ``F``. + +See also [`FiberBundleProductRetraction`](@ref). +""" +struct FiberBundleInverseProductRetraction <: AbstractInverseRetractionMethod end + +@doc raw""" + struct FiberBundleProductRetraction <: AbstractRetractionMethod end + +Product retraction map of tangent vector ``X`` at point ``p`` from vector bundle `B` over +manifold `B.fiber` (denoted ``\mathcal M``). The retraction is derived as a product manifold-style +approximation to the exponential map in the Sasaki metric. The considered product manifold +is the product between the manifold ``\mathcal M`` and the topological vector space isometric +to the fiber. + +## Notation: +* The point ``p = (x_p, V_p)`` where ``x_p ∈ \mathcal M`` and ``V_p`` belongs to the + fiber ``F=π^{-1}(\{x_p\})`` of the vector bundle ``B`` where ``π`` is the + canonical projection of that vector bundle ``B``. +* The tangent vector ``X = (V_{X,M}, V_{X,F}) ∈ T_pB`` where + ``V_{X,M}`` is a tangent vector from the tangent space ``T_{x_p}\mathcal M`` and + ``V_{X,F}`` is a tangent vector from the tangent space ``T_{V_p}F`` (isomorphic to ``F``). + +The retraction is calculated as + +```math +\operatorname{retr}_p(X) = (\exp_{x_p}(V_{X,M}), V_{\exp}) +```` + +where ``V_{\exp}`` is the result of vector transport of ``V_p + V_{X,F}`` +to the point ``\exp_{x_p}(V_{X,M})``. +The sum ``V_p + V_{X,F}`` corresponds to the exponential map in the vector space ``F``. + +See also [`FiberBundleInverseProductRetraction`](@ref). +""" +struct FiberBundleProductRetraction <: AbstractRetractionMethod end + +vector_bundle_transport(::FiberType, M::AbstractManifold) = ParallelTransport() + +struct FiberBundleBasisData{BBasis<:CachedBasis,TBasis<:CachedBasis} + base_basis::BBasis + fiber_basis::TBasis +end + +""" + base_manifold(B::FiberBundle) + +Return the manifold the [`FiberBundle`](@ref)s is build on. +""" +base_manifold(B::FiberBundle) = base_manifold(B.manifold) + +""" + bundle_projection(B::FiberBundle, p) + +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 +of `p` is attached. +""" +bundle_projection(B::FiberBundle, p) = submanifold_component(B.manifold, p, Val(1)) + +function get_basis(M::FiberBundle, p, B::AbstractBasis) + xp1 = submanifold_component(p, Val(1)) + base_basis = get_basis(M.manifold, xp1, B) + fiber_basis = get_basis(M.fiber, xp1, B) + return CachedBasis(B, FiberBundleBasisData(base_basis, fiber_basis)) +end +function get_basis(M::FiberBundle, p, B::CachedBasis) + return invoke(get_basis, Tuple{AbstractManifold,Any,CachedBasis}, M, p, B) +end + +function get_basis(M::FiberBundle, p, B::DiagonalizingOrthonormalBasis) + xp1 = submanifold_component(p, Val(1)) + bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(1))) + b1 = get_basis(M.manifold, xp1, bv1) + bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(2))) + b2 = get_basis(M.fiber, xp1, bv2) + return CachedBasis(B, FiberBundleBasisData(b1, b2)) +end + +function get_coordinates(M::FiberBundle, p, X, B::AbstractBasis) + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + n = manifold_dimension(M.manifold) + return vcat( + get_coordinates(M.manifold, px, VXM, B), + get_coordinates(M.fiber, px, VXF, B), + ) +end + +function get_coordinates!(M::FiberBundle, Y, p, X, B::AbstractBasis) + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + n = manifold_dimension(M.manifold) + get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B) + get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B) + return Y +end + +function get_coordinates( + M::FiberBundle, + p, + X, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + return vcat( + get_coordinates(M.manifold, px, VXM, B.data.base_basis), + get_coordinates(M.fiber, px, VXF, B.data.fiber_basis), + ) +end + +function get_coordinates!( + M::FiberBundle, + Y, + p, + X, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + px, Vx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + n = manifold_dimension(M.manifold) + get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B.data.base_basis) + get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B.data.fiber_basis) + return Y +end + +function get_vector!(M::FiberBundle, Y, p, X, B::AbstractBasis) + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + get_vector!(M.manifold, submanifold_component(Y, Val(1)), xp1, X[1:n], B) + get_vector!(M.fiber, submanifold_component(Y, Val(2)), xp1, X[(n + 1):end], B) + return Y +end + +function get_vector!( + M::FiberBundle, + Y, + p, + X, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + get_vector!( + M.manifold, + submanifold_component(Y, Val(1)), + xp1, + X[1:n], + B.data.base_basis, + ) + get_vector!( + M.fiber, + submanifold_component(Y, Val(2)), + xp1, + X[(n + 1):end], + B.data.fiber_basis, + ) + return Y +end + +function get_vectors(M::BundleFibers, p, B::CachedBasis) + return get_vectors(M.manifold, p, B) +end + +function _isapprox(B::FiberBundle, p, q; kwargs...) + xp, Vp = submanifold_components(B.manifold, p) + xq, Vq = submanifold_components(B.manifold, q) + return isapprox(B.manifold, xp, xq; kwargs...) && + isapprox(FiberAtPoint(B.fiber, xp), Vp, Vq; kwargs...) +end +function _isapprox(B::FiberBundle, p, X, Y; kwargs...) + px, Vx = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + VYM, VYF = submanifold_components(B.manifold, Y) + return isapprox(B.manifold, VXM, VYM; kwargs...) && + isapprox(FiberAtPoint(B.fiber, px), VXF, VYF; kwargs...) +end + +function manifold_dimension(B::FiberBundle) + return manifold_dimension(B.manifold) + fiber_dimension(B.fiber) +end + +function Random.rand!(M::FiberBundle, pX; vector_at=nothing) + return rand!(Random.default_rng(), M, pX; vector_at=vector_at) +end +function Random.rand!(rng::AbstractRNG, M::FiberBundle, pX; vector_at=nothing) + pXM, pXF = submanifold_components(M.manifold, pX) + if vector_at === nothing + rand!(rng, M.manifold, pXM) + rand!(rng, FiberAtPoint(M.fiber, pXM), pXF) + else + vector_atM, vector_atF = submanifold_components(M.manifold, vector_at) + rand!(rng, M.manifold, pXM; vector_at=vector_atM) + rand!(rng, FiberAtPoint(M.fiber, pXM), pXF; vector_at=vector_atF) + end + return pX +end + +@doc raw""" + zero_vector(B::FiberBundle, p) + +Zero tangent vector at point `p` from the fiber bundle `B` +over manifold `B.fiber` (denoted $\mathcal M$). The zero vector belongs to the space $T_{p}B$ + +Notation: + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the + canonical projection of that vector bundle $B$. + +The zero vector is calculated as + +$\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)$ + +where $\mathbf{0}_{x_p}$ is the zero tangent vector from $T_{x_p}\mathcal M$ and +$\mathbf{0}_F$ is the zero element of the vector space $F$. +""" +zero_vector(::FiberBundle, ::Any...) + +function zero_vector!(B::FiberBundle, X, p) + xp, Vp = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + zero_vector!(B.manifold, VXM, xp) + zero_vector!(B.fiber, VXF, Vp) + return X +end + +@inline function allocate_result(M::FiberBundle, f::TF) where {TF} + return ArrayPartition(allocate_result(M.manifold, f), allocate_result(M.fiber, f)) +end + +function get_vector(M::FiberBundle, p, X, B::AbstractBasis) + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + return ArrayPartition( + get_vector(M.manifold, xp1, X[1:n], B), + get_vector(M.fiber, xp1, X[(n + 1):end], B), + ) +end +function get_vector( + M::FiberBundle, + p, + X, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + n = manifold_dimension(M.manifold) + xp1 = submanifold_component(p, Val(1)) + 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.fiber_basis), + ) +end + +function get_vectors( + M::FiberBundle, + p::ArrayPartition, + B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, +) where {𝔽} + xp1 = submanifold_component(p, Val(1)) + zero_m = zero_vector(M.manifold, xp1) + zero_f = zero_vector(M.fiber, xp1) + vs = typeof(ArrayPartition(zero_m, zero_f))[] + for bv in get_vectors(M.manifold, xp1, B.data.base_basis) + push!(vs, ArrayPartition(bv, zero_f)) + end + for bv in get_vectors(M.fiber, xp1, B.data.fiber_basis) + push!(vs, ArrayPartition(zero_m, bv)) + end + return vs +end + +""" + getindex(p::ArrayPartition, M::FiberBundle, s::Symbol) + p[M::FiberBundle, s] + +Access the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` by +using the symbols `:point` and `:vector` or `:fiber` for the base and vector or fiber +component, respectively. +""" +@inline function Base.getindex(p::ArrayPartition, M::FiberBundle, s::Symbol) + (s === :point) && return p.x[1] + (s === :vector || s === :fiber) && return p.x[2] + return throw(DomainError(s, "unknown component $s on $M.")) +end + +""" + setindex!(p::ArrayPartition, val, M::FiberBundle, s::Symbol) + p[M::VectorBundle, s] = val + +Set the element(s) at index `s` of a point `p` on a [`FiberBundle`](@ref) `M` to `val` by +using the symbols `:point` and `:fiber` or `:vector` for the base and fiber or vector +component, respectively. + +!!! note + + The *content* of element of `p` is replaced, not the element itself. +""" +@inline function Base.setindex!(x::ArrayPartition, val, M::FiberBundle, s::Symbol) + if s === :point + return copyto!(x.x[1], val) + elseif s === :vector || s === :fiber + return copyto!(x.x[2], val) + else + throw(DomainError(s, "unknown component $s on $M.")) + end +end + +@inline function Base.view(x::ArrayPartition, M::FiberBundle, s::Symbol) + (s === :point) && return x.x[1] + (s === :vector || s === :fiber) && return x.x[2] + throw(DomainError(s, "unknown component $s on $M.")) +end diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index ed3b1ab516..7acd69f3c0 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -38,20 +38,16 @@ struct PowerPointDistribution{TM<:AbstractPowerManifold,TD<:MPointDistribution,T end """ - PowerFVectorDistribution([type::VectorBundleFibers], [x], distr) + PowerFVectorDistribution([type::VectorSpaceFiber], [x], distr) Generates a random vector at a `point` from vector space (a fiber of a tangent bundle) of type `type` using the power distribution of `distr`. Vector space type and `point` can be automatically inferred from distribution `distr`. """ -struct PowerFVectorDistribution{ - TSpace<:VectorBundleFibers{<:VectorSpaceType,<:AbstractPowerManifold}, - TD<:FVectorDistribution, - TX, -} <: FVectorDistribution{TSpace,TX} +struct PowerFVectorDistribution{TSpace<:VectorSpaceFiber,TD<:FVectorDistribution} <: + FVectorDistribution{TSpace} type::TSpace - point::TX distribution::TD end @@ -157,7 +153,7 @@ function Distributions._rand!( PM = d.type.manifold rep_size = representation_size(PM.manifold) for i in get_iterator(d.type.manifold) - copyto!(d.distribution.point, _read(PM, rep_size, d.point, i)) + copyto!(d.distribution.point, _read(PM, rep_size, d.type.point, i)) Distributions._rand!(rng, d.distribution, _read(PM, rep_size, v, i)) end return v diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 5814f2953e..90527128fd 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -8,7 +8,7 @@ function allocate_coordinates(::ProductManifold, p, T, n::Int) end """ - ProductFVectorDistribution([type::VectorBundleFibers], [x], distrs...) + ProductFVectorDistribution([type::VectorSpaceFiber], [x], distrs...) Generates a random vector at point `x` from vector space (a fiber of a tangent bundle) of type `type` using the product distribution of given distributions. @@ -16,12 +16,10 @@ bundle) of type `type` using the product distribution of given distributions. Vector space type and `x` can be automatically inferred from distributions `distrs`. """ struct ProductFVectorDistribution{ - TSpace<:VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, + TSpace<:VectorSpaceFiber{<:Any,<:ProductManifold}, TD<:(NTuple{N,Distribution} where {N}), - TX, -} <: FVectorDistribution{TSpace,TX} +} <: FVectorDistribution{TSpace} type::TSpace - x::TX distributions::TD end @@ -149,35 +147,17 @@ manifolds `M` is constructed from. """ manifold_volume(M::ProductManifold) = mapreduce(manifold_volume, *, M.manifolds) -function ProductFVectorDistribution( - type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, - p::Union{AbstractArray,AbstractManifoldPoint}, - distributions::FVectorDistribution..., -) - return ProductFVectorDistribution{typeof(type),typeof(distributions),typeof(p)}( - type, - p, - distributions, - ) -end -function ProductFVectorDistribution( - type::VectorBundleFibers{<:VectorSpaceType,<:ProductManifold}, - distributions::FVectorDistribution..., -) - p = ArrayPartition(map(d -> support(d).point, distributions)) - return ProductFVectorDistribution(type, p, distributions...) -end function ProductFVectorDistribution(distributions::FVectorDistribution...) M = ProductManifold(map(d -> support(d).space.manifold, distributions)...) - fiber = support(distributions[1]).space.fiber - if !all(d -> support(d).space.fiber == fiber, distributions) + fiber_type = support(distributions[1]).space.fiber_type + if !all(d -> support(d).space.fiber_type == fiber_type, distributions) error( "Not all distributions have support in vector spaces of the same type, which is currently not supported", ) end # Probably worth considering sum spaces in the future? - x = ArrayPartition(map(d -> support(d).point, distributions)...) - return ProductFVectorDistribution(VectorBundleFibers(fiber, M), x, distributions...) + p = ArrayPartition(map(d -> support(d).space.point, distributions)...) + return ProductFVectorDistribution(Fiber(M, fiber_type, p), distributions) end function ProductPointDistribution(M::ProductManifold, distributions::MPointDistribution...) @@ -270,10 +250,7 @@ sharp(::ProductManifold, ::Any...) Distributions.support(d::ProductPointDistribution) = MPointSupport(d.manifold) function Distributions.support(tvd::ProductFVectorDistribution) - return FVectorSupport( - tvd.type, - ArrayPartition(map(d -> support(d).point, tvd.distributions)...), - ) + return FVectorSupport(tvd.type) end function uniform_distribution(M::ProductManifold) diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 1c52a4cbbc..66d524020f 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -1,33 +1,399 @@ +""" + const VectorBundleVectorTransport = FiberBundleProductVectorTransport + +Deprecated: an alias for `FiberBundleProductVectorTransport`. +""" +const VectorBundleVectorTransport = FiberBundleProductVectorTransport + +""" + fiber_bundle_transport(fiber::FiberType, M::AbstractManifold) + +Determine the vector tranport used for [`exp`](@ref exp(::FiberBundle, ::Any...)) and +[`log`](@ref log(::FiberBundle, ::Any...)) maps on a vector bundle with fiber type +`fiber` and manifold `M`. +""" +fiber_bundle_transport(::VectorSpaceType, ::AbstractManifold) = ParallelTransport() + +""" + VectorBundle{𝔽,TVS,TM,VTV} = = FiberBundle{𝔽,VectorSpaceFiberType{TVS},TM,TVT} + +Alias for [`FiberBundle`](@ref) when fiber type is a `TVS` of type +[`VectorSpaceType`](@ref). + +`VectorSpaceFiberType` is used to encode vector spaces as fiber types. +""" +const VectorBundle{𝔽,TVS,TM,TVT} = FiberBundle{ + 𝔽, + TVS, + TM, + TVT, +} where { + TVS<:VectorSpaceType, + TM<:AbstractManifold{𝔽}, + TVT<:FiberBundleProductVectorTransport, +} + +""" + TangentBundle{𝔽,M} = VectorBundle{𝔽,TangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} + +Tangent bundle for manifold of type `M`, as a manifold with the Sasaki metric [Sasaki:1958](@cite). + +Exact retraction and inverse retraction can be approximated using [`FiberBundleProductRetraction`](@ref), +[`FiberBundleInverseProductRetraction`](@ref) and [`SasakiRetraction`](@ref). +[`FiberBundleProductVectorTransport`](@ref) can be used as a vector transport. + +# Constructors + + TangentBundle(M::AbstractManifold) + TangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) +""" +const TangentBundle{𝔽,M} = + VectorBundle{𝔽,TangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} + +TangentBundle(M::AbstractManifold) = FiberBundle(TangentSpaceType(), M) +function TangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) + return VectorBundle(TangentSpaceType(), M, vtm) +end + const CotangentBundle{𝔽,M} = VectorBundle{𝔽,CotangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} -CotangentBundle(M::AbstractManifold) = VectorBundle(CotangentSpace, M) +CotangentBundle(M::AbstractManifold) = FiberBundle(CotangentSpaceType(), M) function CotangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) - return VectorBundle(CotangentSpace, M, vtm) + return VectorBundle(CotangentSpaceType(), M, vtm) +end + +function default_inverse_retraction_method(::TangentBundle) + return FiberBundleInverseProductRetraction() +end + +function default_retraction_method(::TangentBundle) + return FiberBundleProductRetraction() +end + +function default_vector_transport_method(B::TangentBundle) + default_vt_m = default_vector_transport_method(B.manifold) + return FiberBundleProductVectorTransport(default_vt_m, default_vt_m) end """ - const VectorBundleVectorTransport = FiberBundleProductVectorTransport + injectivity_radius(M::TangentBundle) -Deprecated: an alias for `FiberBundleProductVectorTransport`. +Injectivity radius of [`TangentBundle`](@ref) manifold is infinite if the base manifold +is flat and 0 otherwise. +See [https://mathoverflow.net/questions/94322/injectivity-radius-of-the-sasaki-metric](https://mathoverflow.net/questions/94322/injectivity-radius-of-the-sasaki-metric). """ -const VectorBundleVectorTransport = FiberBundleProductVectorTransport +function injectivity_radius(M::TangentBundle) + if is_flat(M.manifold) + return Inf + else + return 0.0 + end +end -const CotangentBundle{𝔽,M} = - VectorBundle{𝔽,CotangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} +@doc raw""" + inner(B::VectorBundle, p, X, Y) + +Inner product of tangent vectors `X` and `Y` at point `p` from the +vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). + +Notation: + * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the + fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the + canonical projection of that vector bundle $B$. + * The tangent vector $v = (V_{X,M}, V_{X,F}) ∈ T_{x}B$ where + $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}\mathcal M$ and + $V_{X,F}$ is a tangent vector from the tangent space $T_{V_p}F$ (isomorphic to $F$). + Similarly for the other tangent vector $w = (V_{Y,M}, V_{Y,F}) ∈ T_{x}B$. + +The inner product is calculated as -function representation_size(B::CotangentBundleFibers) - return representation_size(B.manifold) +$⟨X, Y⟩_p = ⟨V_{X,M}, V_{Y,M}⟩_{x_p} + ⟨V_{X,F}, V_{Y,F}⟩_{V_p}.$ +""" +function inner(B::FiberBundle, p, X, Y) + px, Vx = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + VYM, VYF = submanifold_components(B.manifold, Y) + # for tangent bundle Vx is discarded by the method of inner for TangentSpace + # and px is actually used as the base point + return inner(B.manifold, px, VXM, VYM) + inner(FiberAtPoint(B.fiber, px), Vx, VXF, VYF) end -Base.show(io::IO, vb::CotangentBundle) = print(io, "CotangentBundle($(vb.manifold))") +function _inverse_retract(M::FiberBundle, p, q, ::FiberBundleInverseProductRetraction) + return inverse_retract_product(M, p, q) +end + +function _inverse_retract!(M::FiberBundle, X, p, q, ::FiberBundleInverseProductRetraction) + return inverse_retract_product!(M, X, p, q) +end """ - fiber_bundle_transport(fiber::FiberType, M::AbstractManifold) + inverse_retract(M::VectorBundle, p, q, ::FiberBundleInverseProductRetraction) -Determine the vector tranport used for [`exp`](@ref exp(::FiberBundle, ::Any...)) and -[`log`](@ref log(::FiberBundle, ::Any...)) maps on a vector bundle with fiber type -`fiber` and manifold `M`. +Compute the allocating variant of the [`FiberBundleInverseProductRetraction`](@ref), +which by default allocates and calls `inverse_retract_product!`. """ -fiber_bundle_transport(::VectorSpaceType, ::AbstractManifold) = ParallelTransport() +inverse_retract(::VectorBundle, p, q, ::FiberBundleInverseProductRetraction) + +function inverse_retract_product(M::VectorBundle, p, q) + X = allocate_result(M, inverse_retract, p, q) + return inverse_retract_product!(M, X, p, q) +end + +function inverse_retract_product!(B::VectorBundle, X, p, q) + px, Vx = submanifold_components(B.manifold, p) + py, Vy = submanifold_components(B.manifold, q) + VXM, VXF = submanifold_components(B.manifold, X) + log!(B.manifold, VXM, px, py) + vector_transport_to!(B.fiber, VXF, py, Vy, px, B.vector_transport.method_fiber) + copyto!(VXF, VXF - Vx) + return X +end + +""" + is_flat(::VectorBundle) + +Return true if the underlying manifold of [`VectorBundle`](@ref) `M` is flat. +""" +is_flat(M::VectorBundle) = is_flat(M.manifold) + +@doc raw""" + project(B::VectorBundle, p) + +Project the point `p` from the ambient space of the vector bundle `B` +over manifold `B.fiber` (denoted ``\mathcal M``) to the vector bundle. + +Notation: + * The point ``p = (x_p, V_p)`` where ``x_p`` belongs to the ambient space of ``\mathcal M`` + and ``V_p`` belongs to the ambient space of the + fiber ``F=π^{-1}(\{x_p\})`` of the vector bundle ``B`` where ``π`` is the + canonical projection of that vector bundle ``B``. + +The projection is calculated by projecting the point ``x_p`` to the manifold ``\mathcal M`` +and then projecting the vector ``V_p`` to the tangent space ``T_{x_p}\mathcal M``. +""" +project(::VectorBundle, ::Any) + +function project!(B::VectorBundle, q, p) + px, pVx = submanifold_components(B.manifold, p) + qx, qVx = submanifold_components(B.manifold, q) + project!(B.manifold, qx, px) + project!(B.fiber, qVx, qx, pVx) + return q +end + +@doc raw""" + project(B::VectorBundle, p, X) + +Project the element `X` of the ambient space of the tangent space ``T_p B`` +to the tangent space ``T_p B``. + +Notation: + * The point ``p = (x_p, V_p)`` where ``x_p ∈ \mathcal M`` and ``V_p`` belongs to the + fiber ``F=π^{-1}(\{x_p\})`` of the vector bundle ``B`` where ``π`` is the + canonical projection of that vector bundle ``B``. + * The vector ``x = (V_{X,M}, V_{X,F})`` where ``x_p`` belongs to the ambient space of + ``T_{x_p}\mathcal M`` and ``V_{X,F}`` belongs to the ambient space of the + fiber ``F=π^{-1}(\{x_p\})`` of the vector bundle ``B`` where ``π`` is the + canonical projection of that vector bundle ``B``. + +The projection is calculated by projecting ``V_{X,M}`` to tangent space ``T_{x_p}\mathcal M`` +and then projecting the vector ``V_{X,F}`` to the fiber ``F``. +""" +project(::VectorBundle, ::Any, ::Any) + +function project!(B::VectorBundle, Y, p, X) + px, Vx = submanifold_components(B.manifold, p) + VXM, VXF = submanifold_components(B.manifold, X) + VYM, VYF = submanifold_components(B.manifold, Y) + project!(B.manifold, VYM, px, VXM) + project!(B.fiber, VYF, px, VXF) + return Y +end + +""" + project(B::BundleFibers, p, X) + +Project vector `X` from the vector space of type `B.fiber` at point `p`. +""" +function project(B::BundleFibers, p, X) + Y = allocate_result(B, project, p, X) + return project!(B, Y, p, X) +end + +function _retract(M::VectorBundle, p, X, t::Number, ::FiberBundleProductRetraction) + return retract_product(M, p, X, t) +end + +function _retract!(M::VectorBundle, q, p, X, t::Number, ::FiberBundleProductRetraction) + return retract_product!(M, q, p, X, t) +end + +""" + retract(M::VectorBundle, p, q, t::Number, ::FiberBundleProductRetraction) + +Compute the allocating variant of the [`FiberBundleProductRetraction`](@ref), +which by default allocates and calls `retract_product!`. +""" +retract(::VectorBundle, p, q, t::Number, ::FiberBundleProductRetraction) + +function retract_product(M::VectorBundle, p, X, t::Number) + q = allocate_result(M, retract, p, X) + return retract_product!(M, q, p, X, t) +end + +function retract_product!(B::VectorBundle, q, p, X, t::Number) + tX = t * X + xp, Xp = submanifold_components(B.manifold, p) + xq, Xq = submanifold_components(B.manifold, q) + VXM, VXF = submanifold_components(B.manifold, tX) + # this temporary avoids overwriting `p` when `q` and `p` occupy the same memory + xqt = exp(B.manifold, xp, VXM) + vector_transport_direction!( + B.manifold, + Xq, + xp, + Xp + VXF, + VXM, + B.vector_transport.method_point, + ) + copyto!(B.manifold, xq, xqt) + return q +end + +function retract_sasaki!(B::TangentBundle, q, p, X, t::Number, m::SasakiRetraction) + tX = t * X + xp, Xp = submanifold_components(B.manifold, p) + xq, Xq = submanifold_components(B.manifold, q) + VXM, VXF = submanifold_components(B.manifold, tX) + p_k = allocate(B.manifold, xp) + copyto!(B.manifold, p_k, xp) + X_k = allocate(B.manifold, Xp) + copyto!(B.manifold, X_k, Xp) + Y_k = allocate(B.manifold, VXM) + copyto!(B.manifold, Y_k, VXM) + Z_k = allocate(B.manifold, VXF) + copyto!(B.manifold, Z_k, VXF) + ϵ = 1 / m.L + for k in 1:(m.L) + p_kp1 = exp(B.manifold, p_k, ϵ * Y_k) + X_kp1 = parallel_transport_to(B.manifold, p_k, X_k .+ ϵ .* Z_k, p_kp1) + Y_kp1 = parallel_transport_to( + B.manifold, + p_k, + Y_k .+ ϵ .* riemann_tensor(B.manifold, p_k, X_k, Z_k, Y_k), + p_kp1, + ) + Z_kp1 = parallel_transport_to(B.manifold, p_k, Z_k, p_kp1) + copyto!(B.manifold, p_k, p_kp1) + copyto!(B.manifold, X_k, p_kp1, X_kp1) + copyto!(B.manifold, Y_k, p_kp1, Y_kp1) + copyto!(B.manifold, Z_k, p_kp1, Z_kp1) + end + copyto!(B.manifold, xq, p_k) + copyto!(B.manifold, Xq, xq, X_k) + return q +end + +function Base.show(io::IO, vb::VectorBundle) + return print(io, "VectorBundle($(vb.type.fiber), $(vb.manifold))") +end +Base.show(io::IO, vb::TangentBundle) = print(io, "TangentBundle($(vb.manifold))") + +function vector_transport_direction(M::VectorBundle, p, X, d) + return vector_transport_direction(M, p, X, d, M.vector_transport) +end + +function vector_transport_direction!(M::VectorBundle, Y, p, X, d) + return vector_transport_direction!(M, Y, p, X, d, M.vector_transport) +end + +function _vector_transport_direction!( + M::VectorBundle, + Y, + p, + X, + d, + m::FiberBundleProductVectorTransport, +) + VYM, VYF = submanifold_components(M.manifold, Y) + px, pVx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + dx, dVx = submanifold_components(M.manifold, d) + vector_transport_direction!(M.manifold, VYM, px, VXM, dx, m.method_point), + vector_transport_direction!(M.manifold, VYF, px, VXF, dx, m.method_fiber), + return Y +end + +@doc raw""" + vector_transport_to(M::VectorBundle, p, X, q, m::FiberBundleProductVectorTransport) + +Compute the vector transport the tangent vector `X`at `p` to `q` on the +[`VectorBundle`](@ref) `M` using the [`FiberBundleProductVectorTransport`](@ref) `m`. +""" +vector_transport_to( + ::VectorBundle, + ::Any, + ::Any, + ::Any, + ::FiberBundleProductVectorTransport, +) + +function vector_transport_to(M::VectorBundle, p, X, q) + return vector_transport_to(M, p, X, q, M.vector_transport) +end + +function vector_transport_to!(M::VectorBundle, Y, p, X, q) + return vector_transport_to!(M, Y, p, X, q, M.vector_transport) +end +function vector_transport_to!( + M::TangentBundle, + Y, + p, + X, + q, + m::FiberBundleProductVectorTransport, +) + px, pVx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + VYM, VYF = submanifold_components(M.manifold, Y) + qx, qVx = submanifold_components(M.manifold, q) + vector_transport_to!(M.manifold, VYM, px, VXM, qx, m.method_point) + vector_transport_to!(M.manifold, VYF, px, VXF, qx, m.method_fiber) + return Y +end + +function _vector_transport_direction( + M::VectorBundle, + p, + X, + d, + m::FiberBundleProductVectorTransport, +) + px, pVx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + dx, dVx = submanifold_components(M.manifold, d) + return ArrayPartition( + vector_transport_direction(M.manifold, px, VXM, dx, m.method_point), + vector_transport_direction(M.fiber, px, VXF, dx, m.method_fiber), + ) +end + +function _vector_transport_to( + M::VectorBundle, + p, + X, + q, + m::FiberBundleProductVectorTransport, +) + px, pVx = submanifold_components(M.manifold, p) + VXM, VXF = submanifold_components(M.manifold, X) + qx, qVx = submanifold_components(M.manifold, q) + return ArrayPartition( + vector_transport_to(M.manifold, px, VXM, qx, m.method_point), + vector_transport_to(M.manifold, px, VXF, qx, m.method_fiber), + ) +end + +Base.show(io::IO, vb::CotangentBundle) = print(io, "CotangentBundle($(vb.manifold))") diff --git a/src/manifolds/VectorFiber.jl b/src/manifolds/VectorFiber.jl index cf40944e8f..bfe9a66382 100644 --- a/src/manifolds/VectorFiber.jl +++ b/src/manifolds/VectorFiber.jl @@ -1,15 +1,6 @@ -const CotangentFiberType = VectorSpaceFiberType{CotangentSpaceType} - -const CotangentBundleFibers{M} = - BundleFibers{CotangentFiberType,M} where {M<:AbstractManifold} - -CotangentBundleFibers(M::AbstractManifold) = BundleFibers(CotangentFiber, M) - -const CotangentFiber = VectorSpaceFiberType{CotangentSpaceType}(CotangentSpace) - const CotangentSpaceAtPoint{𝔽,M} = - VectorSpaceAtPoint{𝔽,CotangentBundleFibers{M}} where {𝔽,M<:AbstractManifold{𝔽}} + Fiber{𝔽,CotangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} """ CotangentSpaceAtPoint(M::AbstractManifold, p) @@ -17,12 +8,10 @@ const CotangentSpaceAtPoint{𝔽,M} = Return an object of type [`VectorSpaceAtPoint`](@ref) representing cotangent space at `p`. """ -function CotangentSpaceAtPoint(M::AbstractManifold, p) - return VectorSpaceAtPoint(M, CotangentFiber, p) +function CotangentSpace(M::AbstractManifold, p) + return Fiber(M, CotangentSpaceType(), p) end -fiber_dimension(M::CotangentBundleFibers) = manifold_dimension(M.manifold) - """ TensorProductType(spaces::VectorSpaceType...) diff --git a/src/projected_distribution.jl b/src/projected_distribution.jl index 28dc84a457..d312606384 100644 --- a/src/projected_distribution.jl +++ b/src/projected_distribution.jl @@ -65,7 +65,7 @@ end Distributions.support(d::ProjectedPointDistribution) = MPointSupport(d.manifold) """ - ProjectedFVectorDistribution(type::VectorBundleFibers, p, d, project!) + ProjectedFVectorDistribution(type::VectorSpaceFiber, p, d, project!) Generates a random vector from ambient space of manifold `type.manifold` at point `p` and projects it to vector space of type `type` using function @@ -74,33 +74,23 @@ Generated arrays are of type `TResult`. """ struct ProjectedFVectorDistribution{ TResult, - TSpace<:VectorBundleFibers, - ManifoldPoint, + TSpace<:VectorSpaceFiber, TD<:Distribution, TProj, -} <: FVectorDistribution{TSpace,ManifoldPoint} +} <: FVectorDistribution{TSpace} type::TSpace - point::ManifoldPoint distribution::TD project!::TProj end function ProjectedFVectorDistribution( - type::VectorBundleFibers, - p, + type::VectorSpaceFiber, d::Distribution, project!, ::TResult, ) where {TResult} - return ProjectedFVectorDistribution{ - TResult, - typeof(type), - typeof(p), - typeof(d), - typeof(project!), - }( + return ProjectedFVectorDistribution{TResult,typeof(type),typeof(d),typeof(project!)}( type, - p, d, project!, ) @@ -110,8 +100,8 @@ function Random.rand( rng::AbstractRNG, d::ProjectedFVectorDistribution{TResult}, ) where {TResult} - X = convert(TResult, reshape(rand(rng, d.distribution), size(d.point))) - return d.project!(d.type, X, d.point, X) + X = convert(TResult, reshape(rand(rng, d.distribution), size(d.type.point))) + return d.project!(d.type.manifold, X, d.type.point, X) end function Distributions._rand!( @@ -131,9 +121,9 @@ projected to tangent space at `p`. """ function normal_tvector_distribution(M::AbstractManifold, p, σ) d = Distributions.MvNormal(zero(vec(p)), σ) - return ProjectedFVectorDistribution(TangentBundleFibers(M), p, d, project!, p) + return ProjectedFVectorDistribution(TangentSpace(M, p), d, project!, p) end function Distributions.support(tvd::ProjectedFVectorDistribution) - return FVectorSupport(tvd.type, tvd.point) + return FVectorSupport(tvd.type) end diff --git a/test/differentiation.jl b/test/differentiation.jl index df9835e4c5..35a9dca2d7 100644 --- a/test/differentiation.jl +++ b/test/differentiation.jl @@ -180,7 +180,7 @@ end q2 = [1.0, 0.0, 0.0] f2(X) = [0.0 0.0 0.0; 0.0 2.0 -1.0; 0.0 -3.0 1.0] * X - Tq2s2 = TangentSpaceAtPoint(s2, q2) + Tq2s2 = TangentSpace(s2, q2) @test isapprox( Manifolds.jacobian(Tq2s2, Tq2s2, f2, zero_vector(s2, q2), rb_onb_default), [2.0 -1.0; -3.0 1.0], @@ -188,7 +188,7 @@ end q3 = [0.0, 1.0, 0.0] f3(X) = [0.0 2.0 1.0; 0.0 0.0 0.0; 0.0 5.0 1.0] * X - Tq3s2 = TangentSpaceAtPoint(s2, q3) + Tq3s2 = TangentSpace(s2, q3) @test isapprox( Manifolds.jacobian(Tq2s2, Tq3s2, f3, zero_vector(s2, q2), rb_onb_default), [-2.0 -1.0; 5.0 1.0], diff --git a/test/manifolds/fiber.jl b/test/manifolds/fiber.jl index cf7c7bc2b8..7582e10aa1 100644 --- a/test/manifolds/fiber.jl +++ b/test/manifolds/fiber.jl @@ -8,9 +8,7 @@ struct TestVectorSpaceType <: VectorSpaceType end M = Sphere(2) @testset "tangent and cotangent space" begin p = [1.0, 0.0, 0.0] - t_p = TangentSpaceAtPoint(M, p) - t_p2 = TangentSpace(M, p) - @test t_p == t_p2 + t_p = TangentSpace(M, p) ct_p = CotangentSpaceAtPoint(M, p) t_ps = sprint(show, "text/plain", t_p) sp = sprint(show, "text/plain", p) @@ -30,15 +28,5 @@ struct TestVectorSpaceType <: VectorSpaceType end X = [0.0, 0.0, 1.0] @test embed(t_p, X) == X @test embed(t_p, X, X) == X - # generic vector space at - fiber = VectorBundleFibers(TestVectorSpaceType(), M) - X_p = Manifolds.FiberAtPoint(fiber, p) - X_ps = sprint(show, "text/plain", X_p) - fiber_s = sprint(show, "text/plain", fiber) - X_ps_test = "$(typeof(X_p))\nFiber:\n $(fiber_s)\nBase point:\n $(sp)" - @test X_ps == X_ps_test - @test_throws MethodError project(fiber, p, X) - @test_throws MethodError norm(fiber, p, X) - @test_throws MethodError distance(fiber, p, X, X) end end diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index c164511ff3..22d678e7c4 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -77,13 +77,11 @@ end sphere_tv_dist = Manifolds.normal_tvector_distribution(Ms, (@MVector [1.0, 0.0, 0.0]), 1.0) power_s1_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Ms1), - rand(power_s1_pt_dist), + TangentSpace(Ms1, rand(power_s1_pt_dist)), sphere_tv_dist, ) power_s2_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Ms2), - rand(power_s2_pt_dist), + TangentSpace(Ms2, rand(power_s2_pt_dist)), sphere_tv_dist, ) @@ -105,23 +103,19 @@ end ) rotations_tv_dist = Manifolds.normal_tvector_distribution(Mr, MMatrix(id_rot), 1.0) power_r1_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Mr1), - rand(power_r1_pt_dist), + TangentSpace(Mr1, rand(power_r1_pt_dist)), rotations_tv_dist, ) power_rn1_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Mrn1), - rand(power_rn1_pt_dist), + TangentSpace(Mrn1, rand(power_rn1_pt_dist)), rotations_tv_dist, ) power_r2_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Mr2), - rand(power_r2_pt_dist), + TangentSpace(Mr2, rand(power_r2_pt_dist)), rotations_tv_dist, ) power_rn2_tv_dist = Manifolds.PowerFVectorDistribution( - TangentBundleFibers(Mrn2), - rand(power_rn2_pt_dist), + TangentSpace(Mrn2, rand(power_rn2_pt_dist)), rotations_tv_dist, ) diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index 278f26e922..5418cf4d5a 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -423,13 +423,13 @@ using RecursiveArrayTools: ArrayPartition # make sure `get_coordinates` does not return an `ArrayPartition` p1 = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) X1 = ArrayPartition([1.0, 0.0, -1.0], [1.0, 0.0]) - Tp1Mse = TangentSpaceAtPoint(Mse, p1) + Tp1Mse = TangentSpace(Mse, p1) c = get_coordinates(Tp1Mse, p1, X1, DefaultOrthonormalBasis()) @test c isa Vector p1ap = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) X1ap = ArrayPartition([1.0, 0.0, -1.0], [1.0, 0.0]) - Tp1apMse = TangentSpaceAtPoint(Mse, p1ap) + Tp1apMse = TangentSpace(Mse, p1ap) cap = get_coordinates(Tp1apMse, p1ap, X1ap, DefaultOrthonormalBasis()) @test cap isa Vector end diff --git a/test/manifolds/vector_bundle.jl b/test/manifolds/vector_bundle.jl index 9c0cdc5d00..2857bf0072 100644 --- a/test/manifolds/vector_bundle.jl +++ b/test/manifolds/vector_bundle.jl @@ -45,13 +45,11 @@ struct TestVectorSpaceType <: VectorSpaceType end @test default_vector_transport_method(TB) isa Manifolds.FiberBundleProductVectorTransport CTB = CotangentBundle(M) - CTBF = CotangentBundleFibers(M) @test sprint(show, CTB) == "CotangentBundle(Sphere(2, ℝ))" @test sprint(show, VectorBundle(TestVectorSpaceType(), M)) == "VectorBundle(TestVectorSpaceType(), Sphere(2, ℝ))" - @test sprint(show, CTBF) == "VectorBundleFibers(CotangentSpace, Sphere(2, ℝ))" - @test Manifolds.fiber_dimension(CTBF) == 2 - @test Manifolds.fiber_dimension(M, ManifoldsBase.CotangentSpace) == 2 + + @test Manifolds.fiber_dimension(M, ManifoldsBase.CotangentSpaceType()) == 2 @test base_manifold(TangentBundle(M)) == M end @@ -61,7 +59,7 @@ struct TestVectorSpaceType <: VectorSpaceType end for T in types p = convert(T, [1.0, 0.0, 0.0]) - TpM = TangentSpaceAtPoint(M, p) + TpM = TangentSpace(M, p) @test is_flat(TpM) @testset "Type $T" begin @@ -175,15 +173,17 @@ struct TestVectorSpaceType <: VectorSpaceType end @testset "tensor product" begin TT = Manifolds.TensorProductType(TangentSpace, TangentSpace) @test sprint(show, TT) == "TensorProductType(TangentSpace, TangentSpace)" - @test vector_space_dimension(VectorBundleFibers(TT, Sphere(2))) == 4 - @test vector_space_dimension(VectorBundleFibers(TT, Sphere(3))) == 9 - @test base_manifold(VectorBundleFibers(TT, Sphere(2))) == M - @test sprint(show, VectorBundleFibers(TT, Sphere(2))) == - "VectorBundleFibers(TensorProductType(TangentSpace, TangentSpace), Sphere(2, ℝ))" + @test vector_space_dimension(VectorSpaceFiber(TT, Sphere(2), [1.0, 0.0, 0.0])) == 4 + @test vector_space_dimension( + VectorSpaceFiber(TT, Sphere(3), [1.0, 0.0, 0.0, 0.0]), + ) == 9 + @test base_manifold(VectorSpaceFiber(TT, Sphere(2), [1.0, 0.0, 0.0])) == M + @test sprint(show, VectorSpaceFiber(TT, Sphere(2), [1.0, 0.0, 0.0])) == + "VectorSpaceFiber(TensorProductType(TangentSpace, TangentSpace), Sphere(2, ℝ))" end @testset "Error messages" begin - vbf = VectorBundleFibers(TestVectorSpaceType(), Euclidean(3)) + vbf = VectorSpaceFiber(TestVectorSpaceType(), Euclidean(3), [1.0, 0.0, 0.0]) @test_throws MethodError inner(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) @test_throws MethodError project!(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) @test_throws MethodError zero_vector!(vbf, [1, 2, 3], [1, 2, 3]) @@ -227,13 +227,4 @@ struct TestVectorSpaceType <: VectorSpaceType end @test is_flat(M) @test injectivity_radius(M) == Inf end - - @testset "Weingarten Map" begin - p0 = [1.0, 0.0, 0.0] - M = TangentSpaceAtPoint(Sphere(2), p0) - p = [0.0, 1.0, 1.0] - X = [0.0, 1.0, 0.0] - V = [1.0, 0.0, 0.0] - @test Weingarten(M, p, X, V) == zero_vector(M, p) - end end diff --git a/test/metric.jl b/test/metric.jl index 8d145df19d..dba1badf06 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -614,8 +614,8 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, cofY = flat(M, p, fY) @test coX(X) ≈ norm(M, p, X)^2 @test coY(X) ≈ inner(M, p, X, Y) - cotspace = CotangentBundleFibers(M) - cotspace2 = CotangentBundleFibers(MM) + cotspace = CotangentSpace(M, p) + cotspace2 = CotangentSpace(MM, p) @test coX.X ≈ X @test inner(M, p, X, Y) ≈ inner(cotspace, p, coX, coY) @test inner(MM, p, fX, fY) ≈ inner(cotspace, p, coX, coY) diff --git a/test/notation.jl b/test/notation.jl index ea6df530f5..294cc39a7c 100644 --- a/test/notation.jl +++ b/test/notation.jl @@ -7,7 +7,7 @@ include("utils.jl") @test (2 * p1 ∈ M) == is_point(M, 2 * p1) X1 = [0.0, 1.0, 0.0] X2 = [1.0, 0.0, 0.0] - TpM = TangentSpaceAtPoint(M, p1) + TpM = TangentSpace(M, p1) @test (X1 ∈ TpM) == is_vector(M, p1, X1) @test (X2 ∈ TpM) == is_vector(M, p1, X2) end From 561b0b5b4033cb3e28e85d432c30d5ee404f4bc6 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 11 Oct 2023 12:50:14 +0200 Subject: [PATCH 38/81] some updates --- ext/ManifoldsTestExt/tests_general.jl | 3 +- src/Manifolds.jl | 2 +- src/atlases.jl | 4 +- src/manifolds/Fiber.jl | 70 ----------------- src/manifolds/FiberBundle.jl | 109 ++++++++++++-------------- src/manifolds/VectorBundle.jl | 19 ++--- test/manifolds/euclidean.jl | 8 +- test/manifolds/sphere.jl | 2 +- test/manifolds/vector_bundle.jl | 13 +-- test/metric.jl | 12 +-- 10 files changed, 77 insertions(+), 165 deletions(-) diff --git a/ext/ManifoldsTestExt/tests_general.jl b/ext/ManifoldsTestExt/tests_general.jl index 23824aaf67..8ff647d8cd 100644 --- a/ext/ManifoldsTestExt/tests_general.jl +++ b/ext/ManifoldsTestExt/tests_general.jl @@ -810,7 +810,8 @@ function test_manifold( Test.@testset "tangent vector distributions" begin for tvd in tvector_distributions supp = Manifolds.support(tvd) - Test.@test supp isa Manifolds.FVectorSupport{<:TangentSpace{ℝ,typeof(M)}} + Test.@test supp isa + Manifolds.FVectorSupport{<:TangentSpace{number_system(M),typeof(M)}} for _ in 1:10 randtv = rand(tvd) atol = rand_tvector_atol_multiplier * find_eps(randtv) diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 7fdaa69cc9..9516743e77 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -672,7 +672,7 @@ export AbstractDecoratorManifold export IsIsometricEmbeddedManifold, IsEmbeddedManifold, IsEmbeddedSubmanifold export IsDefaultMetric, IsDefaultConnection, IsMetricManifold, IsConnectionManifold export ValidationManifold, ValidationMPoint, ValidationTVector, ValidationCoTVector -export CotangentBundle, CotangentSpace, FVector +export FiberBundle, CotangentBundle, CotangentSpace, FVector export AbstractPowerManifold, AbstractPowerRepresentation, ArrayPowerRepresentation, diff --git a/src/atlases.jl b/src/atlases.jl index 5bb5f1ce4b..a9ebc7a0ae 100644 --- a/src/atlases.jl +++ b/src/atlases.jl @@ -380,7 +380,7 @@ struct InducedBasis{𝔽,VST<:VectorSpaceType,TA<:AbstractAtlas,TI} <: AbstractB end """ - induced_basis(::AbstractManifold, A::AbstractAtlas, i, VST::VectorSpaceType = TangentSpace) + induced_basis(::AbstractManifold, A::AbstractAtlas, i, VST::VectorSpaceType = TangentSpaceType()) Get the basis induced by chart with index `i` from an [`AbstractAtlas`](@ref) `A` of vector space of type `vs`. Returns an object of type [`InducedBasis`](@ref). @@ -417,7 +417,7 @@ function dual_basis( ::Any, B::InducedBasis{𝔽,CotangentSpaceType}, ) where {𝔽} - return induced_basis(M, B.A, B.i, TangentSpace) + return induced_basis(M, B.A, B.i, TangentSpaceType()) end function ManifoldsBase._get_coordinates(M::AbstractManifold, p, X, B::InducedBasis) diff --git a/src/manifolds/Fiber.jl b/src/manifolds/Fiber.jl index 069a3fac15..8b13789179 100644 --- a/src/manifolds/Fiber.jl +++ b/src/manifolds/Fiber.jl @@ -1,71 +1 @@ -""" - BundleFibers(fiber::FiberType, M::AbstractManifold) - -Type representing a family of fibers of a fiber bundle over `M` -with fiber of type `fiber`. In contrast with `FiberBundle`, operations -on `BundleFibers` expect point-like and fiber-like parts to be -passed separately instead of being bundled together. It can be thought of -as a representation of fibers from a fiber bundle but without -storing the point at which a fiber is attached (which is specified -separately in various functions). -""" -struct BundleFibers{TF<:FiberType,TM<:AbstractManifold} - fiber::TF - manifold::TM -end - -""" - allocate_result(B::BundleFibers, f, x...) - -Allocates an array for the result of function `f` that is -an element of the fiber space of type `B.fiber` on manifold `B.manifold` -and arguments `x...` for implementing the non-modifying operation -using the modifying operation. -""" -@inline function allocate_result(B::BundleFibers, f::TF, x...) where {TF} - if length(x) == 0 - # TODO: this may be incorrect when point and tangent vector representation are - # different for the manifold but there is no easy and generic way around that - return allocate_result(B.manifold, f) - else - T = allocate_result_type(B, f, x) - return allocate(x[1], T) - end -end - -""" - allocate_result_type(B::BundleFibers, f, args::NTuple{N,Any}) where N - -Return type of element of the array that will represent the result of -function `f` for representing an operation with result in the fiber `fiber` -for manifold `M` on given arguments (passed at a tuple). -""" -@inline function allocate_result_type( - ::BundleFibers, - f::TF, - args::NTuple{N,Any}, -) where {TF,N} - return typeof(mapreduce(eti -> one(number_eltype(eti)), +, args)) -end - -base_manifold(B::BundleFibers) = base_manifold(B.manifold) - -function fiber_dimension(B::BundleFibers) - return fiber_dimension(B.manifold, B.fiber) -end - -function Base.show(io::IO, fiber::BundleFibers) - return print(io, "BundleFibers($(fiber.fiber), $(fiber.manifold))") -end - -""" - zero_vector(B::BundleFibers, p) - -Compute the zero vector from the vector space of type `B.fiber` at point `p` -from manifold `B.manifold`. -""" -function zero_vector(B::BundleFibers, p) - X = allocate_result(B, zero_vector, p) - return zero_vector!(B, X, p) -end diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index 4f43a1ce92..c90507036c 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -67,17 +67,9 @@ struct FiberBundle{ } <: AbstractManifold{𝔽} type::TF manifold::TM - fiber::BundleFibers{TF,TM} vector_transport::TVT end -function FiberBundle( - fiber::TVS, - M::TM, - vtm::FiberBundleProductVectorTransport, -) where {TVS<:FiberType,TM<:AbstractManifold{𝔽}} where {𝔽} - return FiberBundle{𝔽,TVS,TM,typeof(vtm)}(fiber, M, BundleFibers(fiber, M), vtm) -end function FiberBundle(fiber::FiberType, M::AbstractManifold) vtmm = vector_bundle_transport(fiber, M) vtbm = FiberBundleProductVectorTransport(vtmm, vtmm) @@ -157,6 +149,16 @@ Return the manifold the [`FiberBundle`](@ref)s is build on. """ base_manifold(B::FiberBundle) = base_manifold(B.manifold) +@doc raw""" + bundle_transport_to(B::FiberBundle, p, X, q) + +Given a fiber bundle ``B=F \mathcal M``, points ``p, q\in\mathcal M``, an element ``X`` of +the fiber over ``p``, transport ``X`` to fiber over ``q``. + +Exact meaning of the operation depends on the fiber bundle, or may even be undefined. +""" +bundle_transport_to(B::FiberBundle, p, X, q) + """ bundle_projection(B::FiberBundle, p) @@ -167,32 +169,21 @@ of `p` is attached. bundle_projection(B::FiberBundle, p) = submanifold_component(B.manifold, p, Val(1)) function get_basis(M::FiberBundle, p, B::AbstractBasis) - xp1 = submanifold_component(p, Val(1)) + xp1, xp2 = submanifold_components(M, p) base_basis = get_basis(M.manifold, xp1, B) - fiber_basis = get_basis(M.fiber, xp1, B) + F = Fiber(M.manifold, xp1, M.type) + fiber_basis = get_basis(F, xp2, B) return CachedBasis(B, FiberBundleBasisData(base_basis, fiber_basis)) end function get_basis(M::FiberBundle, p, B::CachedBasis) return invoke(get_basis, Tuple{AbstractManifold,Any,CachedBasis}, M, p, B) end -function get_basis(M::FiberBundle, p, B::DiagonalizingOrthonormalBasis) - xp1 = submanifold_component(p, Val(1)) - bv1 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(1))) - b1 = get_basis(M.manifold, xp1, bv1) - bv2 = DiagonalizingOrthonormalBasis(submanifold_component(B.frame_direction, Val(2))) - b2 = get_basis(M.fiber, xp1, bv2) - return CachedBasis(B, FiberBundleBasisData(b1, b2)) -end - function get_coordinates(M::FiberBundle, p, X, B::AbstractBasis) px, Vx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) - n = manifold_dimension(M.manifold) - return vcat( - get_coordinates(M.manifold, px, VXM, B), - get_coordinates(M.fiber, px, VXF, B), - ) + F = Fiber(M.manifold, xp1, M.type) + return vcat(get_coordinates(M.manifold, px, VXM, B), get_coordinates(F, Vx, VXF, B)) end function get_coordinates!(M::FiberBundle, Y, p, X, B::AbstractBasis) @@ -200,7 +191,8 @@ function get_coordinates!(M::FiberBundle, Y, p, X, B::AbstractBasis) VXM, VXF = submanifold_components(M.manifold, X) n = manifold_dimension(M.manifold) get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B) - get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B) + F = Fiber(M.manifold, px, M.type) + get_coordinates!(F, view(Y, (n + 1):length(Y)), Vx, VXF, B) return Y end @@ -212,9 +204,10 @@ function get_coordinates( ) where {𝔽} px, Vx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) + F = Fiber(M.manifold, xp1, M.type) return vcat( get_coordinates(M.manifold, px, VXM, B.data.base_basis), - get_coordinates(M.fiber, px, VXF, B.data.fiber_basis), + get_coordinates(F, Vx, VXF, B.data.fiber_basis), ) end @@ -228,16 +221,19 @@ function get_coordinates!( px, Vx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) n = manifold_dimension(M.manifold) + F = Fiber(M.manifold, xp1, M.type) get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B.data.base_basis) - get_coordinates!(M.fiber, view(Y, (n + 1):length(Y)), px, VXF, B.data.fiber_basis) + get_coordinates!(F, view(Y, (n + 1):length(Y)), Vx, VXF, B.data.fiber_basis) return Y end function get_vector!(M::FiberBundle, Y, p, X, B::AbstractBasis) n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - get_vector!(M.manifold, submanifold_component(Y, Val(1)), xp1, X[1:n], B) - get_vector!(M.fiber, submanifold_component(Y, Val(2)), xp1, X[(n + 1):end], B) + xp1, xp2 = submanifold_components(M, p) + Yp1, Yp2 = submanifold_components(M, Y) + F = Fiber(M.manifold, xp1, M.type) + get_vector!(M.manifold, Yp1, xp1, X[1:n], B) + get_vector!(F, Yp2, xp2, X[(n + 1):end], B) return Y end @@ -249,44 +245,30 @@ function get_vector!( B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, ) where {𝔽} n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) - get_vector!( - M.manifold, - submanifold_component(Y, Val(1)), - xp1, - X[1:n], - B.data.base_basis, - ) - get_vector!( - M.fiber, - submanifold_component(Y, Val(2)), - xp1, - X[(n + 1):end], - B.data.fiber_basis, - ) + xp1, xp2 = submanifold_components(M, p) + Yp1, Yp2 = submanifold_components(M, Y) + F = Fiber(M.manifold, M.type, xp1) + get_vector!(M.manifold, Yp1, xp1, X[1:n], B.data.base_basis) + get_vector!(F, Yp2, xp2, X[(n + 1):end], B.data.fiber_basis) return Y end -function get_vectors(M::BundleFibers, p, B::CachedBasis) - return get_vectors(M.manifold, p, B) -end - function _isapprox(B::FiberBundle, p, q; kwargs...) xp, Vp = submanifold_components(B.manifold, p) xq, Vq = submanifold_components(B.manifold, q) return isapprox(B.manifold, xp, xq; kwargs...) && - isapprox(FiberAtPoint(B.fiber, xp), Vp, Vq; kwargs...) + isapprox(Fiber(B.manifold, xp, B.type), Vp, Vq; kwargs...) end function _isapprox(B::FiberBundle, p, X, Y; kwargs...) px, Vx = submanifold_components(B.manifold, p) VXM, VXF = submanifold_components(B.manifold, X) VYM, VYF = submanifold_components(B.manifold, Y) return isapprox(B.manifold, VXM, VYM; kwargs...) && - isapprox(FiberAtPoint(B.fiber, px), VXF, VYF; kwargs...) + isapprox(Fiber(B.manifold, px, B.type), Vx, VXF, VYF; kwargs...) end function manifold_dimension(B::FiberBundle) - return manifold_dimension(B.manifold) + fiber_dimension(B.fiber) + return manifold_dimension(B.manifold) + fiber_dimension(B.manifold, B.type) end function Random.rand!(M::FiberBundle, pX; vector_at=nothing) @@ -296,11 +278,11 @@ function Random.rand!(rng::AbstractRNG, M::FiberBundle, pX; vector_at=nothing) pXM, pXF = submanifold_components(M.manifold, pX) if vector_at === nothing rand!(rng, M.manifold, pXM) - rand!(rng, FiberAtPoint(M.fiber, pXM), pXF) + rand!(rng, Fiber(M.manifold, pXM, M.type), pXF) else vector_atM, vector_atF = submanifold_components(M.manifold, vector_at) rand!(rng, M.manifold, pXM; vector_at=vector_atM) - rand!(rng, FiberAtPoint(M.fiber, pXM), pXF; vector_at=vector_atF) + rand!(rng, Fiber(M.manifold, pXM, M.type), pXF; vector_at=vector_atF) end return pX end @@ -339,10 +321,11 @@ end function get_vector(M::FiberBundle, p, X, B::AbstractBasis) n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) + xp1, xp2 = submanifold_components(M, p) + F = Fiber(M.manifold, xp1, M.type) return ArrayPartition( get_vector(M.manifold, xp1, X[1:n], B), - get_vector(M.fiber, xp1, X[(n + 1):end], B), + get_vector(F, xp2, X[(n + 1):end], B), ) end function get_vector( @@ -352,10 +335,11 @@ function get_vector( B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, ) where {𝔽} n = manifold_dimension(M.manifold) - xp1 = submanifold_component(p, Val(1)) + xp1, xp2 = submanifold_components(M, p) + F = Fiber(M.manifold, M.type, xp1) 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.fiber_basis), + get_vector(F, xp2, X[(n + 1):end], B.data.fiber_basis), ) end @@ -364,14 +348,15 @@ function get_vectors( p::ArrayPartition, B::CachedBasis{𝔽,<:AbstractBasis{𝔽},<:FiberBundleBasisData}, ) where {𝔽} - xp1 = submanifold_component(p, Val(1)) + xp1, xp2 = submanifold_components(M, p) zero_m = zero_vector(M.manifold, xp1) zero_f = zero_vector(M.fiber, xp1) vs = typeof(ArrayPartition(zero_m, zero_f))[] + F = Fiber(M.manifold, M.type, xp1) for bv in get_vectors(M.manifold, xp1, B.data.base_basis) push!(vs, ArrayPartition(bv, zero_f)) end - for bv in get_vectors(M.fiber, xp1, B.data.fiber_basis) + for bv in get_vectors(F, xp2, B.data.fiber_basis) push!(vs, ArrayPartition(zero_m, bv)) end return vs @@ -413,6 +398,10 @@ component, respectively. end end +function Base.show(io::IO, B::FiberBundle) + return print(io, "FiberBundle($(B.type), $(B.manifold), $(B.vector_transport))") +end + @inline function Base.view(x::ArrayPartition, M::FiberBundle, s::Symbol) (s === :point) && return x.x[1] (s === :vector || s === :fiber) && return x.x[2] diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 66d524020f..3f15b25e15 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -64,6 +64,10 @@ function CotangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTrans return VectorBundle(CotangentSpaceType(), M, vtm) end +function bundle_transport_to!(B::TangentBundle, Y, p, X, q) + return vector_transport_to!(B.manifold, Y, p, X, q, B.vector_transport.method_fiber) +end + function default_inverse_retraction_method(::TangentBundle) return FiberBundleInverseProductRetraction() end @@ -146,7 +150,7 @@ function inverse_retract_product!(B::VectorBundle, X, p, q) py, Vy = submanifold_components(B.manifold, q) VXM, VXF = submanifold_components(B.manifold, X) log!(B.manifold, VXM, px, py) - vector_transport_to!(B.fiber, VXF, py, Vy, px, B.vector_transport.method_fiber) + bundle_transport_to!(B, VXF, py, Vy, px) copyto!(VXF, VXF - Vx) return X end @@ -212,16 +216,6 @@ function project!(B::VectorBundle, Y, p, X) return Y end -""" - project(B::BundleFibers, p, X) - -Project vector `X` from the vector space of type `B.fiber` at point `p`. -""" -function project(B::BundleFibers, p, X) - Y = allocate_result(B, project, p, X) - return project!(B, Y, p, X) -end - function _retract(M::VectorBundle, p, X, t::Number, ::FiberBundleProductRetraction) return retract_product(M, p, X, t) end @@ -296,9 +290,6 @@ function retract_sasaki!(B::TangentBundle, q, p, X, t::Number, m::SasakiRetracti return q end -function Base.show(io::IO, vb::VectorBundle) - return print(io, "VectorBundle($(vb.type.fiber), $(vb.manifold))") -end Base.show(io::IO, vb::TangentBundle) = print(io, "TangentBundle($(vb.manifold))") function vector_transport_direction(M::VectorBundle, p, X, d) diff --git a/test/manifolds/euclidean.jl b/test/manifolds/euclidean.jl index 8e7742f94c..b263d31195 100644 --- a/test/manifolds/euclidean.jl +++ b/test/manifolds/euclidean.jl @@ -24,7 +24,7 @@ using FiniteDifferences @test is_flat(Ec) p = zeros(3) A = Manifolds.RetractionAtlas() - B = induced_basis(EM, A, p, TangentSpace) + B = induced_basis(EM, A, p, TangentSpaceType()) @test det_local_metric(EM, p, B) == one(eltype(p)) @test log_local_metric_density(EM, p, B) == zero(eltype(p)) @test project!(E, p, p) == p @@ -264,7 +264,7 @@ using FiniteDifferences p = zeros(2) A = Manifolds.get_default_atlas(M) i = Manifolds.get_chart_index(M, A, p) - B = Manifolds.induced_basis(M, A, i, TangentSpace) + B = Manifolds.induced_basis(M, A, i, TangentSpaceType()) C1 = christoffel_symbols_first(M, p, B) @test size(C1) == (2, 2, 2) @test norm(C1) ≈ 0.0 atol = 1e-13 @@ -294,7 +294,7 @@ using FiniteDifferences p = zeros(3) M = DefaultManifold() TpM = TangentSpace(M, p) - B = induced_basis(M, Manifolds.get_default_atlas(M), p, TangentSpace) + B = induced_basis(M, Manifolds.get_default_atlas(M), p, TangentSpaceType()) MM = MetricManifold(M, EuclideanMetric()) @test local_metric(MM, p, B) == Diagonal(ones(3)) @test inverse_local_metric(MM, p, B) == Diagonal(ones(3)) @@ -302,7 +302,7 @@ using FiniteDifferences DB1 = dual_basis(MM, p, B) @test DB1 isa InducedBasis @test DB1.vs isa ManifoldsBase.CotangentSpaceType - DB2 = induced_basis(M, Manifolds.get_default_atlas(M), p, CotangentSpace) + DB2 = induced_basis(M, Manifolds.get_default_atlas(M), p, CotangentSpaceType()) @test DB2 isa InducedBasis @test DB2.vs isa ManifoldsBase.CotangentSpaceType DDB = dual_basis(MM, p, DB2) diff --git a/test/manifolds/sphere.jl b/test/manifolds/sphere.jl index ffb9585744..4ef5e70340 100644 --- a/test/manifolds/sphere.jl +++ b/test/manifolds/sphere.jl @@ -203,7 +203,7 @@ using ManifoldsBase: TFVector p3 ./= norm(p3) X2 = log(M, p, p2) X3 = log(M, p, p3) - B = induced_basis(M, A, i, TangentSpace) + B = induced_basis(M, A, i, TangentSpaceType()) X2B = get_coordinates(M, p, X2, B) X3B = get_coordinates(M, p, X3, B) diff --git a/test/manifolds/vector_bundle.jl b/test/manifolds/vector_bundle.jl index 2857bf0072..3bb0d7f97b 100644 --- a/test/manifolds/vector_bundle.jl +++ b/test/manifolds/vector_bundle.jl @@ -46,8 +46,8 @@ struct TestVectorSpaceType <: VectorSpaceType end Manifolds.FiberBundleProductVectorTransport CTB = CotangentBundle(M) @test sprint(show, CTB) == "CotangentBundle(Sphere(2, ℝ))" - @test sprint(show, VectorBundle(TestVectorSpaceType(), M)) == - "VectorBundle(TestVectorSpaceType(), Sphere(2, ℝ))" + @test sprint(show, FiberBundle(TestVectorSpaceType(), M)) == + "FiberBundle(TestVectorSpaceType(), Sphere(2, ℝ), Manifolds.FiberBundleProductVectorTransport{ParallelTransport, ParallelTransport}(ParallelTransport(), ParallelTransport()))" @test Manifolds.fiber_dimension(M, ManifoldsBase.CotangentSpaceType()) == 2 @test base_manifold(TangentBundle(M)) == M @@ -171,19 +171,20 @@ struct TestVectorSpaceType <: VectorSpaceType end VectorBundle{ℝ,Manifolds.CotangentSpaceType,Sphere{2,ℝ}} @testset "tensor product" begin - TT = Manifolds.TensorProductType(TangentSpace, TangentSpace) - @test sprint(show, TT) == "TensorProductType(TangentSpace, TangentSpace)" + TT = Manifolds.TensorProductType(TangentSpaceType(), TangentSpaceType()) + @test sprint(show, TT) == + "TensorProductType(TangentSpaceType(), TangentSpaceType())" @test vector_space_dimension(VectorSpaceFiber(TT, Sphere(2), [1.0, 0.0, 0.0])) == 4 @test vector_space_dimension( VectorSpaceFiber(TT, Sphere(3), [1.0, 0.0, 0.0, 0.0]), ) == 9 @test base_manifold(VectorSpaceFiber(TT, Sphere(2), [1.0, 0.0, 0.0])) == M @test sprint(show, VectorSpaceFiber(TT, Sphere(2), [1.0, 0.0, 0.0])) == - "VectorSpaceFiber(TensorProductType(TangentSpace, TangentSpace), Sphere(2, ℝ))" + "VectorSpaceFiber(TensorProductType(TangentSpaceType(), TangentSpaceType()), Sphere(2, ℝ))" end @testset "Error messages" begin - vbf = VectorSpaceFiber(TestVectorSpaceType(), Euclidean(3), [1.0, 0.0, 0.0]) + vbf = Fiber(TestVectorSpaceType(), Euclidean(3), [1.0, 0.0, 0.0]) @test_throws MethodError inner(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) @test_throws MethodError project!(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) @test_throws MethodError zero_vector!(vbf, [1, 2, 3], [1, 2, 3]) diff --git a/test/metric.jl b/test/metric.jl index dba1badf06..778d4e79ee 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -295,7 +295,7 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, p = [3, 4] i = get_chart_index(M, A, p) - B = induced_basis(M, A, i, TangentSpace) + B = induced_basis(M, A, i, TangentSpaceType()) @test_throws MethodError local_metric(M, p, B) end @testset "scaled Euclidean metric" begin @@ -317,7 +317,7 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, @test metric(M) === g i_zeros = get_chart_index(M, A, zeros(3)) - B_i_zeros = induced_basis(M, A, i_zeros, TangentSpace) + B_i_zeros = induced_basis(M, A, i_zeros, TangentSpaceType()) @test_throws MethodError local_metric_jacobian(E, zeros(3), B_i_zeros) @test_throws MethodError christoffel_symbols_second_jacobian(E, zeros(3), B_i_zeros) @@ -325,7 +325,7 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, p, X, Y = vtype(randn(n)), vtype(randn(n)), vtype(randn(n)) chart_p = get_chart_index(M, A, p) - B_chart_p = induced_basis(M, A, chart_p, TangentSpace) + B_chart_p = induced_basis(M, A, chart_p, TangentSpaceType()) @test check_point(M, p) == check_point(E, p) @test check_vector(M, p, X) == check_vector(E, p, X) @@ -404,7 +404,7 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, for vtype in (Vector, MVector{n}) p = vtype([θ, ϕ]) chart_p = get_chart_index(M, A, p) - B_p = induced_basis(M, A, chart_p, TangentSpace) + B_p = induced_basis(M, A, chart_p, TangentSpaceType()) G = Diagonal(vtype([1, sin(θ)^2])) .* r^2 invG = Diagonal(vtype([1, 1 / sin(θ)^2])) ./ r^2 X, Y = normalize(randn(n)), normalize(randn(n)) @@ -511,7 +511,7 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, X = [0.5, 0.7, 0.11] chart_p = get_chart_index(M, A, p) - B_p = induced_basis(M, A, chart_p, TangentSpace) + B_p = induced_basis(M, A, chart_p, TangentSpaceType()) fX = ManifoldsBase.TFVector(X, B_p) fY = ManifoldsBase.TFVector(Y, B_p) @@ -550,7 +550,7 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, A = Manifolds.get_default_atlas(MM2) chart_p = get_chart_index(MM2, A, p) - B_p = induced_basis(MM2, A, chart_p, TangentSpace) + B_p = induced_basis(MM2, A, chart_p, TangentSpaceType()) @test_throws MethodError local_metric(MM2, p, B_p) @test_throws MethodError local_metric_jacobian(MM2, p, B_p) @test_throws MethodError christoffel_symbols_second_jacobian(MM2, p, B_p) From b2dfeeef8ceca2940ba36ab1275b4f3c0ba0404d Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 11 Oct 2023 22:31:19 +0200 Subject: [PATCH 39/81] fixes --- ext/ManifoldsTestExt/tests_general.jl | 4 +-- src/Manifolds.jl | 8 +++--- src/atlases.jl | 2 +- src/manifolds/ConnectionManifold.jl | 2 +- src/manifolds/FiberBundle.jl | 35 +++++++++++++++------------ src/manifolds/PowerManifold.jl | 6 ++--- src/manifolds/ProductManifold.jl | 2 +- src/manifolds/VectorBundle.jl | 13 ++++++---- test/manifolds/vector_bundle.jl | 15 ++++-------- 9 files changed, 45 insertions(+), 42 deletions(-) diff --git a/ext/ManifoldsTestExt/tests_general.jl b/ext/ManifoldsTestExt/tests_general.jl index 8ff647d8cd..d99fa69160 100644 --- a/ext/ManifoldsTestExt/tests_general.jl +++ b/ext/ManifoldsTestExt/tests_general.jl @@ -388,9 +388,9 @@ function test_manifold( for p in pts X = zero_vector(M, p) mts = TangentSpace(M, p) - Test.@test isapprox(M, p, X, zero_vector(mts, p)) + Test.@test isapprox(M, p, X, zero_vector(mts, X)) if is_mutating - zero_vector!(mts, X, p) + zero_vector!(mts, X, X) Test.@test isapprox(M, p, X, zero_vector(M, p)) end end diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 9516743e77..2d08d7f421 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -139,6 +139,7 @@ import ManifoldsBase: retract_polar!, retract_project!, retract_qr!, + retract_sasaki!, retract_softmax!, riemann_tensor, riemann_tensor!, @@ -261,6 +262,7 @@ using ManifoldsBase: ProductManifold, ProductMetric, ProductRetraction, + ProductVectorTransport, ProjectedOrthonormalBasis, ProjectionInverseRetraction, ProjectionRetraction, @@ -282,6 +284,7 @@ using ManifoldsBase: TFVector, TVector, TypeParameter, + ValidationCoTVector, ValidationManifold, ValidationMPoint, ValidationTVector, @@ -672,7 +675,7 @@ export AbstractDecoratorManifold export IsIsometricEmbeddedManifold, IsEmbeddedManifold, IsEmbeddedSubmanifold export IsDefaultMetric, IsDefaultConnection, IsMetricManifold, IsConnectionManifold export ValidationManifold, ValidationMPoint, ValidationTVector, ValidationCoTVector -export FiberBundle, CotangentBundle, CotangentSpace, FVector +export Fiber, FiberBundle, CotangentBundle, CotangentSpace, FVector export AbstractPowerManifold, AbstractPowerRepresentation, ArrayPowerRepresentation, @@ -689,8 +692,7 @@ export AbstractVectorTransportMethod, DifferentiatedRetractionVectorTransport, ParallelTransport, ProjectedPointDistribution export PoleLadderTransport, SchildsLadderTransport export ProductVectorTransport -export AbstractAffineConnection, - AbstractConnectionManifold, ConnectionManifold, LeviCivitaConnection +export AbstractAffineConnection, ConnectionManifold, LeviCivitaConnection export AbstractCartanSchoutenConnection, CartanSchoutenMinus, CartanSchoutenPlus, CartanSchoutenZero export MetricManifold diff --git a/src/atlases.jl b/src/atlases.jl index a9ebc7a0ae..c5f87e5069 100644 --- a/src/atlases.jl +++ b/src/atlases.jl @@ -393,7 +393,7 @@ function induced_basis( ::AbstractManifold{𝔽}, A::AbstractAtlas, i, - VST::VectorSpaceType=TangentSpace, + VST::VectorSpaceType=TangentSpaceType(), ) where {𝔽} return InducedBasis{𝔽,typeof(VST),typeof(A),typeof(i)}(VST, A, i) end diff --git a/src/manifolds/ConnectionManifold.jl b/src/manifolds/ConnectionManifold.jl index 1f234ec9cc..c8fa3a5aad 100644 --- a/src/manifolds/ConnectionManifold.jl +++ b/src/manifolds/ConnectionManifold.jl @@ -372,7 +372,7 @@ function solve_exp_ode end @doc raw""" solve_exp_ode( - M::AbstractConnectionManifold, + M::ConnectionManifold, p, X, t::Number, diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index c90507036c..f86f6989fe 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -35,7 +35,7 @@ struct FiberBundleProductVectorTransport{ method_fiber::TMV end function FiberBundleProductVectorTransport( - M::AbstractManifold=DefaultManifold(); + M::AbstractManifold=ManifoldsBase.DefaultManifold(); vector_tansport_method_point=default_vector_transport_method(M), vector_transport_method_fiber=default_vector_transport_method(M), ) @@ -182,7 +182,7 @@ end function get_coordinates(M::FiberBundle, p, X, B::AbstractBasis) px, Vx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) - F = Fiber(M.manifold, xp1, M.type) + F = Fiber(M.manifold, px, M.type) return vcat(get_coordinates(M.manifold, px, VXM, B), get_coordinates(F, Vx, VXF, B)) end @@ -204,7 +204,7 @@ function get_coordinates( ) where {𝔽} px, Vx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) - F = Fiber(M.manifold, xp1, M.type) + F = Fiber(M.manifold, px, M.type) return vcat( get_coordinates(M.manifold, px, VXM, B.data.base_basis), get_coordinates(F, Vx, VXF, B.data.fiber_basis), @@ -221,7 +221,7 @@ function get_coordinates!( px, Vx = submanifold_components(M.manifold, p) VXM, VXF = submanifold_components(M.manifold, X) n = manifold_dimension(M.manifold) - F = Fiber(M.manifold, xp1, M.type) + F = Fiber(M.manifold, px, M.type) get_coordinates!(M.manifold, view(Y, 1:n), px, VXM, B.data.base_basis) get_coordinates!(F, view(Y, (n + 1):length(Y)), Vx, VXF, B.data.fiber_basis) return Y @@ -291,32 +291,35 @@ end zero_vector(B::FiberBundle, p) Zero tangent vector at point `p` from the fiber bundle `B` -over manifold `B.fiber` (denoted $\mathcal M$). The zero vector belongs to the space $T_{p}B$ +over manifold `B.fiber` (denoted ``\mathcal M``). The zero vector belongs to the space ``T_{p}B`` Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. + * The point ``p = (x_p, V_p)`` where ``x_p ∈ \mathcal M`` and ``V_p`` belongs to the + fiber ``F=π^{-1}(\{x_p\})`` of the vector bundle ``B`` where ``π`` is the + canonical projection of that vector bundle ``B``. The zero vector is calculated as -$\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)$ +``\mathbf{0}_{p} = (\mathbf{0}_{x_p}, \mathbf{0}_F)`` -where $\mathbf{0}_{x_p}$ is the zero tangent vector from $T_{x_p}\mathcal M$ and -$\mathbf{0}_F$ is the zero element of the vector space $F$. +where ``\mathbf{0}_{x_p}`` is the zero tangent vector from ``T_{x_p}\mathcal M`` and +``\mathbf{0}_F`` is the zero element of the vector space ``F``. """ zero_vector(::FiberBundle, ::Any...) function zero_vector!(B::FiberBundle, X, p) xp, Vp = submanifold_components(B.manifold, p) VXM, VXF = submanifold_components(B.manifold, X) + F = Fiber(B.manifold, xp, B.type) zero_vector!(B.manifold, VXM, xp) - zero_vector!(B.fiber, VXF, Vp) + zero_vector!(F, VXF, Vp) return X end @inline function allocate_result(M::FiberBundle, f::TF) where {TF} - return ArrayPartition(allocate_result(M.manifold, f), allocate_result(M.fiber, f)) + p = allocate_result(M.manifold, f) + X = allocate_result(Fiber(M.manifold, p, M.type), f) + return ArrayPartition(p, X) end function get_vector(M::FiberBundle, p, X, B::AbstractBasis) @@ -336,7 +339,7 @@ function get_vector( ) where {𝔽} n = manifold_dimension(M.manifold) xp1, xp2 = submanifold_components(M, p) - F = Fiber(M.manifold, M.type, xp1) + F = Fiber(M.manifold, xp1, M.type) return ArrayPartition( get_vector(M.manifold, xp1, X[1:n], B.data.base_basis), get_vector(F, xp2, X[(n + 1):end], B.data.fiber_basis), @@ -350,9 +353,9 @@ function get_vectors( ) where {𝔽} xp1, xp2 = submanifold_components(M, p) zero_m = zero_vector(M.manifold, xp1) - zero_f = zero_vector(M.fiber, xp1) + F = Fiber(M.manifold, xp1, M.type) + zero_f = zero_vector(F, xp1) vs = typeof(ArrayPartition(zero_m, zero_f))[] - F = Fiber(M.manifold, M.type, xp1) for bv in get_vectors(M.manifold, xp1, B.data.base_basis) push!(vs, ArrayPartition(bv, zero_f)) end diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 7acd69f3c0..1622c87bfb 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -135,7 +135,7 @@ function manifold_volume(M::PowerManifold{𝔽,<:AbstractManifold,TSize}) where end function Random.rand(rng::AbstractRNG, d::PowerFVectorDistribution) - fv = zero_vector(d.type, d.point) + fv = zero_vector(d.type.manifold, d.type.point) Distributions._rand!(rng, d, fv) return fv end @@ -153,7 +153,7 @@ function Distributions._rand!( PM = d.type.manifold rep_size = representation_size(PM.manifold) for i in get_iterator(d.type.manifold) - copyto!(d.distribution.point, _read(PM, rep_size, d.type.point, i)) + copyto!(d.distribution.type.point, _read(PM, rep_size, d.type.point, i)) Distributions._rand!(rng, d.distribution, _read(PM, rep_size, v, i)) end return v @@ -271,7 +271,7 @@ function Base.show( return print(io, "PowerManifold($(M.manifold), $(join(TSize.parameters, ", ")))") end -Distributions.support(tvd::PowerFVectorDistribution) = FVectorSupport(tvd.type, tvd.point) +Distributions.support(tvd::PowerFVectorDistribution) = FVectorSupport(tvd.type) Distributions.support(d::PowerPointDistribution) = MPointSupport(d.manifold) @doc raw""" diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 90527128fd..14321356a2 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -157,7 +157,7 @@ function ProductFVectorDistribution(distributions::FVectorDistribution...) end # Probably worth considering sum spaces in the future? p = ArrayPartition(map(d -> support(d).space.point, distributions)...) - return ProductFVectorDistribution(Fiber(M, fiber_type, p), distributions) + return ProductFVectorDistribution(Fiber(M, p, fiber_type), distributions) end function ProductPointDistribution(M::ProductManifold, distributions::MPointDistribution...) diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 3f15b25e15..2a51833308 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -53,7 +53,7 @@ const TangentBundle{𝔽,M} = TangentBundle(M::AbstractManifold) = FiberBundle(TangentSpaceType(), M) function TangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) - return VectorBundle(TangentSpaceType(), M, vtm) + return FiberBundle(TangentSpaceType(), M, vtm) end const CotangentBundle{𝔽,M} = @@ -61,7 +61,7 @@ const CotangentBundle{𝔽,M} = CotangentBundle(M::AbstractManifold) = FiberBundle(CotangentSpaceType(), M) function CotangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTransport) - return VectorBundle(CotangentSpaceType(), M, vtm) + return FiberBundle(CotangentSpaceType(), M, vtm) end function bundle_transport_to!(B::TangentBundle, Y, p, X, q) @@ -119,9 +119,10 @@ function inner(B::FiberBundle, p, X, Y) px, Vx = submanifold_components(B.manifold, p) VXM, VXF = submanifold_components(B.manifold, X) VYM, VYF = submanifold_components(B.manifold, Y) + F = Fiber(B.manifold, px, B.type) # for tangent bundle Vx is discarded by the method of inner for TangentSpace # and px is actually used as the base point - return inner(B.manifold, px, VXM, VYM) + inner(FiberAtPoint(B.fiber, px), Vx, VXF, VYF) + return inner(B.manifold, px, VXM, VYM) + inner(F, Vx, VXF, VYF) end function _inverse_retract(M::FiberBundle, p, q, ::FiberBundleInverseProductRetraction) @@ -183,7 +184,8 @@ function project!(B::VectorBundle, q, p) px, pVx = submanifold_components(B.manifold, p) qx, qVx = submanifold_components(B.manifold, q) project!(B.manifold, qx, px) - project!(B.fiber, qVx, qx, pVx) + F = Fiber(B.manifold, qx, B.type) + project!(F, qVx, pVx) return q end @@ -211,8 +213,9 @@ function project!(B::VectorBundle, Y, p, X) px, Vx = submanifold_components(B.manifold, p) VXM, VXF = submanifold_components(B.manifold, X) VYM, VYF = submanifold_components(B.manifold, Y) + F = Fiber(B.manifold, px, B.type) project!(B.manifold, VYM, px, VXM) - project!(B.fiber, VYF, px, VXF) + project!(F, VYF, Vx, VXF) return Y end diff --git a/test/manifolds/vector_bundle.jl b/test/manifolds/vector_bundle.jl index 3bb0d7f97b..4b5e6fd84d 100644 --- a/test/manifolds/vector_bundle.jl +++ b/test/manifolds/vector_bundle.jl @@ -73,12 +73,9 @@ struct TestVectorSpaceType <: VectorSpaceType end end X12_prod = inverse_retract(TB, pts_tb[1], pts_tb[2], m_prod_invretr) X13_prod = inverse_retract(TB, pts_tb[1], pts_tb[3], m_prod_invretr) - diag_basis = DiagonalizingOrthonormalBasis(X12_prod) basis_types = ( DefaultOrthonormalBasis(), get_basis(TB, pts_tb[1], DefaultOrthonormalBasis()), - diag_basis, - get_basis(TB, pts_tb[1], diag_basis), ) test_manifold( TB, @@ -174,17 +171,15 @@ struct TestVectorSpaceType <: VectorSpaceType end TT = Manifolds.TensorProductType(TangentSpaceType(), TangentSpaceType()) @test sprint(show, TT) == "TensorProductType(TangentSpaceType(), TangentSpaceType())" - @test vector_space_dimension(VectorSpaceFiber(TT, Sphere(2), [1.0, 0.0, 0.0])) == 4 - @test vector_space_dimension( - VectorSpaceFiber(TT, Sphere(3), [1.0, 0.0, 0.0, 0.0]), - ) == 9 - @test base_manifold(VectorSpaceFiber(TT, Sphere(2), [1.0, 0.0, 0.0])) == M - @test sprint(show, VectorSpaceFiber(TT, Sphere(2), [1.0, 0.0, 0.0])) == + @test vector_space_dimension(Sphere(2), TT) == 4 + @test vector_space_dimension(Sphere(3), TT) == 9 + @test base_manifold(Fiber(Sphere(2), [1.0, 0.0, 0.0], TT)) == M + @test sprint(show, Fiber(Sphere(2), [1.0, 0.0, 0.0], TT)) == "VectorSpaceFiber(TensorProductType(TangentSpaceType(), TangentSpaceType()), Sphere(2, ℝ))" end @testset "Error messages" begin - vbf = Fiber(TestVectorSpaceType(), Euclidean(3), [1.0, 0.0, 0.0]) + vbf = Fiber(Euclidean(3), [1.0, 0.0, 0.0], TestVectorSpaceType()) @test_throws MethodError inner(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) @test_throws MethodError project!(vbf, [1, 2, 3], [1, 2, 3], [1, 2, 3]) @test_throws MethodError zero_vector!(vbf, [1, 2, 3], [1, 2, 3]) From 32c8e9695ca3226a5c448c89b46b5de852673f76 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Fri, 13 Oct 2023 22:29:46 +0200 Subject: [PATCH 40/81] fixes --- src/manifolds/FiberBundle.jl | 41 +++++++++++++++++++++++++++-- src/manifolds/VectorBundle.jl | 46 ++++++++++++++++++++++++--------- test/manifolds/vector_bundle.jl | 26 +++---------------- 3 files changed, 77 insertions(+), 36 deletions(-) diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index f86f6989fe..81e62ab69b 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -157,7 +157,44 @@ the fiber over ``p``, transport ``X`` to fiber over ``q``. Exact meaning of the operation depends on the fiber bundle, or may even be undefined. """ -bundle_transport_to(B::FiberBundle, p, X, q) +function bundle_transport_to(B::FiberBundle, p, X, q) + Y = allocate(X) + return bundle_transport_to!(B, Y, p, X, q) +end + +@doc raw""" + bundle_transport_tangent_direction(B::FiberBundle, p, X, d) + +TODO +""" +function bundle_transport_tangent_direction( + B::FiberBundle, + p, + X, + d, + m::AbstractVectorTransportMethod=default_vector_transport_method(B.manifold), +) + Y = allocate(X) + return bundle_transport_tangent_direction!(B, Y, p, X, d, m) +end + +@doc raw""" + bundle_transport_tangent_to(B::FiberBundle, p, X, q) + +TODO + +Ehresmann connection; ``X`` is an element of the vertical bundle ``VF\mathcal M`` from tangent to fiber ``\pi^{-1}({p})``, ``p\in \mathcal M``. +""" +function bundle_transport_tangent_to( + B::FiberBundle, + p, + X, + q, + m::AbstractVectorTransportMethod=default_vector_transport_method(B.manifold), +) + Y = allocate(X) + return bundle_transport_tangent_to!(B, Y, p, X, q, m) +end """ bundle_projection(B::FiberBundle, p) @@ -247,7 +284,7 @@ function get_vector!( n = manifold_dimension(M.manifold) xp1, xp2 = submanifold_components(M, p) Yp1, Yp2 = submanifold_components(M, Y) - F = Fiber(M.manifold, M.type, xp1) + F = Fiber(M.manifold, xp1, M.type) get_vector!(M.manifold, Yp1, xp1, X[1:n], B.data.base_basis) get_vector!(F, Yp2, xp2, X[(n + 1):end], B.data.fiber_basis) return Y diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 2a51833308..b30c6c3101 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -68,6 +68,28 @@ function bundle_transport_to!(B::TangentBundle, Y, p, X, q) return vector_transport_to!(B.manifold, Y, p, X, q, B.vector_transport.method_fiber) end +function bundle_transport_tangent_direction!( + B::TangentBundle, + Y, + p, + X, + d, + m::AbstractVectorTransportMethod=default_vector_transport_method(B.manifold), +) + return vector_transport_direction!(B.manifold, Y, p, X, d, m) +end + +function bundle_transport_tangent_to!( + B::TangentBundle, + Y, + p, + X, + q, + m::AbstractVectorTransportMethod=default_vector_transport_method(B.manifold), +) + return vector_transport_to!(B.manifold, Y, p, X, q, m) +end + function default_inverse_retraction_method(::TangentBundle) return FiberBundleInverseProductRetraction() end @@ -100,20 +122,20 @@ end inner(B::VectorBundle, p, X, Y) Inner product of tangent vectors `X` and `Y` at point `p` from the -vector bundle `B` over manifold `B.fiber` (denoted $\mathcal M$). +vector bundle `B` over manifold `B.fiber` (denoted ``\mathcal M``). Notation: - * The point $p = (x_p, V_p)$ where $x_p ∈ \mathcal M$ and $V_p$ belongs to the - fiber $F=π^{-1}(\{x_p\})$ of the vector bundle $B$ where $π$ is the - canonical projection of that vector bundle $B$. - * The tangent vector $v = (V_{X,M}, V_{X,F}) ∈ T_{x}B$ where - $V_{X,M}$ is a tangent vector from the tangent space $T_{x_p}\mathcal M$ and - $V_{X,F}$ is a tangent vector from the tangent space $T_{V_p}F$ (isomorphic to $F$). - Similarly for the other tangent vector $w = (V_{Y,M}, V_{Y,F}) ∈ T_{x}B$. + * The point ``p = (x_p, V_p)`` where ``x_p ∈ \mathcal M`` and ``V_p`` belongs to the + fiber ``F=π^{-1}(\{x_p\})`` of the vector bundle ``B`` where ``π`` is the + canonical projection of that vector bundle ``B``. + * The tangent vector ``v = (V_{X,M}, V_{X,F}) ∈ T_{x}B`` where + ``V_{X,M}`` is a tangent vector from the tangent space ``T_{x_p}\mathcal M`` and + ``V_{X,F}`` is a tangent vector from the tangent space ``T_{V_p}F`` (isomorphic to ``F``). + Similarly for the other tangent vector ``w = (V_{Y,M}, V_{Y,F}) ∈ T_{x}B``. The inner product is calculated as -$⟨X, Y⟩_p = ⟨V_{X,M}, V_{Y,M}⟩_{x_p} + ⟨V_{X,F}, V_{Y,F}⟩_{V_p}.$ +``⟨X, Y⟩_p = ⟨V_{X,M}, V_{Y,M}⟩_{x_p} + ⟨V_{X,F}, V_{Y,F}⟩_{V_p}.`` """ function inner(B::FiberBundle, p, X, Y) px, Vx = submanifold_components(B.manifold, p) @@ -354,7 +376,7 @@ function vector_transport_to!( VYM, VYF = submanifold_components(M.manifold, Y) qx, qVx = submanifold_components(M.manifold, q) vector_transport_to!(M.manifold, VYM, px, VXM, qx, m.method_point) - vector_transport_to!(M.manifold, VYF, px, VXF, qx, m.method_fiber) + bundle_transport_tangent_to!(M, VYF, px, VXF, qx, m.method_fiber) return Y end @@ -370,7 +392,7 @@ function _vector_transport_direction( dx, dVx = submanifold_components(M.manifold, d) return ArrayPartition( vector_transport_direction(M.manifold, px, VXM, dx, m.method_point), - vector_transport_direction(M.fiber, px, VXF, dx, m.method_fiber), + bundle_transport_tangent_direction(M, px, VXF, dx, m.method_fiber), ) end @@ -386,7 +408,7 @@ function _vector_transport_to( qx, qVx = submanifold_components(M.manifold, q) return ArrayPartition( vector_transport_to(M.manifold, px, VXM, qx, m.method_point), - vector_transport_to(M.manifold, px, VXF, qx, m.method_fiber), + bundle_transport_tangent_to(M, px, VXF, qx, m.method_fiber), ) end diff --git a/test/manifolds/vector_bundle.jl b/test/manifolds/vector_bundle.jl index 4b5e6fd84d..b9a0a07a8d 100644 --- a/test/manifolds/vector_bundle.jl +++ b/test/manifolds/vector_bundle.jl @@ -103,26 +103,8 @@ struct TestVectorSpaceType <: VectorSpaceType end Xir = allocate(pts_tb[1]) inverse_retract!(TB, Xir, pts_tb[1], pts_tb[2], m_prod_invretr) @test isapprox(TB, pts_tb[1], Xir, X12_prod) - @test isapprox( - norm(TB.fiber, pts_tb[1][TB, :point], pts_tb[1][TB, :vector]), - sqrt( - inner( - TB.fiber, - pts_tb[1][TB, :point], - pts_tb[1][TB, :vector], - pts_tb[1][TB, :vector], - ), - ), - ) - @test isapprox( - distance( - TB.fiber, - pts_tb[1][TB, :point], - pts_tb[1][TB, :vector], - [0.0, 2.0, 3.0], - ), - 5.0, - ) + F = Fiber(M, pts_tb[1][TB, :point], TangentSpaceType()) + @test isapprox(distance(F, pts_tb[1][TB, :vector], [0.0, 2.0, 3.0]), 5.0) Xir2 = allocate(pts_tb[1]) vector_transport_to!( TB, @@ -175,7 +157,7 @@ struct TestVectorSpaceType <: VectorSpaceType end @test vector_space_dimension(Sphere(3), TT) == 9 @test base_manifold(Fiber(Sphere(2), [1.0, 0.0, 0.0], TT)) == M @test sprint(show, Fiber(Sphere(2), [1.0, 0.0, 0.0], TT)) == - "VectorSpaceFiber(TensorProductType(TangentSpaceType(), TangentSpaceType()), Sphere(2, ℝ))" + "VectorSpaceFiber{ℝ, Sphere{TypeParameter{Tuple{2}}, ℝ}, Manifolds.TensorProductType{Tuple{TangentSpaceType, TangentSpaceType}}, Vector{Float64}}(Sphere(2, ℝ), [1.0, 0.0, 0.0], TensorProductType(TangentSpaceType(), TangentSpaceType()))" end @testset "Error messages" begin @@ -215,7 +197,7 @@ struct TestVectorSpaceType <: VectorSpaceType end tbvt = Manifolds.FiberBundleProductVectorTransport(ppt, ppt) @test TangentBundle(M, tbvt).vector_transport === tbvt @test CotangentBundle(M, tbvt).vector_transport === tbvt - @test VectorBundle(TangentSpace, M, tbvt).vector_transport === tbvt + @test TangentBundle(M, tbvt).vector_transport === tbvt end @testset "Extended flatness tests" begin From 3a1e4d62f88ea6ea8ac6ed309d9407d79f1e92f6 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sat, 14 Oct 2023 19:58:55 +0200 Subject: [PATCH 41/81] fixes --- src/Manifolds.jl | 8 +++----- src/groups/product_group.jl | 2 +- src/manifolds/VectorFiber.jl | 16 ++++------------ test/manifolds/fiber.jl | 10 +++++----- test/metric.jl | 9 +++++---- test/runtests.jl | 1 - 6 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 2d08d7f421..2258590533 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -220,8 +220,10 @@ using ManifoldsBase: ComplexNumbers, ComponentManifoldError, CompositeManifoldError, + CotangentSpace, CotangentSpaceType, CoTFVector, + CoTVector, DefaultBasis, DefaultOrthogonalBasis, DefaultOrthonormalBasis, @@ -255,6 +257,7 @@ using ManifoldsBase: PolarInverseRetraction, PolarRetraction, PoleLadderTransport, + PowerBasisData, PowerManifold, PowerManifoldNested, PowerManifoldNestedReplacing, @@ -728,7 +731,6 @@ export AbstractRetractionMethod, ODEExponentialRetraction, PadeRetraction, ProductRetraction, - PowerRetraction, SasakiRetraction # Inverse Retraction types export AbstractInverseRetractionMethod, @@ -793,7 +795,6 @@ export ×, einstein_tensor, embed, embed!, - equiv, exp, exp!, flat, @@ -806,8 +807,6 @@ export ×, get_embedding, get_orbit_action, get_total_space, - grad_euclidean_to_manifold, - grad_euclidean_to_manifold!, hat, hat!, horizontal_component, @@ -919,7 +918,6 @@ export AbstractGroupAction, GroupManifold, GroupOperationAction, Identity, - InvariantMetric, LeftAction, LeftInvariantMetric, LeftSide, diff --git a/src/groups/product_group.jl b/src/groups/product_group.jl index 415c7dd259..571f108ed6 100644 --- a/src/groups/product_group.jl +++ b/src/groups/product_group.jl @@ -62,7 +62,7 @@ function Base.show(io::IO, ::MIME"text/plain", G::ProductGroup) io, "ProductGroup with $(length(base_manifold(G).manifolds)) subgroup$(length(base_manifold(G).manifolds) == 1 ? "" : "s"):", ) - return _show_product_manifold_no_header(io, base_manifold(G)) + return ManifoldsBase._show_product_manifold_no_header(io, base_manifold(G)) end function Base.show(io::IO, G::ProductGroup) diff --git a/src/manifolds/VectorFiber.jl b/src/manifolds/VectorFiber.jl index bfe9a66382..410d19babb 100644 --- a/src/manifolds/VectorFiber.jl +++ b/src/manifolds/VectorFiber.jl @@ -1,16 +1,4 @@ -const CotangentSpaceAtPoint{𝔽,M} = - Fiber{𝔽,CotangentSpaceType,M} where {𝔽,M<:AbstractManifold{𝔽}} - -""" - CotangentSpaceAtPoint(M::AbstractManifold, p) - -Return an object of type [`VectorSpaceAtPoint`](@ref) representing cotangent -space at `p`. -""" -function CotangentSpace(M::AbstractManifold, p) - return Fiber(M, CotangentSpaceType(), p) -end """ TensorProductType(spaces::VectorSpaceType...) @@ -24,6 +12,10 @@ end TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) +function inner(B::CotangentSpace, p, X, Y) + return inner(B.manifold, B.point, sharp(B.manifold, B.point, X), sharp(B.manifold, B.point, Y)) +end + function Base.show(io::IO, tpt::TensorProductType) return print(io, "TensorProductType(", join(tpt.spaces, ", "), ")") end diff --git a/test/manifolds/fiber.jl b/test/manifolds/fiber.jl index 7582e10aa1..8bf9253d09 100644 --- a/test/manifolds/fiber.jl +++ b/test/manifolds/fiber.jl @@ -9,7 +9,7 @@ struct TestVectorSpaceType <: VectorSpaceType end @testset "tangent and cotangent space" begin p = [1.0, 0.0, 0.0] t_p = TangentSpace(M, p) - ct_p = CotangentSpaceAtPoint(M, p) + ct_p = CotangentSpace(M, p) t_ps = sprint(show, "text/plain", t_p) sp = sprint(show, "text/plain", p) sp = replace(sp, '\n' => "\n ") @@ -17,10 +17,10 @@ struct TestVectorSpaceType <: VectorSpaceType end @test t_ps == t_ps_test @test base_manifold(t_p) == M @test base_manifold(ct_p) == M - @test t_p.fiber.manifold == M - @test ct_p.fiber.manifold == M - @test t_p.fiber.fiber == Manifolds.TangentFiber - @test ct_p.fiber.fiber == Manifolds.CotangentFiber + @test t_p.manifold == M + @test ct_p.manifold == M + @test t_p.fiber_type == TangentSpaceType() + @test ct_p.fiber_type == CotangentSpaceType() @test t_p.point == p @test ct_p.point == p @test injectivity_radius(t_p) == Inf diff --git a/test/metric.jl b/test/metric.jl index 778d4e79ee..2ec18b89f3 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -617,15 +617,16 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, cotspace = CotangentSpace(M, p) cotspace2 = CotangentSpace(MM, p) @test coX.X ≈ X - @test inner(M, p, X, Y) ≈ inner(cotspace, p, coX, coY) - @test inner(MM, p, fX, fY) ≈ inner(cotspace, p, coX, coY) + X0p = zero_vector(MM, p) + @test inner(M, p, X, Y) ≈ inner(cotspace, X0p, coX, coY) + @test inner(MM, p, fX, fY) ≈ inner(cotspace, X0p, coX, coY) - @test inner(MM, p, fX, fY) ≈ inner(cotspace2, p, cofX, cofY) + @test inner(MM, p, fX, fY) ≈ inner(cotspace2, X0p, cofX, cofY) @test sharp(M, p, coX) ≈ X coMMfX = flat(MM, p, fX) coMMfY = flat(MM, p, fY) - @test inner(MM, p, fX, fY) ≈ inner(cotspace2, p, coMMfX, coMMfY) + @test inner(MM, p, fX, fY) ≈ inner(cotspace2, X0p, coMMfX, coMMfY) @test isapprox(sharp(MM, p, coMMfX).data, fX.data) @testset "Mutating flat/sharp" begin diff --git a/test/runtests.jl b/test/runtests.jl index c5f5f730b1..afb113cc85 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -176,7 +176,6 @@ include("utils.jl") include_test("manifolds/product_manifold.jl") include_test("manifolds/power_manifold.jl") include_test("manifolds/quotient_manifold.jl") - include_test("manifolds/fiber.jl") include_test("manifolds/fiber_bundle.jl") include_test("manifolds/vector_bundle.jl") include_test("manifolds/graph.jl") From 0b99b8eafcd667734fc502253b0f575704be067c Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sat, 14 Oct 2023 21:27:58 +0200 Subject: [PATCH 42/81] adapt power manifold --- src/manifolds/PowerManifold.jl | 34 +++++++++++++++++++++++--------- src/manifolds/VectorFiber.jl | 8 ++++++-- test/manifolds/power_manifold.jl | 6 ++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/manifolds/PowerManifold.jl b/src/manifolds/PowerManifold.jl index 1622c87bfb..a52d535ee4 100644 --- a/src/manifolds/PowerManifold.jl +++ b/src/manifolds/PowerManifold.jl @@ -21,8 +21,13 @@ tangent space of the power manifold. """ struct PowerMetric <: AbstractMetric end -function PowerManifold(M::AbstractManifold{𝔽}, size::Integer...) where {𝔽} - return PowerManifold{𝔽,typeof(M),Tuple{size...},ArrayPowerRepresentation}(M) +function PowerManifold( + M::AbstractManifold{𝔽}, + size::Integer...; + parameter::Symbol=:field, +) where {𝔽} + size_w = wrap_type_parameter(parameter, size) + return PowerManifold{𝔽,typeof(M),typeof(size_w),ArrayPowerRepresentation}(M, size_w) end """ @@ -130,8 +135,9 @@ end Return the manifold volume of an [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) `M`. """ -function manifold_volume(M::PowerManifold{𝔽,<:AbstractManifold,TSize}) where {𝔽,TSize} - return manifold_volume(M.manifold)^prod(size_to_tuple(TSize)) +function manifold_volume(M::PowerManifold) + size = get_parameter(M.size) + return manifold_volume(M.manifold)^prod(size) end function Random.rand(rng::AbstractRNG, d::PowerFVectorDistribution) @@ -210,8 +216,8 @@ function Base.view( return _write(M, rep_size, p, I...) end -function representation_size(M::PowerManifold{𝔽,<:AbstractManifold,TSize}) where {𝔽,TSize} - return (representation_size(M.manifold)..., size_to_tuple(TSize)...) +function representation_size(M::PowerManifold) + return (representation_size(M.manifold)..., get_parameter(M.size)...) end @doc raw""" @@ -266,9 +272,19 @@ end function Base.show( io::IO, - M::PowerManifold{𝔽,TM,TSize,ArrayPowerRepresentation}, -) where {𝔽,TM,TSize} - return print(io, "PowerManifold($(M.manifold), $(join(TSize.parameters, ", ")))") + M::PowerManifold{𝔽,TM,TypeParameter{TSize},ArrayPowerRepresentation}, +) where {𝔽,TM<:AbstractManifold{𝔽},TSize} + return print( + io, + "PowerManifold($(M.manifold), $(join(TSize.parameters, ", ")), parameter=:type)", + ) +end +function Base.show( + io::IO, + M::PowerManifold{𝔽,TM,<:Tuple,ArrayPowerRepresentation}, +) where {𝔽,TM<:AbstractManifold{𝔽}} + size = get_parameter(M.size) + return print(io, "PowerManifold($(M.manifold), $(join(size, ", ")))") end Distributions.support(tvd::PowerFVectorDistribution) = FVectorSupport(tvd.type) diff --git a/src/manifolds/VectorFiber.jl b/src/manifolds/VectorFiber.jl index 410d19babb..96bb07ae2a 100644 --- a/src/manifolds/VectorFiber.jl +++ b/src/manifolds/VectorFiber.jl @@ -1,5 +1,4 @@ - """ TensorProductType(spaces::VectorSpaceType...) @@ -13,7 +12,12 @@ end TensorProductType(spaces::VectorSpaceType...) = TensorProductType{typeof(spaces)}(spaces) function inner(B::CotangentSpace, p, X, Y) - return inner(B.manifold, B.point, sharp(B.manifold, B.point, X), sharp(B.manifold, B.point, Y)) + return inner( + B.manifold, + B.point, + sharp(B.manifold, B.point, X), + sharp(B.manifold, B.point, Y), + ) end function Base.show(io::IO, tpt::TensorProductType) diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index 22d678e7c4..0be2f281cd 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -489,4 +489,10 @@ end X = repeat([0.0, 1.0, 0.0], 1, 5) @test volume_density(Ms1, p, X) ≈ volume_density(Ms, p[:, 1], X[:, 1])^5 end + + @testset "Static type parameter" begin + Ms1s = PowerManifold(Ms, 5; parameter=:type) + @test sprint(show, "text/plain", Ms1s) == + "PowerManifold(Sphere(2, ℝ), 5, parameter=:type)" + end end From 600f46f4bf93715e4eb167fada84b6c4cbb6d4eb Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 15 Oct 2023 11:39:49 +0200 Subject: [PATCH 43/81] Adapt to recent changes in ManifoldsBase 0.15 WIP --- src/manifolds/PositiveNumbers.jl | 40 +++++++++++++++++++++++------- src/manifolds/ProductManifold.jl | 25 ------------------- test/manifolds/positive_numbers.jl | 9 +++++++ 3 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/manifolds/PositiveNumbers.jl b/src/manifolds/PositiveNumbers.jl index 81267c86a4..84313f329c 100644 --- a/src/manifolds/PositiveNumbers.jl +++ b/src/manifolds/PositiveNumbers.jl @@ -15,28 +15,40 @@ please use [`SymmetricPositiveDefinite`](@ref)`(1)`. struct PositiveNumbers <: AbstractManifold{ℝ} end """ - PositiveVectors(n) + PositiveVectors(n::Integer; parameter::Symbol=:type) Generate the manifold of vectors with positive entries. This manifold is modeled as a [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) of [`PositiveNumbers`](@ref). + +`parameter`: whether a type parameter should be used to store `n`. By default size +is stored in a type parameter. Value can either be `:field` or `:type`. """ -PositiveVectors(n::Integer) = PositiveNumbers()^n +PositiveVectors(n::Integer; parameter::Symbol=:type) = + PowerManifold(PositiveNumbers(), n; parameter=parameter) """ - PositiveMatrices(m,n) + PositiveMatrices(m::Integer, n::Integer; parameter::Symbol=:type) Generate the manifold of matrices with positive entries. This manifold is modeled as a [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) of [`PositiveNumbers`](@ref). + +`parameter`: whether a type parameter should be used to store `n`. By default size +is stored in a type parameter. Value can either be `:field` or `:type`. """ -PositiveMatrices(n::Integer, m::Integer) = PositiveNumbers()^(n, m) +PositiveMatrices(n::Integer, m::Integer; parameter::Symbol=:type) = + PowerManifold(PositiveNumbers(), n, m; parameter=parameter) """ - PositiveArrays(n₁,n₂,...,nᵢ) + PositiveArrays(n₁, n₂, ..., nᵢ; parameter::Symbol=:type) Generate the manifold of `i`-dimensional arrays with positive entries. This manifold is modeled as a [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) of [`PositiveNumbers`](@ref). + +`parameter`: whether a type parameter should be used to store `n`. By default size +is stored in a type parameter. Value can either be `:field` or `:type`. """ -PositiveArrays(n::Vararg{Int,I}) where {I} = PositiveNumbers()^(n) +PositiveArrays(n::Vararg{Int,I}; parameter::Symbol=:type) where {I} = + PowerManifold(PositiveNumbers(), n...; parameter=parameter) @doc raw""" change_representer(M::PositiveNumbers, E::EuclideanMetric, p, X) @@ -267,13 +279,23 @@ Base.show(io::IO, ::PositiveNumbers) = print(io, "PositiveNumbers()") function Base.show( io::IO, - ::PowerManifold{ℝ,PositiveNumbers,TSize,ArrayPowerRepresentation}, -) where {TSize} - s = [TSize.parameters...] + M::PowerManifold{ℝ,PositiveNumbers,TSize,ArrayPowerRepresentation}, +) where {TSize<:TypeParameter} + s = get_parameter(M.size) (length(s) == 1) && return print(io, "PositiveVectors($(s[1]))") (length(s) == 2) && return print(io, "PositiveMatrices($(s[1]), $(s[2]))") return print(io, "PositiveArrays($(join(s, ", ")))") end +function Base.show( + io::IO, + M::PowerManifold{ℝ,PositiveNumbers,TSize,ArrayPowerRepresentation}, +) where {TSize<:Tuple} + s = get_parameter(M.size) + (length(s) == 1) && return print(io, "PositiveVectors($(s[1]); parameter=:field)") + (length(s) == 2) && + return print(io, "PositiveMatrices($(s[1]), $(s[2]); parameter=:field)") + return print(io, "PositiveArrays($(join(s, ", ")); parameter=:field)") +end @doc raw""" parallel_transport_to(M::PositiveNumbers, p, X, q) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 14321356a2..275010f9b9 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -70,31 +70,6 @@ function adjoint_Jacobi_field!(M::ProductManifold, Y, p, q, t, X, β::Tβ) where return Y end -@doc raw""" - cross(M, N) - cross(M1, M2, M3,...) - -Return the [`ProductManifold`](@ref) For two `AbstractManifold`s `M` and `N`, -where for the case that one of them is a [`ProductManifold`](@ref) itself, -the other is either prepended (if `N` is a product) or appenden (if `M`) is. -If both are product manifold, they are combined into one product manifold, -keeping the order. - -For the case that more than one is a product manifold of these is build with the -same approach as above -""" -cross(::AbstractManifold...) -LinearAlgebra.cross(M1::AbstractManifold, M2::AbstractManifold) = ProductManifold(M1, M2) -function LinearAlgebra.cross(M1::ProductManifold, M2::AbstractManifold) - return ProductManifold(M1.manifolds..., M2) -end -function LinearAlgebra.cross(M1::AbstractManifold, M2::ProductManifold) - return ProductManifold(M1, M2.manifolds...) -end -function LinearAlgebra.cross(M1::ProductManifold, M2::ProductManifold) - return ProductManifold(M1.manifolds..., M2.manifolds...) -end - @doc raw""" flat(M::ProductManifold, p, X::FVector{TangentSpaceType}) diff --git a/test/manifolds/positive_numbers.jl b/test/manifolds/positive_numbers.jl index 3aec6d5868..65687159b6 100644 --- a/test/manifolds/positive_numbers.jl +++ b/test/manifolds/positive_numbers.jl @@ -102,4 +102,13 @@ include("../utils.jl") rand!(M, X; vector_at=p) @test is_vector(M, p, X) end + + @testset "field parameter" begin + M1 = PositiveVectors(3; parameter=:field) + @test repr(M1) == "PositiveVectors(3; parameter=:field)" + M2 = PositiveMatrices(3, 4; parameter=:field) + @test repr(M2) == "PositiveMatrices(3, 4; parameter=:field)" + M3 = PositiveArrays(3, 4, 5; parameter=:field) + @test repr(M3) == "PositiveArrays(3, 4, 5; parameter=:field)" + end end From 17b5a4afd35b9f496cd8619e0fa319131ec8ff2d Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 15 Oct 2023 19:10:09 +0200 Subject: [PATCH 44/81] minor improvements --- NEWS.md | 7 +++++-- src/manifolds/FiberBundle.jl | 24 ++++++++++++++++-------- src/manifolds/FixedRankMatrices.jl | 13 ------------- src/manifolds/ProductManifold.jl | 2 +- src/manifolds/ProjectiveSpace.jl | 2 +- src/manifolds/Rotations.jl | 30 +++++++++++++++--------------- src/manifolds/Sphere.jl | 2 +- src/manifolds/VectorBundle.jl | 8 +++++--- src/projected_distribution.jl | 2 +- test/manifolds/product_manifold.jl | 4 ++-- test/metric.jl | 7 +++++-- 11 files changed, 52 insertions(+), 49 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3e6ae1018c..3e4e6bc401 100644 --- a/NEWS.md +++ b/NEWS.md @@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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 type parameter, replicating the previous behavior. + The default is set to store the size in type parameter (except for `PowerManifold` and its variants), replicating the previous behavior. For field storage, pass the `parameter=:field` keyword argument to manifold constructor. For example statically sized `CenteredMatrices{m,n}` is now `CenteredMatrices{TypeParameter{Tuple{m,n}}}`, whereas the type of special Euclidean group with field-stored size is `CenteredMatrices{Tuple{Int,Int}}`. Similar change applies to: - `CenteredMatrices{m,n}`, @@ -36,6 +36,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `MultinomialDoublyStochastic{n}`, - `MultinomialSymmetric{n}`, - `Orthogonal{n}`, + - `PowerManifold`, + - `PositiveArrays`, + - `PositiveMatrices`, + - `PositiveNumbers`, - `ProbabilitySimplex{n}`, - `SPDFixedDeterminant{n}`, - `SpecialLinear{n}`, @@ -93,7 +97,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ``` - Argument order for type aliases `RotationActionOnVector` and `RotationTranslationActionOnVector`: most often dispatched on argument is now first. -- `HasLeftInvariantMetric`, `HasRightInvariantMetric` and `HasBiinvariantMetric` now explicitly refer to using default implementations of invariant metric functions. If you provide your own implementations, these traits should not be specified. ### Removed diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index 81e62ab69b..3885f02b3d 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -156,6 +156,8 @@ Given a fiber bundle ``B=F \mathcal M``, points ``p, q\in\mathcal M``, an elemen the fiber over ``p``, transport ``X`` to fiber over ``q``. Exact meaning of the operation depends on the fiber bundle, or may even be undefined. +Some fiber bundles may declare a default local section around each point crossing `X`, +represented by this function. """ function bundle_transport_to(B::FiberBundle, p, X, q) Y = allocate(X) @@ -163,37 +165,43 @@ function bundle_transport_to(B::FiberBundle, p, X, q) end @doc raw""" - bundle_transport_tangent_direction(B::FiberBundle, p, X, d) + bundle_transport_tangent_direction(B::FiberBundle, p, pf, X, d) -TODO +Compute parallel transport of vertical vector `X` according to Ehresmann connection on +[`FiberBundle`](@ref) `B`, in direction ``d\in T_p \mathcal M``. ``X`` is an element of the +vertical bundle ``VF\mathcal M`` at `pf` from tangent to fiber ``\pi^{-1}({p})``, +``p\in \mathcal M``. """ function bundle_transport_tangent_direction( B::FiberBundle, p, + pf, X, d, m::AbstractVectorTransportMethod=default_vector_transport_method(B.manifold), ) Y = allocate(X) - return bundle_transport_tangent_direction!(B, Y, p, X, d, m) + return bundle_transport_tangent_direction!(B, Y, p, pf, X, d, m) end @doc raw""" - bundle_transport_tangent_to(B::FiberBundle, p, X, q) + bundle_transport_tangent_to(B::FiberBundle, p, pf, X, q) -TODO - -Ehresmann connection; ``X`` is an element of the vertical bundle ``VF\mathcal M`` from tangent to fiber ``\pi^{-1}({p})``, ``p\in \mathcal M``. +Compute parallel transport of vertical vector `X` according to Ehresmann connection on +[`FiberBundle`](@ref) `B`, to point ``q\in \mathcal M``. ``X`` is an element of the vertical +bundle ``VF\mathcal M`` at `pf` from tangent to fiber ``\pi^{-1}({p})``, +``p\in \mathcal M``. """ function bundle_transport_tangent_to( B::FiberBundle, p, + pf, X, q, m::AbstractVectorTransportMethod=default_vector_transport_method(B.manifold), ) Y = allocate(X) - return bundle_transport_tangent_to!(B, Y, p, X, q, m) + return bundle_transport_tangent_to!(B, Y, p, pf, X, q, m) end """ diff --git a/src/manifolds/FixedRankMatrices.jl b/src/manifolds/FixedRankMatrices.jl index ce396ca733..9b09232b1c 100644 --- a/src/manifolds/FixedRankMatrices.jl +++ b/src/manifolds/FixedRankMatrices.jl @@ -132,19 +132,6 @@ function allocate_result(M::FixedRankMatrices, ::typeof(project), X, p, vals...) # vals are p and X, so we can use their fields to set up those of the UMVTVector return UMVTVector(allocate(p.U, m, k), allocate(p.S, k, k), allocate(p.Vt, k, n)) end -function allocate_result(::FixedRankMatrices, ::typeof(rand), p) - m, n, k = get_parameter(M.size) - # vals are p and X, so we can use their fields to set up those of the UMVTVector - return UMVTVector(allocate(p.U, m, k), allocate(p.S, k, k), allocate(p.Vt, k, n)) -end -function allocate_result(::FixedRankMatrices, ::typeof(rand)) - m, n, k = get_parameter(M.size) - return SVDMPoint( - Matrix{Float64}(undef, m, k), - Vector{Float64}(undef, k), - Matrix{Float64}(undef, k, n), - ) -end Base.copy(v::UMVTVector) = UMVTVector(copy(v.U), copy(v.M), copy(v.Vt)) diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 275010f9b9..5070073f62 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -163,7 +163,7 @@ function Distributions._rand!( p::ArrayPartition, ) map( - t -> Distributions._rand!(rng, t[1], t[2]), + (t1, t2) -> Distributions._rand!(rng, t1, t2), d.distributions, submanifold_components(d.manifold, p), ) diff --git a/src/manifolds/ProjectiveSpace.jl b/src/manifolds/ProjectiveSpace.jl index 8d1eb2ca77..392215431b 100644 --- a/src/manifolds/ProjectiveSpace.jl +++ b/src/manifolds/ProjectiveSpace.jl @@ -507,7 +507,7 @@ Uniform distribution on given [`ProjectiveSpace`](@ref) `M`. Generated points wi similar type as `p`. """ function uniform_distribution(M::ProjectiveSpace{<:Any,ℝ}, p) - d = Distributions.MvNormal(zero(p), 1.0) + d = Distributions.MvNormal(zero(p), 1.0 * I) return ProjectedPointDistribution(M, d, project!, p) end diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index 9f786450ed..d1859026b3 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -43,7 +43,7 @@ end @doc raw""" angles_4d_skew_sym_matrix(A) -The Lie algebra of [`Rotations(4)`](@ref) in $ℝ^{4 × 4}$, $𝔰𝔬(4)$, consists of $4 × 4$ +The Lie algebra of [`Rotations(4)`](@ref) in ``ℝ^{4 × 4}``, ``𝔰𝔬(4)``, consists of ``4 × 4`` skew-symmetric matrices. The unique imaginary components of their eigenvalues are the angles of the two plane rotations. This function computes these more efficiently than `eigvals`. @@ -158,7 +158,7 @@ end injectivity_radius(M::Rotations, ::PolarRetraction) Return the radius of injectivity for the [`PolarRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.PolarRetraction) on the -[`Rotations`](@ref) `M` which is $\frac{π}{\sqrt{2}}$. +[`Rotations`](@ref) `M` which is ``\frac{π}{\sqrt{2}}``. """ injectivity_radius(::Rotations, ::PolarRetraction) function _injectivity_radius(M::Rotations, ::PolarRetraction) @@ -169,7 +169,7 @@ end @doc raw""" inverse_retract(M, p, q, ::PolarInverseRetraction) -Compute a vector from the tangent space $T_p\mathrm{SO}(n)$ +Compute a vector from the tangent space ``T_p\mathrm{SO}(n)`` of the point `p` on the [`Rotations`](@ref) manifold `M` with which the point `q` can be reached by the [`PolarRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.PolarRetraction) from the point `p` after time 1. @@ -180,16 +180,16 @@ The formula reads = -\frac{1}{2}(p^{\mathrm{T}}qs - (p^{\mathrm{T}}qs)^{\mathrm{T}}) ```` -where $s$ is the solution to the Sylvester equation +where ``s`` is the solution to the Sylvester equation -$p^{\mathrm{T}}qs + s(p^{\mathrm{T}}q)^{\mathrm{T}} + 2I_n = 0.$ +``p^{\mathrm{T}}qs + s(p^{\mathrm{T}}q)^{\mathrm{T}} + 2I_n = 0.`` """ inverse_retract(::Rotations, ::Any, ::Any, ::PolarInverseRetraction) @doc raw""" inverse_retract(M::Rotations, p, q, ::QRInverseRetraction) -Compute a vector from the tangent space $T_p\mathrm{SO}(n)$ of the point `p` on the +Compute a vector from the tangent space ``T_p\mathrm{SO}(n)`` of the point `p` on the [`Rotations`](@ref) manifold `M` with which the point `q` can be reached by the [`QRRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions.html#ManifoldsBase.QRRetraction) from the point `q` after time 1. """ @@ -230,18 +230,18 @@ end normal_rotation_distribution(M::Rotations, p, σ::Real) Return a random point on the manifold [`Rotations`](@ref) `M` -by generating a (Gaussian) random orthogonal matrix with determinant $+1$. Let +by generating a (Gaussian) random orthogonal matrix with determinant ``+1``. Let -$QR = A$ +``QR = A`` -be the QR decomposition of a random matrix $A$, then the formula reads +be the QR decomposition of a random matrix ``A``, then the formula reads -$p = QD$ +``p = QD`` -where $D$ is a diagonal matrix with the signs of the diagonal entries of $R$, +where ``D`` is a diagonal matrix with the signs of the diagonal entries of ``R``, i.e. -$D_{ij}=\begin{cases} \operatorname{sgn}(R_{ij}) & \text{if} \; i=j \\ 0 & \, \text{otherwise} \end{cases}.$ +``D_{ij}=\begin{cases} \operatorname{sgn}(R_{ij}) & \text{if} \; i=j \\ 0 & \, \text{otherwise} \end{cases}.`` It can happen that the matrix gets -1 as a determinant. In this case, the first and second columns are swapped. @@ -250,7 +250,7 @@ The argument `p` is used to determine the type of returned points. """ function normal_rotation_distribution(M::Rotations, p, σ::Real) n = get_parameter(M.size)[1] - d = Distributions.MvNormal(zeros(n * n), σ) + d = Distributions.MvNormal(zeros(n * n), σ * I) return NormalRotationDistribution(M, d, p) end @@ -259,7 +259,7 @@ end Project `p` to the nearest point on manifold `M`. -Given the singular value decomposition $p = U Σ V^\mathrm{T}$, with the +Given the singular value decomposition ``p = U Σ V^\mathrm{T}``, with the singular values sorted in descending order, the projection is ````math @@ -267,7 +267,7 @@ singular values sorted in descending order, the projection is U\operatorname{diag}\left[1,1,…,\det(U V^\mathrm{T})\right] V^\mathrm{T} ```` -The diagonal matrix ensures that the determinant of the result is $+1$. +The diagonal matrix ensures that the determinant of the result is ``+1``. If `p` is expected to be almost special orthogonal, then you may avoid this check with `check_det = false`. """ diff --git a/src/manifolds/Sphere.jl b/src/manifolds/Sphere.jl index 4c6deaa8ec..e2fb758159 100644 --- a/src/manifolds/Sphere.jl +++ b/src/manifolds/Sphere.jl @@ -520,7 +520,7 @@ similar type as `p`. """ function uniform_distribution(M::Sphere{<:Any,ℝ}, p) n = get_parameter(M.size)[1] - d = Distributions.MvNormal(zero(p), 1.0) + d = Distributions.MvNormal(zero(p), 1.0 * I) return ProjectedPointDistribution(M, d, project!, p) end diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index b30c6c3101..d2fcca6b82 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -72,6 +72,7 @@ function bundle_transport_tangent_direction!( B::TangentBundle, Y, p, + pf, X, d, m::AbstractVectorTransportMethod=default_vector_transport_method(B.manifold), @@ -83,6 +84,7 @@ function bundle_transport_tangent_to!( B::TangentBundle, Y, p, + pf, X, q, m::AbstractVectorTransportMethod=default_vector_transport_method(B.manifold), @@ -376,7 +378,7 @@ function vector_transport_to!( VYM, VYF = submanifold_components(M.manifold, Y) qx, qVx = submanifold_components(M.manifold, q) vector_transport_to!(M.manifold, VYM, px, VXM, qx, m.method_point) - bundle_transport_tangent_to!(M, VYF, px, VXF, qx, m.method_fiber) + bundle_transport_tangent_to!(M, VYF, px, pVx, VXF, qx, m.method_fiber) return Y end @@ -392,7 +394,7 @@ function _vector_transport_direction( dx, dVx = submanifold_components(M.manifold, d) return ArrayPartition( vector_transport_direction(M.manifold, px, VXM, dx, m.method_point), - bundle_transport_tangent_direction(M, px, VXF, dx, m.method_fiber), + bundle_transport_tangent_direction(M, px, pVx, VXF, dx, m.method_fiber), ) end @@ -408,7 +410,7 @@ function _vector_transport_to( qx, qVx = submanifold_components(M.manifold, q) return ArrayPartition( vector_transport_to(M.manifold, px, VXM, qx, m.method_point), - bundle_transport_tangent_to(M, px, VXF, qx, m.method_fiber), + bundle_transport_tangent_to(M, px, pVx, VXF, qx, m.method_fiber), ) end diff --git a/src/projected_distribution.jl b/src/projected_distribution.jl index d312606384..1d707e417a 100644 --- a/src/projected_distribution.jl +++ b/src/projected_distribution.jl @@ -120,7 +120,7 @@ Normal distribution in ambient space with standard deviation `σ` projected to tangent space at `p`. """ function normal_tvector_distribution(M::AbstractManifold, p, σ) - d = Distributions.MvNormal(zero(vec(p)), σ) + d = Distributions.MvNormal(zero(vec(p)), σ * I) return ProjectedFVectorDistribution(TangentSpace(M, p), d, project!, p) end diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index 5418cf4d5a..797cadca6a 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -345,7 +345,7 @@ using RecursiveArrayTools: ArrayPartition distr_M1 = Manifolds.uniform_distribution(M1, pts_sphere[1]) distr_M2 = Manifolds.projected_distribution( M2, - Distributions.MvNormal(zero(pts_r2[1]), 1.0), + Distributions.MvNormal(zero(pts_r2[1]), 1.0 * I), ) distr_tv_M1 = Manifolds.normal_tvector_distribution(M1, pts_sphere[1], 1.0) distr_tv_M2 = Manifolds.normal_tvector_distribution(M2, pts_r2[1], 1.0) @@ -383,7 +383,7 @@ using RecursiveArrayTools: ArrayPartition test_tangent_vector_broadcasting=true, test_project_tangent=true, test_project_point=true, - test_mutating_rand=false, + test_mutating_rand=true, retraction_methods=retraction_methods, inverse_retraction_methods=inverse_retraction_methods, test_riesz_representer=true, diff --git a/test/metric.jl b/test/metric.jl index 2ec18b89f3..b9fd75b17a 100644 --- a/test/metric.jl +++ b/test/metric.jl @@ -595,10 +595,13 @@ Manifolds.inner(::MetricManifold{ℝ,<:AbstractManifold{ℝ},Issue539Metric}, p, @test is_point(MM2, p) === is_point(M, p) @test is_vector(MM2, p, X) === is_vector(M, p, X) - a = Manifolds.projected_distribution(M, Distributions.MvNormal(zero(zeros(3)), 1.0)) + a = Manifolds.projected_distribution( + M, + Distributions.MvNormal(zero(zeros(3)), 1.0 * I), + ) b = Manifolds.projected_distribution( MM2, - Distributions.MvNormal(zero(zeros(3)), 1.0), + Distributions.MvNormal(zero(zeros(3)), 1.0 * I), ) @test isapprox(Matrix(a.distribution.Σ), Matrix(b.distribution.Σ)) @test isapprox(a.distribution.μ, b.distribution.μ) From d6091f8e8374de491589a40541d9fbfc72bedeb7 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 16 Oct 2023 13:55:14 +0200 Subject: [PATCH 45/81] minor adaptation of (inverse) retractions --- src/Manifolds.jl | 3 --- src/manifolds/VectorBundle.jl | 8 -------- test/manifolds/product_manifold.jl | 13 ++++++------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/Manifolds.jl b/src/Manifolds.jl index 2258590533..fe5dba1718 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -23,10 +23,8 @@ import ManifoldsBase: _access_nested, _get_basis, _injectivity_radius, - _inverse_retract, _inverse_retract!, _read, - _retract, _retract!, _write, active_traits, @@ -100,7 +98,6 @@ import ManifoldsBase: is_vector, inverse_retract, inverse_retract!, - _inverse_retract, _inverse_retract!, inverse_retract_cayley!, inverse_retract_embedded!, diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index d2fcca6b82..e54387a718 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -149,10 +149,6 @@ function inner(B::FiberBundle, p, X, Y) return inner(B.manifold, px, VXM, VYM) + inner(F, Vx, VXF, VYF) end -function _inverse_retract(M::FiberBundle, p, q, ::FiberBundleInverseProductRetraction) - return inverse_retract_product(M, p, q) -end - function _inverse_retract!(M::FiberBundle, X, p, q, ::FiberBundleInverseProductRetraction) return inverse_retract_product!(M, X, p, q) end @@ -243,10 +239,6 @@ function project!(B::VectorBundle, Y, p, X) return Y end -function _retract(M::VectorBundle, p, X, t::Number, ::FiberBundleProductRetraction) - return retract_product(M, p, X, t) -end - function _retract!(M::VectorBundle, q, p, X, t::Number, ::FiberBundleProductRetraction) return retract_product!(M, q, p, X, t) end diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index 797cadca6a..f1b312b4cc 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -47,16 +47,15 @@ using RecursiveArrayTools: ArrayPartition TEST_STATIC_SIZED && push!(types, MVector{5,Float64}) retraction_methods = [ - Manifolds.ProductRetraction( - ManifoldsBase.ExponentialRetraction(), - ManifoldsBase.ExponentialRetraction(), - ), + ProductRetraction(ExponentialRetraction(), ExponentialRetraction()), + ExponentialRetraction(), ] inverse_retraction_methods = [ - Manifolds.InverseProductRetraction( - ManifoldsBase.LogarithmicInverseRetraction(), - ManifoldsBase.LogarithmicInverseRetraction(), + InverseProductRetraction( + LogarithmicInverseRetraction(), + LogarithmicInverseRetraction(), ), + LogarithmicInverseRetraction(), ] @testset "get_component, set_component!, getindex and setindex!" begin From 0a1ecfcf10947705422e96cdb7f9e0a72149e6dc Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 16 Oct 2023 17:35:47 +0200 Subject: [PATCH 46/81] adapt to exp/retract unification --- src/Manifolds.jl | 2 ++ src/manifolds/VectorBundle.jl | 8 ++++++++ test/manifolds/circle.jl | 4 ---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Manifolds.jl b/src/Manifolds.jl index fe5dba1718..d0e48f4337 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -23,8 +23,10 @@ import ManifoldsBase: _access_nested, _get_basis, _injectivity_radius, + _inverse_retract, _inverse_retract!, _read, + _retract, _retract!, _write, active_traits, diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index e54387a718..340b847ba2 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -149,6 +149,10 @@ function inner(B::FiberBundle, p, X, Y) return inner(B.manifold, px, VXM, VYM) + inner(F, Vx, VXF, VYF) end +function _inverse_retract(M::FiberBundle, p, q, ::FiberBundleInverseProductRetraction) + return inverse_retract_product(M, p, q) +end + function _inverse_retract!(M::FiberBundle, X, p, q, ::FiberBundleInverseProductRetraction) return inverse_retract_product!(M, X, p, q) end @@ -251,6 +255,10 @@ which by default allocates and calls `retract_product!`. """ retract(::VectorBundle, p, q, t::Number, ::FiberBundleProductRetraction) +function _retract(M::VectorBundle, p, X, t::Number, ::FiberBundleProductRetraction) + return retract_product(M, p, X, t) +end + function retract_product(M::VectorBundle, p, X, t::Number) q = allocate_result(M, retract, p, X) return retract_product!(M, q, p, X, t) diff --git a/test/manifolds/circle.jl b/test/manifolds/circle.jl index 0187eb732b..d0ab19fd92 100644 --- a/test/manifolds/circle.jl +++ b/test/manifolds/circle.jl @@ -2,10 +2,6 @@ include("../utils.jl") using Manifolds: TFVector, CoTFVector -# TODO: remove after bug in StaticArray is fixed -@inline Base.copy(a::SizedArray) = __copy(a) -@inline __copy(a::SizedArray{S,T}) where {S,T} = SizedArray{S,T}(copy(a.data)) - @testset "Circle" begin M = Circle() @testset "Real Circle Basics" begin From 7cfde4e7e484747faab23dbb4ac3c2dcf1483cf3 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 16 Oct 2023 19:21:10 +0200 Subject: [PATCH 47/81] Mostly update to error in is_point/is_vector changes --- ext/ManifoldsTestExt/tests_general.jl | 14 +- src/groups/rotation_action.jl | 4 +- src/groups/validation_group.jl | 140 +++++++++--------- src/manifolds/StiefelSubmersionMetric.jl | 2 +- test/groups/circle_group.jl | 2 +- test/groups/general_linear.jl | 36 +++-- test/groups/general_unitary_groups.jl | 10 +- test/groups/groups_general.jl | 11 +- test/groups/special_euclidean.jl | 8 +- test/groups/special_linear.jl | 24 +-- test/manifolds/centered_matrices.jl | 12 +- test/manifolds/cholesky_space.jl | 12 +- test/manifolds/circle.jl | 16 +- test/manifolds/elliptope.jl | 6 +- test/manifolds/essential_manifold.jl | 8 +- test/manifolds/euclidean.jl | 28 ++-- test/manifolds/fixed_rank.jl | 40 +++-- test/manifolds/generalized_grassmann.jl | 25 ++-- test/manifolds/generalized_stiefel.jl | 20 +-- test/manifolds/graph.jl | 12 +- test/manifolds/grassmann.jl | 64 ++++---- test/manifolds/hyperbolic.jl | 39 +++-- .../multinomial_doubly_stochastic.jl | 12 +- test/manifolds/multinomial_matrices.jl | 8 +- test/manifolds/multinomial_symmetric.jl | 10 +- test/manifolds/oblique.jl | 8 +- test/manifolds/positive_numbers.jl | 2 +- test/manifolds/power_manifold.jl | 4 +- test/manifolds/probability_simplex.jl | 14 +- test/manifolds/product_manifold.jl | 10 +- test/manifolds/projective_space.jl | 21 ++- test/manifolds/rotations.jl | 16 +- test/manifolds/shape_space.jl | 10 +- test/manifolds/skewhermitian.jl | 12 +- test/manifolds/spd_fixed_determinant.jl | 4 +- test/manifolds/spectrahedron.jl | 8 +- test/manifolds/sphere.jl | 9 +- test/manifolds/sphere_symmetric_matrices.jl | 20 +-- test/manifolds/stiefel.jl | 22 +-- test/manifolds/symmetric.jl | 12 +- ...metric_positive_semidefinite_fixed_rank.jl | 2 +- test/manifolds/symplectic.jl | 9 +- test/manifolds/symplecticstiefel.jl | 4 +- test/manifolds/torus.jl | 10 +- test/manifolds/unitary_matrices.jl | 36 ++--- tutorials/getstarted.qmd | 6 +- 46 files changed, 442 insertions(+), 360 deletions(-) diff --git a/ext/ManifoldsTestExt/tests_general.jl b/ext/ManifoldsTestExt/tests_general.jl index d99fa69160..435f1a7484 100644 --- a/ext/ManifoldsTestExt/tests_general.jl +++ b/ext/ManifoldsTestExt/tests_general.jl @@ -750,12 +750,12 @@ function test_manifold( test_rand_point && Test.@testset "Base.rand point generation" begin rng_a = MersenneTwister(123) rng_b = MersenneTwister(123) - Test.@test is_point(M, rand(M), true) + Test.@test is_point(M, rand(M); error=:error) # ensure that the RNG source is actually used Test.@test rand(rng_a, M) == rand(rng_b, M) # generation of multiple points - Test.@test all(p -> is_point(M, p, true), rand(M, 3)) - Test.@test all(p -> is_point(M, p, true), rand(rng_a, M, 3)) + Test.@test all(p -> is_point(M, p; error=:error), rand(M, 3)) + Test.@test all(p -> is_point(M, p; error=:error), rand(rng_a, M, 3)) if test_inplace && is_mutating rng_a = MersenneTwister(123) @@ -763,10 +763,10 @@ function test_manifold( p = allocate(pts[1]) rand!(M, p) - Test.@test is_point(M, p, true) + Test.@test is_point(M, p; error=:error) p = allocate(pts[1]) rand!(rng_a, M, p) - Test.@test is_point(M, p, true) + Test.@test is_point(M, p; error=:error) # ensure that the RNG source is actually used q = allocate(pts[1]) rand!(rng_b, M, q) @@ -799,7 +799,7 @@ function test_manifold( Test.@test is_vector(M, p, X, true; atol=atol) X = allocate(tv[1]) rand!(rng_a, M, X; vector_at=p) - Test.@test is_point(M, p, true) + Test.@test is_point(M, p; error=:error) # ensure that the RNG source is actually used Y = allocate(tv[1]) rand!(rng_b, M, Y; vector_at=p) @@ -889,7 +889,7 @@ function test_parallel_transport( Test.@test isapprox(M, q, Y1, Y2) end # Test that Y is a tangent vector at q - Test.@test is_vector(M, p, Y1, true) + Test.@test is_vector(M, p, Y1; error=:error) end end end diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index 13c5cb50f6..25f211177b 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -282,8 +282,8 @@ where ``U \Sigma V^{\mathrm{T}}`` is the SVD decomposition of ``p q^{\mathrm{T}} is the unit diagonal matrix with the last element on the diagonal replaced with -1. """ function optimal_alignment(A::LeftColumnwiseMultiplicationAction, p, q) - is_point(A.manifold, p, true) - is_point(A.manifold, q, true) + is_point(A.manifold, p; error=:error) + is_point(A.manifold, q; error=:error) Xmul = p * transpose(q) F = svd(Xmul) diff --git a/src/groups/validation_group.jl b/src/groups/validation_group.jl index fcef541e08..be45d09460 100644 --- a/src/groups/validation_group.jl +++ b/src/groups/validation_group.jl @@ -7,20 +7,20 @@ array_point(p) = ValidationMPoint(p) array_point(p::ValidationMPoint) = p function adjoint_action(M::ValidationManifold, p, X; kwargs...) - is_point(M, p, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) eM = Identity(M.manifold) - is_vector(M, eM, X, true; kwargs...) + is_vector(M, eM, X; error=M.mode, kwargs...) Y = ValidationTVector(adjoint_action(M.manifold, array_value(p), array_value(X))) - is_vector(M, eM, Y, true; kwargs...) + is_vector(M, eM, Y; error=M.mode, kwargs...) return Y end function adjoint_action!(M::ValidationManifold, Y, p, X; kwargs...) - is_point(M, p, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) eM = Identity(M.manifold) - is_vector(M, eM, X, true; kwargs...) + is_vector(M, eM, X; error=M.mode, kwargs...) adjoint_action!(M.manifold, array_value(Y), array_value(p), array_value(X)) - is_vector(M, eM, Y, true; kwargs...) + is_vector(M, eM, Y; error=M.mode, kwargs...) return Y end @@ -28,95 +28,95 @@ Identity(M::ValidationManifold) = array_point(Identity(M.manifold)) identity_element!(M::ValidationManifold, p) = identity_element!(M.manifold, array_value(p)) function Base.inv(M::ValidationManifold, p; kwargs...) - is_point(M, p, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) q = array_point(inv(M.manifold, array_value(p))) - is_point(M, q, true; kwargs...) + is_point(M, q; error=M.mode, kwargs...) return q end function inv!(M::ValidationManifold, q, p; kwargs...) - is_point(M, p, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) inv!(M.manifold, array_value(q), array_value(p)) - is_point(M, q, true; kwargs...) + is_point(M, q; error=M.mode, kwargs...) return q end function lie_bracket(M::ValidationManifold, X, Y) eM = Identity(M.manifold) - is_vector(M, eM, X, true) - is_vector(M, eM, Y, true) + is_vector(M, eM, X; error=M.mode) + is_vector(M, eM, Y; error=M.mode) Z = ValidationTVector(lie_bracket(M.manifold, array_value(X), array_value(Y))) - is_vector(M, eM, Z, true) + is_vector(M, eM, Z; error=M.mode) return Z end function lie_bracket!(M::ValidationManifold, Z, X, Y) eM = Identity(M.manifold) - is_vector(M, eM, X, true) - is_vector(M, eM, Y, true) + is_vector(M, eM, X; error=M.mode) + is_vector(M, eM, Y; error=M.mode) lie_bracket!(M.manifold, array_value(Z), array_value(X), array_value(Y)) - is_vector(M, eM, Z, true) + is_vector(M, eM, Z; error=M.mode) return Z end function compose(M::ValidationManifold, p, q; kwargs...) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) x = array_point(compose(M.manifold, array_value(p), array_value(q))) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end function compose(M::ValidationManifold, p::Identity, q; kwargs...) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) x = array_point(compose(M.manifold, p, array_value(q))) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end function compose(M::ValidationManifold, p, q::Identity; kwargs...) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) x = array_point(compose(M.manifold, array_value(p), q)) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end function compose!(M::ValidationManifold, x, p, q; kwargs...) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) compose!(M.manifold, array_value(x), array_value(p), array_value(q)) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end function compose!(M::ValidationManifold, x, p::Identity, q; kwargs...) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) compose!(M.manifold, array_value(x), array_value(p), array_value(q)) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end function compose!(M::ValidationManifold, x, p, q::Identity; kwargs...) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) compose!(M.manifold, array_value(x), array_value(p), array_value(q)) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end function translate(M::ValidationManifold, p, q, conv::ActionDirectionAndSide; kwargs...) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) x = array_point(translate(M.manifold, array_value(p), array_value(q), conv)) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end function translate!(M::ValidationManifold, x, p, q, conv::ActionDirectionAndSide; kwargs...) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) translate!(M.manifold, array_value(x), array_value(p), array_value(q), conv) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end @@ -127,10 +127,10 @@ function inverse_translate( conv::ActionDirectionAndSide; kwargs..., ) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) x = array_point(inverse_translate(M.manifold, array_value(p), array_value(q), conv)) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end @@ -142,10 +142,10 @@ function inverse_translate!( conv::ActionDirectionAndSide; kwargs..., ) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) inverse_translate!(M.manifold, array_value(x), array_value(p), array_value(q), conv) - is_point(M, x, true; kwargs...) + is_point(M, x; error=M.mode, kwargs...) return x end @@ -157,14 +157,14 @@ function translate_diff( conv::ActionDirectionAndSide; kwargs..., ) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) - is_vector(M, q, X, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) + is_vector(M, q, X; error=M.mode, kwargs...) Y = ValidationTVector( translate_diff(M.manifold, array_value(p), array_value(q), array_value(X), conv), ) pq = translate(M, p, q, conv) - is_vector(M, pq, Y, true; kwargs...) + is_vector(M, pq, Y; error=M.mode, kwargs...) return Y end @@ -177,9 +177,9 @@ function translate_diff!( conv::ActionDirectionAndSide; kwargs..., ) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) - is_vector(M, q, X, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) + is_vector(M, q, X; error=M.mode, kwargs...) translate_diff!( M.manifold, array_value(Y), @@ -189,7 +189,7 @@ function translate_diff!( conv, ) pq = translate(M, p, q, conv) - is_vector(M, pq, Y, true; kwargs...) + is_vector(M, pq, Y; error=M.mode, kwargs...) return Y end @@ -201,9 +201,9 @@ function inverse_translate_diff( conv::ActionDirectionAndSide; kwargs..., ) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) - is_vector(M, q, X, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) + is_vector(M, q, X; error=M.mode, kwargs...) Y = ValidationTVector( inverse_translate_diff( M.manifold, @@ -214,7 +214,7 @@ function inverse_translate_diff( ), ) pinvq = inverse_translate(M, p, q, conv) - is_vector(M, pinvq, Y, true; kwargs...) + is_vector(M, pinvq, Y; error=M.mode, kwargs...) return Y end @@ -227,9 +227,9 @@ function inverse_translate_diff!( conv::ActionDirectionAndSide; kwargs..., ) - is_point(M, p, true; kwargs...) - is_point(M, q, true; kwargs...) - is_vector(M, q, X, true; kwargs...) + is_point(M, p; error=M.mode, kwargs...) + is_point(M, q; error=M.mode, kwargs...) + is_vector(M, q, X; error=M.mode, kwargs...) inverse_translate_diff!( M.manifold, array_value(Y), @@ -239,34 +239,34 @@ function inverse_translate_diff!( conv, ) pinvq = inverse_translate(M, p, q, conv) - is_vector(M, pinvq, Y, true; kwargs...) + is_vector(M, pinvq, Y; error=M.mode, kwargs...) return Y end function exp_lie(M::ValidationManifold, X; kwargs...) - is_vector(M, Identity(M.manifold), array_value(X), true; kwargs...) + is_vector(M, Identity(M.manifold), array_value(X); error=M.mode, kwargs...) q = array_point(exp_lie(M.manifold, array_value(X))) - is_point(M, q, true; kwargs...) + is_point(M, q; error=M.mode, kwargs...) return q end function exp_lie!(M::ValidationManifold, q, X; kwargs...) - is_vector(M, Identity(M.manifold), array_value(X), true; kwargs...) + is_vector(M, Identity(M.manifold), array_value(X); error=M.mode, kwargs...) exp_lie!(M.manifold, array_value(q), array_value(X)) - is_point(M, q, true; kwargs...) + is_point(M, q; error=M.mode, kwargs...) return q end function log_lie(M::ValidationManifold, q; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, q; error=M.mode, kwargs...) X = ValidationTVector(log_lie(M.manifold, array_value(q))) - is_vector(M, Identity(M.manifold), array_value(X), true; kwargs...) + is_vector(M, Identity(M.manifold), array_value(X); error=M.mode, kwargs...) return X end function log_lie!(M::ValidationManifold, X, q; kwargs...) - is_point(M, q, true; kwargs...) + is_point(M, q; error=M.mode, kwargs...) log_lie!(M.manifold, array_value(X), array_value(q)) - is_vector(M, Identity(M.manifold), array_value(X), true; kwargs...) + is_vector(M, Identity(M.manifold), array_value(X); error=M.mode, kwargs...) return X end diff --git a/src/manifolds/StiefelSubmersionMetric.jl b/src/manifolds/StiefelSubmersionMetric.jl index 601db4d684..d75d641c80 100644 --- a/src/manifolds/StiefelSubmersionMetric.jl +++ b/src/manifolds/StiefelSubmersionMetric.jl @@ -69,7 +69,7 @@ function exp!(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetr mul!(C, X, p') C .-= C' mul!(tmp, p, A) - mul!(C, tmp, p', -(2α + 1) / (α + 1), true) + mul!(C, tmp, p', -(2α + 1) / (α + 1); error=:error) rmul!(A, α / (α + 1)) mul!(tmp, p, exp(A)) mul!(q, exp(C), tmp) diff --git a/test/groups/circle_group.jl b/test/groups/circle_group.jl index 41f3b81469..f14ceeac1c 100644 --- a/test/groups/circle_group.jl +++ b/test/groups/circle_group.jl @@ -27,7 +27,7 @@ using Manifolds: @test identity_element(G, fill(1.0f0)) == fill(1.0f0) @test !is_point(G, Identity(AdditionOperation())) ef = Identity(AdditionOperation()) - @test_throws DomainError is_point(G, ef, true) + @test_throws DomainError is_point(G, ef; error=:error) @test_throws DomainError is_vector(G, ef, X, true; check_base_point=true) end diff --git a/test/groups/general_linear.jl b/test/groups/general_linear.jl index 2e0ac7b143..04e5123b68 100644 --- a/test/groups/general_linear.jl +++ b/test/groups/general_linear.jl @@ -64,20 +64,20 @@ using NLsolve @testset "Real" begin G = GeneralLinear(3) - @test_throws ManifoldDomainError is_point(G, randn(2, 3), true) - @test_throws ManifoldDomainError is_point(G, randn(2, 2), true) - @test_throws ManifoldDomainError 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 is_point(G, Float64[0 0 1; 0 1 1; 1 1 1], true) - @test is_point(G, Identity(G), true) + @test_throws ManifoldDomainError is_point(G, randn(2, 3); error=:error) + @test_throws ManifoldDomainError is_point(G, randn(2, 2); error=:error) + @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 3, 3); error=:error) + @test_throws DomainError is_point(G, zeros(3, 3); error=:error) + @test_throws DomainError is_point(G, Float64[0 0 0; 0 1 1; 1 1 1]; error=:error) + @test is_point(G, Float64[0 0 1; 0 1 1; 1 1 1]; error=:error) + @test is_point(G, Identity(G); error=:error) @test_throws ManifoldDomainError is_vector( G, Float64[0 1 1; 0 1 1; 1 0 0], randn(3, 3), true, ) - @test is_vector(G, Float64[0 0 1; 0 1 1; 1 1 1], randn(3, 3), true) + @test is_vector(G, Float64[0 0 1; 0 1 1; 1 1 1], randn(3, 3); error=:error) types = [Matrix{Float64}] pts = [ @@ -140,20 +140,24 @@ using NLsolve @testset "Complex" begin G = GeneralLinear(2, ℂ) - @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 2, 3), true) - @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 3, 3), true) - @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, Identity(G), true) - @test_throws ManifoldDomainError is_point(G, Float64[0 0 0; 0 1 1; 1 1 1], true) + @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 2, 3); error=:error) + @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 3, 3); error=:error) + @test_throws DomainError is_point(G, zeros(2, 2); error=:error) + @test_throws DomainError is_point(G, ComplexF64[1 im; 1 im]; error=:error) + @test is_point(G, ComplexF64[1 1; im 1]; error=:error) + @test is_point(G, Identity(G); error=:error) + @test_throws ManifoldDomainError is_point( + G, + Float64[0 0 0; 0 1 1; 1 1 1]; + error=:error, + ) @test_throws ManifoldDomainError is_vector( G, ComplexF64[im im; im im], randn(ComplexF64, 2, 2), true, ) - @test is_vector(G, ComplexF64[1 im; im im], randn(ComplexF64, 2, 2), true) + @test is_vector(G, ComplexF64[1 im; im im], randn(ComplexF64, 2, 2); error=:error) types = [Matrix{ComplexF64}] pts = [ diff --git a/test/groups/general_unitary_groups.jl b/test/groups/general_unitary_groups.jl index ed07437cd2..1a7c69bdb7 100644 --- a/test/groups/general_unitary_groups.jl +++ b/test/groups/general_unitary_groups.jl @@ -147,21 +147,21 @@ include("group_utils.jl") p = ones(2, 2) q = project(SU2, p) - @test is_point(SU2, q, true) + @test is_point(SU2, q; error=:error) q2 = allocate(q) project!(SU2, q2, p) @test q == q2 p2 = copy(p) p2[1, 1] = -1 q2 = project(SU2, p2) - @test is_point(SU2, q2, true) + @test is_point(SU2, q2; error=:error) p3 = [2.0 0; 0.0 2.0] #real pos determinant @test project(SU2, p3) == p3 ./ 2 Xe = ones(2, 2) X = project(SU2, q, Xe) @test is_vector(SU2, q, X) - @test_throws ManifoldDomainError is_vector(SU2, p, X, true, true) # base point wrong - @test_throws DomainError is_vector(SU2, q, Xe, true, true) # Xe not skew hermitian + @test_throws ManifoldDomainError is_vector(SU2, p, X, true; error=:error) # base point wrong + @test_throws DomainError is_vector(SU2, q, Xe, true; error=:error) # Xe not skew hermitian @test_throws DomainError is_vector( SU2, Identity(AdditionOperation()), @@ -170,7 +170,7 @@ include("group_utils.jl") true, ) # base point wrong e = Identity(MultiplicationOperation()) - @test_throws DomainError is_vector(SU2, e, Xe, true, true) # Xe not skew hermitian + @test_throws DomainError is_vector(SU2, e, Xe, true; error=:error) # Xe not skew hermitian @test manifold_volume(SpecialUnitary(1)) ≈ 1 @test manifold_volume(SpecialUnitary(2)) ≈ 2 * π^2 diff --git a/test/groups/groups_general.jl b/test/groups/groups_general.jl index 1b65556f0c..39a445722c 100644 --- a/test/groups/groups_general.jl +++ b/test/groups/groups_general.jl @@ -29,7 +29,7 @@ using Manifolds: Identity(AdditionOperation()), Identity(MultiplicationOperation()), ) - @test_throws DomainError is_point(G, Identity(AdditionOperation()), true) + @test_throws DomainError is_point(G, Identity(AdditionOperation()); error=:error) @test is_point(G, eg) @test_throws MethodError is_identity(G, 1) # same error as before i.e. dispatch isapprox works @test Manifolds.check_size(G, eg) === nothing @@ -40,9 +40,14 @@ using Manifolds: ) isa DomainError @test !is_vector(G, Identity(AdditionOperation()), X) # wrong identity - @test_throws DomainError is_vector(G, Identity(AdditionOperation()), X, true) + @test_throws DomainError is_vector( + G, + Identity(AdditionOperation()), + X; + error=:error, + ) # identity_element for G not implemented - @test_throws MethodError is_vector(G, eg, X, true) + @test_throws MethodError is_vector(G, eg, X; error=:error) @test Identity(NotImplementedOperation()) === eg @test Identity(NotImplementedOperation) === eg @test !is_point(G, Identity(AdditionOperation())) diff --git a/test/groups/special_euclidean.jl b/test/groups/special_euclidean.jl index 3cca03e570..684ac64cd5 100644 --- a/test/groups/special_euclidean.jl +++ b/test/groups/special_euclidean.jl @@ -210,21 +210,21 @@ using Manifolds: 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) + @test_throws DomainError is_vector(G, p, X; error=:error) 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) + @test_throws DomainError is_vector(G, p, X2; error=:error) p[n + 1, n + 1] = 0.1 - @test_throws DomainError is_point(G, p, true) + @test_throws DomainError is_point(G, p; error=:error) 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) + @test_throws DomainError is_point(G, p2; error=:error) # exp/log_lie for ProductGroup on arrays X = copy(G, p, X_pts[1]) p3 = exp_lie(G, X) diff --git a/test/groups/special_linear.jl b/test/groups/special_linear.jl index d463223ee4..e98de6b6ca 100644 --- a/test/groups/special_linear.jl +++ b/test/groups/special_linear.jl @@ -30,13 +30,13 @@ using NLsolve @testset "Real" begin G = SpecialLinear(3) - @test_throws ManifoldDomainError is_point(G, randn(2, 3), true) - @test_throws ManifoldDomainError is_point(G, Float64[2 1; 1 1], true) - @test_throws ManifoldDomainError is_point(G, [1 0 im; im 0 0; 0 -1 0], true) - @test_throws ManifoldDomainError 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 is_point(G, Float64[1 1 1; 2 2 1; 2 3 3], true) - @test is_point(G, Identity(G), true) + @test_throws ManifoldDomainError is_point(G, randn(2, 3); error=:error) + @test_throws ManifoldDomainError is_point(G, Float64[2 1; 1 1]; error=:error) + @test_throws ManifoldDomainError is_point(G, [1 0 im; im 0 0; 0 -1 0]; error=:error) + @test_throws ManifoldDomainError is_point(G, zeros(3, 3); error=:error) + @test_throws DomainError is_point(G, Float64[1 3 3; 1 1 2; 1 2 3]; error=:error) + @test is_point(G, Float64[1 1 1; 2 2 1; 2 3 3]; error=:error) + @test is_point(G, Identity(G); error=:error) @test_throws ManifoldDomainError is_vector( G, Float64[2 3 2; 3 1 2; 1 1 1], @@ -126,16 +126,16 @@ using NLsolve @testset "Complex" begin G = SpecialLinear(2, ℂ) - @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 2, 3), true) - @test_throws DomainError is_point(G, randn(2, 2), true) + @test_throws ManifoldDomainError is_point(G, randn(ComplexF64, 2, 3); error=:error) + @test_throws DomainError is_point(G, randn(2, 2); error=:error) @test_throws ManifoldDomainError 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 is_point(G, ComplexF64[im 1; -2 im], true) - @test is_point(G, Identity(G), true) + @test_throws DomainError is_point(G, ComplexF64[1 im; im 1]; error=:error) + @test is_point(G, ComplexF64[im 1; -2 im]; error=:error) + @test is_point(G, Identity(G); error=:error) @test_throws ManifoldDomainError is_vector( G, ComplexF64[-1+im -1; -im 1], diff --git a/test/manifolds/centered_matrices.jl b/test/manifolds/centered_matrices.jl index 994519eb5b..3113febd70 100644 --- a/test/manifolds/centered_matrices.jl +++ b/test/manifolds/centered_matrices.jl @@ -14,13 +14,13 @@ include("../utils.jl") @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{3,2}},ℝ} @test is_flat(M) @test check_point(M, A) === nothing - @test_throws ManifoldDomainError is_point(M, B, true) - @test_throws ManifoldDomainError is_point(M, C, true) - @test_throws DomainError is_point(M, D, true) + @test_throws ManifoldDomainError is_point(M, B; error=:error) + @test_throws ManifoldDomainError is_point(M, C; error=:error) + @test_throws DomainError is_point(M, D; error=:error) @test check_vector(M, A, A) === nothing - @test_throws DomainError is_vector(M, A, D, true) - @test_throws ManifoldDomainError is_vector(M, D, A, true) - @test_throws ManifoldDomainError is_vector(M, A, B, true) + @test_throws DomainError is_vector(M, A, D; error=:error) + @test_throws ManifoldDomainError is_vector(M, D, A; error=:error) + @test_throws ManifoldDomainError is_vector(M, A, B; error=:error) @test manifold_dimension(M) == 4 @test A == project!(M, A, A) @test A == project(M, A, A) diff --git a/test/manifolds/cholesky_space.jl b/test/manifolds/cholesky_space.jl index f9ee129128..2cbd2e11db 100644 --- a/test/manifolds/cholesky_space.jl +++ b/test/manifolds/cholesky_space.jl @@ -41,18 +41,18 @@ include("../utils.jl") pt3f = [2.0 0.0 1.0; 0.0 1.0 0.0; 0.0 0.0 4.0] # no lower and nonsym pt4 = [2.0 0.0 0.0; 1.0 2.0 0.0; 0.0 0.0 4.0] @test !is_point(M, pt1f) - @test_throws DomainError is_point(M, pt1f, true) + @test_throws DomainError is_point(M, pt1f; error=:error) @test !is_point(M, pt2f) - @test_throws DomainError is_point(M, pt2f, true) + @test_throws DomainError is_point(M, pt2f; error=:error) @test !is_point(M, pt3f) - @test_throws DomainError is_point(M, pt3f, true) + @test_throws DomainError is_point(M, pt3f; error=:error) @test is_point(M, pt4) @test !is_vector(M, pt3f, pt1f) - @test_throws DomainError is_vector(M, pt3f, pt1f, true) + @test_throws DomainError is_vector(M, pt3f, pt1f; error=:error) @test !is_vector(M, pt4, pt1f) - @test_throws DomainError is_vector(M, pt4, pt1f, true) + @test_throws DomainError is_vector(M, pt4, pt1f; error=:error) @test !is_vector(M, pt4, pt3f) - @test_throws DomainError is_vector(M, pt4, pt3f, true) + @test_throws DomainError is_vector(M, pt4, pt3f; error=:error) @test is_vector(M, pt4, pt2f) end @testset "field parameter" begin diff --git a/test/manifolds/circle.jl b/test/manifolds/circle.jl index d0ab19fd92..07d3eddb6d 100644 --- a/test/manifolds/circle.jl +++ b/test/manifolds/circle.jl @@ -13,13 +13,13 @@ using Manifolds: TFVector, CoTFVector @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_throws DomainError is_point(M, 9.0; error=:error) + @test_throws DomainError is_point(M, zeros(3, 3); error=:error) @test !is_vector(M, 9.0, 0.0) @test !is_vector(M, zeros(3, 3), zeros(3, 3)) - @test_throws DomainError is_vector(M, 9.0, 0.0, true) - @test_throws DomainError is_vector(M, zeros(3, 3), zeros(3, 3), true) - @test_throws DomainError is_vector(M, 0.0, zeros(3, 3), true) + @test_throws DomainError is_vector(M, 9.0, 0.0; error=:error) + @test_throws DomainError is_vector(M, zeros(3, 3), zeros(3, 3); error=:error) + @test_throws DomainError is_vector(M, 0.0, zeros(3, 3); error=:error) @test is_vector(M, 0.0, 0.0) @test get_coordinates(M, Ref(0.0), Ref(2.0), DefaultOrthonormalBasis())[] ≈ 2.0 @test get_coordinates( @@ -206,11 +206,11 @@ using Manifolds: TFVector, CoTFVector @test is_vector(Mc, 1im, 0.0) @test is_point(Mc, 1im) @test !is_point(Mc, 1 + 1im) - @test_throws DomainError is_point(Mc, 1 + 1im, true) + @test_throws DomainError is_point(Mc, 1 + 1im; error=:error) @test !is_vector(Mc, 1 + 1im, 0.0) - @test_throws DomainError is_vector(Mc, 1 + 1im, 0.0, true) + @test_throws DomainError is_vector(Mc, 1 + 1im, 0.0; error=:error) @test !is_vector(Mc, 1im, 2im) - @test_throws DomainError is_vector(Mc, 1im, 2im, true) + @test_throws DomainError is_vector(Mc, 1im, 2im; error=:error) rrcv = Manifolds.RieszRepresenterCotangentVector(Mc, 0.0 + 0.0im, 1.0im) @test flat(Mc, 0.0 + 0.0im, 1.0im) == rrcv @test sharp(Mc, 0.0 + 0.0im, rrcv) == 1.0im diff --git a/test/manifolds/elliptope.jl b/test/manifolds/elliptope.jl index e99850d4dc..e36fae782f 100644 --- a/test/manifolds/elliptope.jl +++ b/test/manifolds/elliptope.jl @@ -11,11 +11,11 @@ include("../utils.jl") @test is_point(M, q, true; atol=10^-15) @test base_manifold(M) === M qN = [2.0 0.0; 0.0 1.0; 1/sqrt(2) -1/sqrt(2); 1/sqrt(2) 1/sqrt(2)] - @test_throws DomainError is_point(M, qN, true) + @test_throws DomainError is_point(M, qN; error=:error) Y = [0.0 1.0; 1.0 0.0; 0.0 0.0; 0.0 0.0] - @test is_vector(M, q, Y, true) + @test is_vector(M, q, Y; error=:error) YN = [0.1 1.0; 1.0 0.1; 0.0 0.0; 0.0 0.0] - @test_throws DomainError is_vector(M, q, YN, true) + @test_throws DomainError is_vector(M, q, YN; error=:error) qE = similar(q) embed!(M, qE, q) qE2 = embed(M, q) diff --git a/test/manifolds/essential_manifold.jl b/test/manifolds/essential_manifold.jl index a466998f82..26c3e34db4 100644 --- a/test/manifolds/essential_manifold.jl +++ b/test/manifolds/essential_manifold.jl @@ -22,11 +22,11 @@ include("../utils.jl") np3 = [r1, r2, r3] @test !is_point(M, r1) # first two components of r1 are not rotations - @test_throws DomainError is_point(M, r1, true) - @test_throws DomainError is_point(M, np3, true) + @test_throws DomainError is_point(M, r1; error=:error) + @test_throws DomainError is_point(M, np3; error=:error) @test is_point(M, p1) - @test_throws ComponentManifoldError is_point(M, np1, true) - @test_throws CompositeManifoldError is_point(M, np2, true) + @test_throws ComponentManifoldError is_point(M, np1; error=:error) + @test_throws CompositeManifoldError is_point(M, np2; error=:error) @test !is_vector(M, p1, 0.0) @test_throws DomainError is_vector( M, diff --git a/test/manifolds/euclidean.jl b/test/manifolds/euclidean.jl index b263d31195..c92fe16ce4 100644 --- a/test/manifolds/euclidean.jl +++ b/test/manifolds/euclidean.jl @@ -58,15 +58,25 @@ using FiniteDifferences @test Y == X # real manifold does not allow complex values - @test_throws DomainError is_point(Ec, [:a, :b, :b], true) - @test_throws DomainError is_point(E, [1.0, 1.0im, 0.0], true) - @test_throws DomainError is_point(E, [1], true) - @test_throws DomainError is_vector(Ec, [:a, :b, :b], [1.0, 1.0, 0.0], true) - @test_throws DomainError is_vector(E, [1.0, 1.0im, 0.0], [1.0, 1.0, 0.0], true) # real manifold does not allow complex values - @test_throws DomainError is_vector(E, [1], [1.0, 1.0, 0.0], true) - @test_throws DomainError is_vector(E, [0.0, 0.0, 0.0], [1.0], true) - @test_throws DomainError is_vector(E, [0.0, 0.0, 0.0], [1.0, 0.0, 1.0im], true) - @test_throws DomainError is_vector(Ec, [0.0, 0.0, 0.0], [:a, :b, :c], true) + @test_throws DomainError is_point(Ec, [:a, :b, :b]; error=:error) + @test_throws DomainError is_point(E, [1.0, 1.0im, 0.0], error=:error) + @test_throws DomainError is_point(E, [1]; error=:error) + @test_throws DomainError is_vector(Ec, [:a, :b, :b], [1.0, 1.0, 0.0]; error=:error) + @test_throws DomainError is_vector( + E, + [1.0, 1.0im, 0.0], + [1.0, 1.0, 0.0]; + error=:error, + ) # real manifold does not allow complex values + @test_throws DomainError is_vector(E, [1], [1.0, 1.0, 0.0]; error=:error) + @test_throws DomainError is_vector(E, [0.0, 0.0, 0.0], [1.0]; error=:error) + @test_throws DomainError is_vector( + E, + [0.0, 0.0, 0.0], + [1.0, 0.0, 1.0im]; + error=:error, + ) + @test_throws DomainError is_vector(Ec, [0.0, 0.0, 0.0], [:a, :b, :c]; error=:error) @test E^2 === Euclidean(3, 2, parameter=param) @test ^(E, 2) === Euclidean(3, 2, parameter=param) diff --git a/test/manifolds/fixed_rank.jl b/test/manifolds/fixed_rank.jl index 969b5c1ba7..71c083bd5e 100644 --- a/test/manifolds/fixed_rank.jl +++ b/test/manifolds/fixed_rank.jl @@ -53,9 +53,9 @@ include("../utils.jl") @test !is_flat(M) @test !is_flat(Mc) @test !is_point(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2)) - @test_throws DomainError is_point(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2), true) + @test_throws DomainError is_point(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2); error=:error) @test is_point(M2, p2) - @test_throws DomainError is_point(M2, [1.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test_throws DomainError is_point(M2, [1.0 0.0; 0.0 1.0; 0.0 0.0]; error=:error) @test Manifolds.check_point(M2, [1.0 0.0; 0.0 1.0; 0.0 0.0]) isa DomainError @test default_retraction_method(M) === PolarRetraction() @@ -71,13 +71,23 @@ include("../utils.jl") @test_throws ManifoldDomainError is_vector( M, SVDMPoint([1.0 0.0; 0.0 0.0], 2), - X, - true, + X; + error=:error, ) @test !is_vector(M, p, UMVTVector(p.U, X.M, p.Vt, 2)) - @test_throws DomainError is_vector(M, p, UMVTVector(p.U, X.M, p.Vt, 2), true) + @test_throws DomainError is_vector( + M, + p, + UMVTVector(p.U, X.M, p.Vt, 2); + error=:error, + ) @test !is_vector(M, p, UMVTVector(X.U, X.M, p.Vt, 2)) - @test_throws DomainError is_vector(M, p, UMVTVector(X.U, X.M, p.Vt, 2), true) + @test_throws DomainError is_vector( + M, + p, + UMVTVector(X.U, X.M, p.Vt, 2); + error=:error, + ) @test is_point(M, p) @test is_vector(M, p, X) @@ -134,15 +144,23 @@ include("../utils.jl") xM = embed(M, p) @test is_point(M, xM) @test !is_point(M, xM[1:2, :]) - @test_throws DomainError is_point(M, xM[1:2, :], true) - @test_throws DomainError is_point(FixedRankMatrices(3, 2, 1), p, true) - @test_throws DomainError is_point(FixedRankMatrices(3, 2, 1), xM, true) + @test_throws DomainError is_point(M, xM[1:2, :]; error=:error) + @test_throws DomainError is_point( + FixedRankMatrices(3, 2, 1), + p; + error=:error, + ) + @test_throws DomainError is_point( + FixedRankMatrices(3, 2, 1), + xM; + error=:error, + ) xF1 = SVDMPoint(2 * p.U, p.S, p.Vt) @test !is_point(M, xF1) - @test_throws DomainError is_point(M, xF1, true) + @test_throws DomainError is_point(M, xF1; error=:error) xF2 = SVDMPoint(p.U, p.S, 2 * p.Vt) @test !is_point(M, xF2) - @test_throws DomainError is_point(M, xF2, true) + @test_throws DomainError is_point(M, xF2; error=:error) # copyto yC = allocate(y) copyto!(M, yC, y) diff --git a/test/manifolds/generalized_grassmann.jl b/test/manifolds/generalized_grassmann.jl index c0206f8c9a..243b3af019 100644 --- a/test/manifolds/generalized_grassmann.jl +++ b/test/manifolds/generalized_grassmann.jl @@ -14,22 +14,27 @@ include("../utils.jl") @test manifold_dimension(M) == 2 @test base_manifold(M) === M @test !is_flat(M) - @test_throws ManifoldDomainError is_point(M, [1.0, 0.0, 0.0, 0.0], true) + @test_throws ManifoldDomainError is_point(M, [1.0, 0.0, 0.0, 0.0]; error=:error) @test_throws ManifoldDomainError is_point( M, - 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], - true, + 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0]; + error=:error, ) - @test_throws ManifoldDomainError is_point(M, 2 * p, true) + @test_throws ManifoldDomainError is_point(M, 2 * p; error=:error) @test !is_vector(M, p, [0.0, 0.0, 1.0, 0.0]) - @test_throws ManifoldDomainError is_vector(M, p, [0.0, 0.0, 1.0, 0.0], true) @test_throws ManifoldDomainError is_vector( M, p, - 1 * im * zero_vector(M, p), - true, + [0.0, 0.0, 1.0, 0.0]; + error=:error, ) - @test_throws ManifoldDomainError is_vector(M, p, X, true) + @test_throws ManifoldDomainError is_vector( + M, + p, + 1 * im * zero_vector(M, p); + error=:error, + ) + @test_throws ManifoldDomainError is_vector(M, p, X; error=:error) @test injectivity_radius(M) == π / 2 @test injectivity_radius(M, ExponentialRetraction()) == π / 2 @test injectivity_radius(M, p) == π / 2 @@ -90,9 +95,9 @@ include("../utils.jl") @testset "Type $T" for T in types pts = convert.(T, [p, q, r]) @test !is_point(M, 2 * p) - @test_throws ManifoldDomainError !is_point(M, 2 * r, true) + @test_throws ManifoldDomainError !is_point(M, 2 * r; error=:error) @test !is_vector(M, p, q) - @test_throws ManifoldDomainError is_vector(M, p, q, true) + @test_throws ManifoldDomainError is_vector(M, p, q; error=:error) test_manifold( M, pts, diff --git a/test/manifolds/generalized_stiefel.jl b/test/manifolds/generalized_stiefel.jl index efffa2bf90..9dc195e0e7 100644 --- a/test/manifolds/generalized_stiefel.jl +++ b/test/manifolds/generalized_stiefel.jl @@ -14,22 +14,22 @@ include("../utils.jl") @test manifold_dimension(M) == 3 @test base_manifold(M) === M @test !is_flat(M) - @test_throws DomainError is_point(M, [1.0, 0.0, 0.0, 0.0], true) + @test_throws DomainError is_point(M, [1.0, 0.0, 0.0, 0.0]; error=:error) @test_throws ManifoldDomainError is_point( M, - 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], - true, + 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0]; + error=:error, ) - @test_throws DomainError is_point(M, 2 * p, true) + @test_throws DomainError is_point(M, 2 * p; error=:error) @test !is_vector(M, p, [0.0, 0.0, 1.0, 0.0]) - @test_throws DomainError is_vector(M, p, [0.0, 0.0, 1.0, 0.0], true) + @test_throws DomainError is_vector(M, p, [0.0, 0.0, 1.0, 0.0]; error=:error) @test_throws ManifoldDomainError is_vector( M, p, - 1 * im * zero_vector(M, p), - true, + 1 * im * zero_vector(M, p); + error=:error, ) - @test_throws DomainError is_vector(M, p, X, true) + @test_throws DomainError is_vector(M, p, X; error=:error) @test default_retraction_method(M) == ProjectionRetraction() @test is_point(M, rand(M)) @test is_vector(M, p, rand(M; vector_at=p)) @@ -79,9 +79,9 @@ include("../utils.jl") @testset "Type $T" for T in types pts = convert.(T, [p, y, z]) @test !is_point(M, 2 * p) - @test_throws DomainError !is_point(M, 2 * p, true) + @test_throws DomainError !is_point(M, 2 * p; error=:error) @test !is_vector(M, p, y) - @test_throws DomainError is_vector(M, p, y, true) + @test_throws DomainError is_vector(M, p, y; error=:error) test_manifold( M, pts, diff --git a/test/manifolds/graph.jl b/test/manifolds/graph.jl index 8490ce8f69..73f0cd5c12 100644 --- a/test/manifolds/graph.jl +++ b/test/manifolds/graph.jl @@ -15,12 +15,12 @@ include("../utils.jl") manifold_dimension(M) * ne(G) @test is_point(N, x) @test !is_point(N, [x..., [0.0, 0.0]]) # an entry too much - @test_throws DomainError is_point(N, [x..., [0.0, 0.0]], true) + @test_throws DomainError is_point(N, [x..., [0.0, 0.0]]; error=:error) @test is_vector(N, x, log(N, x, y)) @test !is_vector(N, x[1:2], log(N, x, y)) - @test_throws DomainError is_vector(N, x[1:2], log(N, x, y), true) + @test_throws DomainError is_vector(N, x[1:2], log(N, x, y); error=:error) @test !is_vector(N, x[1:2], log(N, x, y)[1:2]) - @test_throws DomainError is_vector(N, x, log(N, x, y)[1:2], true) + @test_throws DomainError is_vector(N, x, log(N, x, y)[1:2]; error=:error) @test incident_log(N, x) == [x[2] - x[1], x[1] - x[2] + x[3] - x[2], x[2] - x[3]] pts = [x, y, z] @@ -35,12 +35,12 @@ include("../utils.jl") NE = GraphManifold(G, M, EdgeManifold()) @test is_point(NE, x[1:2]) @test !is_point(NE, x) # an entry too much - @test_throws DomainError is_point(NE, x, true) + @test_throws DomainError is_point(NE, x; error=:error) @test is_vector(NE, x[1:2], log(N, x, y)[1:2]) @test !is_vector(NE, x, log(N, x, y)) - @test_throws DomainError is_vector(NE, x, log(N, x, y), true) + @test_throws DomainError is_vector(NE, x, log(N, x, y); error=:error) @test !is_vector(N, x[1:2], log(N, x, y)) - @test_throws DomainError is_vector(NE, x[1:2], log(N, x, y), true) + @test_throws DomainError is_vector(NE, x[1:2], log(N, x, y); error=:error) test_manifold( NE, diff --git a/test/manifolds/grassmann.jl b/test/manifolds/grassmann.jl index 21474fb4e6..4750b35a32 100644 --- a/test/manifolds/grassmann.jl +++ b/test/manifolds/grassmann.jl @@ -19,36 +19,40 @@ include("../utils.jl") Manifolds.RowwiseMultiplicationAction(M, Orthogonal(2)) @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) @test !is_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) - @test_throws ManifoldDomainError is_point(M, [2.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test_throws ManifoldDomainError is_point( + M, + [2.0 0.0; 0.0 1.0; 0.0 0.0]; + error=:error, + ) @test_throws ManifoldDomainError is_vector( M, [2.0 0.0; 0.0 1.0; 0.0 0.0], - zeros(3, 2), - true, + zeros(3, 2); + error=:error, ) @test_throws ManifoldDomainError is_vector( M, [1.0 0.0; 0.0 1.0; 0.0 0.0], - ones(3, 2), - true, + ones(3, 2); + error=:error, ) - @test is_point(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test is_point(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]; error=:error) @test_throws ManifoldDomainError is_point( M, - 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], - true, + 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0]; + error=:error, ) @test is_vector( M, [1.0 0.0; 0.0 1.0; 0.0 0.0], - zero_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]), - true, + zero_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]); + error=:error, ) @test_throws ManifoldDomainError is_vector( M, [1.0 0.0; 0.0 1.0; 0.0 0.0], - 1im * zero_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]), - true, + 1im * zero_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0]); + error=:error, ) @test injectivity_radius(M) == π / 2 @test injectivity_radius(M, ExponentialRetraction()) == π / 2 @@ -135,8 +139,8 @@ include("../utils.jl") @test is_vector( M, p2, - vector_transport_to(M, p1, X, p2, ProjectionTransport()), - true; + vector_transport_to(M, p1, X, p2, ProjectionTransport()); + error=:error, atol=10^-15, ) end @@ -159,12 +163,16 @@ include("../utils.jl") @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) @test !is_vector(M, [1.0 0.0; 0.0 1.0; 0.0 0.0], [0.0, 0.0, 1.0, 0.0]) @test Manifolds.allocation_promotion_function(M, exp!, (1,)) == complex - @test_throws ManifoldDomainError is_point(M, [2.0 0.0; 0.0 1.0; 0.0 0.0], true) + @test_throws ManifoldDomainError is_point( + M, + [2.0 0.0; 0.0 1.0; 0.0 0.0]; + error=:error, + ) @test_throws ManifoldDomainError is_vector( M, [2.0 0.0; 0.0 1.0; 0.0 0.0], - zeros(3, 2), - true, + zeros(3, 2); + error=:error, ) @test_throws ManifoldDomainError is_vector( M, @@ -230,7 +238,7 @@ include("../utils.jl") p = reshape([im, 0.0, 0.0], 3, 1) @test is_point(G, p) X = reshape([-0.5; 0.5; 0], 3, 1) - @test_throws ManifoldDomainError is_vector(G, p, X, true) + @test_throws ManifoldDomainError is_vector(G, p, X; error=:error) Y = project(G, p, X) @test is_vector(G, p, Y) end @@ -307,16 +315,16 @@ include("../utils.jl") M = Grassmann(3, 2) p = StiefelPoint([1.0 0.0; 0.0 1.0; 0.0 0.0]) X = StiefelTVector([0.0 1.0; -1.0 0.0; 0.0 0.0]) - @test is_point(M, p, true) - @test is_vector(M, p, X, true) + @test is_point(M, p; error=:error) + @test is_vector(M, p, X; error=:error) @test repr(p) == "StiefelPoint($(p.value))" @test repr(X) == "StiefelTVector($(X.value))" M2 = Stiefel(3, 2) - @test is_point(M2, p, true) - @test is_vector(M2, p, X, true) + @test is_point(M2, p; error=:error) + @test is_vector(M2, p, X; error=:error) p2 = convert(ProjectorPoint, p) - @test is_point(M, p2, true) + @test is_point(M, p2; error=:error) p3 = convert(ProjectorPoint, p.value) @test p2.value == p3.value X2 = ProjectorTVector([0.0 0.0 1.0; 0.0 0.0 1.0; 1.0 1.0 0.0]) @@ -326,20 +334,20 @@ include("../utils.jl") # rank just 1 pF1 = ProjectorPoint([1.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]) - @test_throws DomainError is_point(M, pF1, true) + @test_throws DomainError is_point(M, pF1; error=:error) # not equal to its square pF2 = ProjectorPoint([1.0 0.0 0.0; 0.0 0.0 0.0; 0.0 1.0 0.0]) - @test_throws DomainError is_point(M, pF2, true) + @test_throws DomainError is_point(M, pF2; error=:error) # not symmetric pF3 = ProjectorPoint([0.0 1.0 0.0; 0.0 1.0 0.0; 0.0 0.0 0.0]) - @test_throws DomainError is_point(M, pF3, true) + @test_throws DomainError is_point(M, pF3; error=:error) # not symmetric XF1 = ProjectorTVector([0.0 0.0 1.0; 0.0 0.0 1.0; 1.0 0.0 0.0]) - @test_throws DomainError is_vector(M, p2, XF1, true) + @test_throws DomainError is_vector(M, p2, XF1; error=:error) # XF2 is not p2*XF2 + XF2*p2 XF2 = ProjectorTVector(ones(3, 3)) - @test_throws DomainError is_vector(M, p2, XF2, true) + @test_throws DomainError is_vector(M, p2, XF2; error=:error) # embed for Stiefel with its point M2 = Stiefel(3, 2) diff --git a/test/manifolds/hyperbolic.jl b/test/manifolds/hyperbolic.jl index 8d270e0992..26633060bd 100644 --- a/test/manifolds/hyperbolic.jl +++ b/test/manifolds/hyperbolic.jl @@ -16,17 +16,22 @@ include("../utils.jl") @test isinf(injectivity_radius(M, [0.0, 0.0, 1.0], ExponentialRetraction())) @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) @test !is_vector(M, [0.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]) - @test_throws DomainError is_point(M, [2.0, 0.0, 0.0], true) + @test_throws DomainError is_point(M, [2.0, 0.0, 0.0]; error=:error) @test !is_point(M, [2.0, 0.0, 0.0]) @test !is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) @test_throws ManifoldDomainError is_vector( M, [1.0, 0.0, 0.0], - [1.0, 0.0, 0.0], - true, + [1.0, 0.0, 0.0]; + error=:error, ) @test !is_vector(M, [0.0, 0.0, 1.0], [1.0, 0.0, 1.0]) - @test_throws DomainError is_vector(M, [0.0, 0.0, 1.0], [1.0, 0.0, 1.0], true) + @test_throws DomainError is_vector( + M, + [0.0, 0.0, 1.0], + [1.0, 0.0, 1.0]; + error=:error, + ) @test is_default_metric(M, MinkowskiMetric()) @test manifold_dimension(M) == 2 @@ -71,22 +76,34 @@ include("../utils.jl") @test is_point(M, pB) @test convert(AbstractVector, pB) == p # convert back yields again p @test convert(HyperboloidPoint, pB).value == pH.value - @test_throws DomainError is_point(M, PoincareBallPoint([0.9, 0.0, 0.0]), true) - @test_throws DomainError is_point(M, PoincareBallPoint([1.1, 0.0]), true) + @test_throws DomainError is_point( + M, + PoincareBallPoint([0.9, 0.0, 0.0]); + error=:error, + ) + @test_throws DomainError is_point(M, PoincareBallPoint([1.1, 0.0]); error=:error) @test is_vector(M, pB, PoincareBallTVector([2.0, 2.0])) @test_throws DomainError is_vector( M, pB, - PoincareBallTVector([2.0, 2.0, 3.0]), - true, + PoincareBallTVector([2.0, 2.0, 3.0]); + error=:error, ) pS = convert(PoincareHalfSpacePoint, p) pS2 = convert(PoincareHalfSpacePoint, pB) pS3 = convert(PoincareHalfSpacePoint, pH) - @test_throws DomainError is_point(M, PoincareHalfSpacePoint([0.0, 0.0, 1.0]), true) - @test_throws DomainError is_point(M, PoincareHalfSpacePoint([0.0, -1.0]), true) + @test_throws DomainError is_point( + M, + PoincareHalfSpacePoint([0.0, 0.0, 1.0]); + error=:error, + ) + @test_throws DomainError is_point( + M, + PoincareHalfSpacePoint([0.0, -1.0]); + error=:error, + ) @test pS.value == pS2.value @test pS.value == pS3.value @@ -255,7 +272,7 @@ include("../utils.jl") B = get_basis(M, p, DefaultOrthonormalBasis()) V = get_vectors(M, p, B) for v in V - @test is_vector(M, p, v, true) + @test is_vector(M, p, v; error=:error) for b in [DefaultOrthonormalBasis(), DiagonalizingOrthonormalBasis(V[1])] @test isapprox(M, p, v, get_vector(M, p, get_coordinates(M, p, v, b), b)) end diff --git a/test/manifolds/multinomial_doubly_stochastic.jl b/test/manifolds/multinomial_doubly_stochastic.jl index bdfa960a36..dd48083f72 100644 --- a/test/manifolds/multinomial_doubly_stochastic.jl +++ b/test/manifolds/multinomial_doubly_stochastic.jl @@ -9,15 +9,15 @@ include("../utils.jl") @test is_point(M, p) @test is_vector(M, p, X) pf1 = [0.1 0.9 0.1; 0.1 0.9 0.1; 0.1 0.1 0.9] #not sum 1 - @test_throws ManifoldDomainError is_point(M, pf1, true) + @test_throws ManifoldDomainError is_point(M, pf1; error=:error) pf2r = [0.1 0.9 0.1; 0.8 0.05 0.15; 0.1 0.05 0.75] - @test_throws DomainError is_point(M, pf2r, true) - @test_throws ManifoldDomainError is_point(M, pf2r', true) + @test_throws DomainError is_point(M, pf2r; error=:error) + @test_throws ManifoldDomainError is_point(M, pf2r'; error=:error) pf3 = [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0] # contains nonpositive entries - @test_throws ManifoldDomainError is_point(M, pf3, true) + @test_throws ManifoldDomainError is_point(M, pf3; error=:error) Xf2c = [-0.1 0.0 0.1; -0.2 0.1 0.1; 0.2 -0.1 -0.1] #nonzero columns - @test_throws ManifoldDomainError is_vector(M, p, Xf2c, true) - @test_throws DomainError is_vector(M, p, Xf2c', true) + @test_throws ManifoldDomainError is_vector(M, p, Xf2c; error=:error) + @test_throws DomainError is_vector(M, p, Xf2c'; error=:error) @test representation_size(M) == (3, 3) @test !is_flat(M) pE = similar(p) diff --git a/test/manifolds/multinomial_matrices.jl b/test/manifolds/multinomial_matrices.jl index ffc57cf903..68f96f586b 100644 --- a/test/manifolds/multinomial_matrices.jl +++ b/test/manifolds/multinomial_matrices.jl @@ -18,9 +18,9 @@ include("../utils.jl") p = [2, 0, 0] p2 = [p p] @test !is_point(M, p) - @test_throws DomainError is_point(M, p, true) + @test_throws DomainError is_point(M, p; error=:error) @test !is_point(M, p2) - @test_throws CompositeManifoldError is_point(M, p2, true) + @test_throws CompositeManifoldError is_point(M, p2; error=:error) @test !is_vector(M, p2, 0.0) @test_throws CompositeManifoldError{ComponentManifoldError{Int64,DomainError}} is_vector( M, @@ -29,10 +29,10 @@ include("../utils.jl") true, ) @test !is_vector(M, p2, [-1.0, 0.0, 0.0]) - @test_throws DomainError is_vector(M, p, [-1.0, 0.0, 0.0], true) + @test_throws DomainError is_vector(M, p, [-1.0, 0.0, 0.0]; error=:error) @test injectivity_radius(M) ≈ 0 x = [0.5 0.4 0.1; 0.5 0.4 0.1]' - @test_throws DomainError is_vector(M, x, [0.0, 0.0, 0.0], true) # tangent wrong + @test_throws DomainError is_vector(M, x, [0.0, 0.0, 0.0]; error=:error) # tangent wrong y = [0.6 0.3 0.1; 0.4 0.5 0.1]' z = [0.3 0.6 0.1; 0.6 0.3 0.1]' test_manifold( diff --git a/test/manifolds/multinomial_symmetric.jl b/test/manifolds/multinomial_symmetric.jl index afe1c7ad79..58450b5bac 100644 --- a/test/manifolds/multinomial_symmetric.jl +++ b/test/manifolds/multinomial_symmetric.jl @@ -9,15 +9,15 @@ include("../utils.jl") @test is_point(M, p) @test is_vector(M, p, X) pf1 = [0.1 0.9 0.1; 0.1 0.9 0.1; 0.1 0.1 0.9] #not symmetric - @test_throws ManifoldDomainError is_point(M, pf1, true) + @test_throws ManifoldDomainError is_point(M, pf1; error=:error) pf2 = [0.8 0.1 0.1; 0.1 0.8 0.1; 0.1 0.1 0.9] # cols do not sum to 1 - @test_throws ManifoldDomainError is_point(M, pf2, true) + @test_throws ManifoldDomainError is_point(M, pf2; error=:error) pf3 = [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0] # contains nonpositive entries - @test_throws ManifoldDomainError is_point(M, pf3, true) + @test_throws ManifoldDomainError is_point(M, pf3; error=:error) Xf1 = [0.0 1.0 -1.0; 0.0 0.0 0.0; 0.0 0.0 0.0] # not symmetric - @test_throws ManifoldDomainError is_vector(M, p, Xf1, true) + @test_throws ManifoldDomainError is_vector(M, p, Xf1; error=:error) Xf2 = [0.0 -1.0 0.0; -1.0 0.0 0.0; 0.0 0.0 0.0] # nonzero sums - @test_throws ManifoldDomainError is_vector(M, p, Xf2, true) + @test_throws ManifoldDomainError is_vector(M, p, Xf2; error=:error) @test representation_size(M) == (3, 3) @test !is_flat(M) pE = similar(p) diff --git a/test/manifolds/oblique.jl b/test/manifolds/oblique.jl index 59dde46ff5..2463653660 100644 --- a/test/manifolds/oblique.jl +++ b/test/manifolds/oblique.jl @@ -15,9 +15,9 @@ include("../utils.jl") p = [2, 0, 0] p2 = [p p] @test !is_point(M, p) - @test_throws DomainError is_point(M, p, true) + @test_throws DomainError is_point(M, p; error=:error) @test !is_point(M, p2) - @test_throws CompositeManifoldError is_point(M, p2, true) + @test_throws CompositeManifoldError is_point(M, p2; error=:error) @test !is_vector(M, p2, 0.0) @test_throws CompositeManifoldError{ComponentManifoldError{Int64,DomainError}} is_vector( M, @@ -26,10 +26,10 @@ include("../utils.jl") true, ) @test !is_vector(M, p2, [0.0, 0.0, 0.0]) - @test_throws DomainError is_vector(M, p, [0.0, 0.0, 0.0], true) # p wrong + @test_throws DomainError is_vector(M, p, [0.0, 0.0, 0.0]; error=:error) # p wrong @test injectivity_radius(M) ≈ π x = [1.0 0.0 0.0; 1.0 0.0 0.0]' - @test_throws DomainError is_vector(M, x, [0.0, 0.0, 0.0], true) # tangent wrong + @test_throws DomainError is_vector(M, x, [0.0, 0.0, 0.0]; error=:error) # tangent wrong y = [1.0 0.0 0.0; 1/sqrt(2) 1/sqrt(2) 0.0]' z = [1/sqrt(2) 1/sqrt(2) 0.0; 1.0 0.0 0.0]' basis_types = (DefaultOrthonormalBasis(),) diff --git a/test/manifolds/positive_numbers.jl b/test/manifolds/positive_numbers.jl index 65687159b6..c914ba0940 100644 --- a/test/manifolds/positive_numbers.jl +++ b/test/manifolds/positive_numbers.jl @@ -11,7 +11,7 @@ include("../utils.jl") @test manifold_dimension(M) == 1 @test !is_flat(M) @test !is_point(M, -1.0) - @test_throws DomainError is_point(M, -1.0, true) + @test_throws DomainError is_point(M, -1.0; error=:error) @test is_vector(M, 1.0, 0.0) @test vector_transport_to(M, 1.0, 3.0, 2.0, ParallelTransport()) == 6.0 @test retract(M, 1.0, 1.0) == exp(M, 1.0, 1.0) diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index 0be2f281cd..84621be366 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -157,8 +157,8 @@ end M = PowerManifold(Sphere(2), NestedPowerRepresentation(), 2) p = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] X = [[0.0, 0.0, 0.0], [0.0, 1.0, 0.0]] - @test_throws ComponentManifoldError is_point(M, X, true) - @test_throws ComponentManifoldError is_vector(M, p, X, true) + @test_throws ComponentManifoldError is_point(M, X; error=:error) + @test_throws ComponentManifoldError is_vector(M, p, X; error=:error) end @testset "power vector transport" begin diff --git a/test/manifolds/probability_simplex.jl b/test/manifolds/probability_simplex.jl index f4b4308c26..f0d99d2e1e 100644 --- a/test/manifolds/probability_simplex.jl +++ b/test/manifolds/probability_simplex.jl @@ -11,16 +11,16 @@ include("../utils.jl") Y = [-0.1, 0.05, 0.05] @test repr(M) == "ProbabilitySimplex(2; boundary=:open)" @test is_point(M, p) - @test_throws DomainError is_point(M, p .+ 1, true) - @test_throws ManifoldDomainError is_point(M, [0], true) - @test_throws DomainError is_point(M, -ones(3), true) + @test_throws DomainError is_point(M, p .+ 1; error=:error) + @test_throws ManifoldDomainError is_point(M, [0]; error=:error) + @test_throws DomainError is_point(M, -ones(3); error=:error) @test manifold_dimension(M) == 2 @test !is_flat(M) @test is_vector(M, p, X) @test is_vector(M, p, Y) - @test_throws ManifoldDomainError is_vector(M, p .+ 1, X, true) - @test_throws ManifoldDomainError is_vector(M, p, zeros(4), true) - @test_throws DomainError is_vector(M, p, Y .+ 1, true) + @test_throws ManifoldDomainError is_vector(M, p .+ 1, X; error=:error) + @test_throws ManifoldDomainError is_vector(M, p, zeros(4); error=:error) + @test_throws DomainError is_vector(M, p, Y .+ 1; error=:error) @test injectivity_radius(M, p) == injectivity_radius(M, p, ExponentialRetraction()) @test injectivity_radius(M, p, SoftmaxRetraction()) == injectivity_radius(M, p) @@ -131,7 +131,7 @@ include("../utils.jl") X = [0, 1, -1] Y = [0, 2, -2] @test is_point(Mb, p) - @test_throws DomainError is_point(Mb, p .- 1, true) + @test_throws DomainError is_point(Mb, p .- 1; error=:error) @test inner(Mb, p, X, Y) == 8 @test_throws ArgumentError ProbabilitySimplex(2; boundary=:tomato) diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index f1b312b4cc..d9d74cd1e6 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -35,7 +35,7 @@ using RecursiveArrayTools: ArrayPartition @test Manifolds.number_of_components(Mse) == 2 # test that arrays are not points - @test_throws DomainError is_point(Mse, [1, 2], true) + @test_throws DomainError is_point(Mse, [1, 2]; error=:error) @test check_point(Mse, [1, 2]) isa DomainError @test_throws DomainError is_vector(Mse, 1, [1, 2], true; check_base_point=false) @test check_vector(Mse, 1, [1, 2]; check_base_point=false) isa DomainError @@ -161,10 +161,10 @@ using RecursiveArrayTools: ArrayPartition X = ArrayPartition(X1, X2) pf = ArrayPartition(p1, X1) Xf = ArrayPartition(X1, p2) - @test is_point(Mpr, p, true) - @test_throws CompositeManifoldError is_point(Mpr, X, true) - @test_throws ComponentManifoldError is_vector(Mpr, pf, X, true) - @test_throws ComponentManifoldError is_vector(Mpr, p, Xf, true) + @test is_point(Mpr, p; error=:error) + @test_throws CompositeManifoldError is_point(Mpr, X; error=:error) + @test_throws ComponentManifoldError is_vector(Mpr, pf, X; error=:error) + @test_throws ComponentManifoldError is_vector(Mpr, p, Xf; error=:error) end @testset "arithmetic" begin diff --git a/test/manifolds/projective_space.jl b/test/manifolds/projective_space.jl index 88e76daa33..eb6d21018e 100644 --- a/test/manifolds/projective_space.jl +++ b/test/manifolds/projective_space.jl @@ -11,10 +11,15 @@ include("../utils.jl") @test is_flat(ProjectiveSpace(1)) @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) @test !is_vector(M, [1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0]) - @test_throws DomainError is_point(M, [2.0, 0.0, 0.0], true) + @test_throws DomainError is_point(M, [2.0, 0.0, 0.0]; error=:error) @test !is_point(M, [2.0, 0.0, 0.0]) @test !is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) - @test_throws DomainError is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], true) + @test_throws DomainError is_vector( + M, + [1.0, 0.0, 0.0], + [1.0, 0.0, 0.0]; + error=:error, + ) @test injectivity_radius(M) == π / 2 @test injectivity_radius(M, ExponentialRetraction()) == π / 2 @test injectivity_radius(M, [1.0, 0.0, 0.0]) == π / 2 @@ -108,7 +113,7 @@ include("../utils.jl") @test Manifolds.allocation_promotion_function(M, exp!, (1,)) == complex @test !is_point(M, [1.0 + 0im, 0.0, 0.0, 0.0]) @test !is_vector(M, [1.0 + 0im, 0.0, 0.0, 0.0], [0.0 + 0im, 1.0, 0.0]) - @test_throws DomainError is_point(M, [1.0, im, 0.0], true) + @test_throws DomainError is_point(M, [1.0, im, 0.0]; error=:error) @test !is_point(M, [1.0, im, 0.0]) @test !is_vector(M, [1.0 + 0im, 0.0, 0.0], [1.0 + 0im, 0.0, 0.0]) @test !is_vector(M, [1.0 + 0im, 0.0, 0.0], [-0.5im, 0.0, 0.0]) @@ -198,21 +203,21 @@ include("../utils.jl") @test !is_flat(M) @test !is_point(M, quat([1.0, 0.0, 0.0, 0.0])) @test !is_vector(M, quat([1.0, 0.0, 0.0, 0.0]), quat([0.0, 1.0, 0.0])) - @test_throws DomainError is_point(M, [1.0, quat(0, 1, 0, 0), 0.0], true) + @test_throws DomainError is_point(M, [1.0, quat(0, 1, 0, 0), 0.0]; error=:error) @test !is_point(M, [1.0, quat(0, 1, 0, 0), 0.0]) @test !is_vector(M, quat([1.0, 0.0, 0.0]), quat([1.0, 0.0, 0.0])) @test !is_vector(M, quat([1.0, 0.0, 0.0]), [quat(0, -0.5, 0, 0), 0.0, 0.0]) @test_throws DomainError is_vector( M, Quaternion[1.0, 0.0, 0.0], - Quaternion[1.0, 0.0, 0.0], - true, + Quaternion[1.0, 0.0, 0.0]; + error=:error, ) @test_throws DomainError is_vector( M, quat([1.0, 0.0, 0.0]), - quat([-0.5, 0.0, 0.0]), - true, + quat([-0.5, 0.0, 0.0]); + error=:error, ) @test injectivity_radius(M) == π / 2 @test injectivity_radius(M, ExponentialRetraction()) == π / 2 diff --git a/test/manifolds/rotations.jl b/test/manifolds/rotations.jl index 186886e82b..3fa56e491b 100644 --- a/test/manifolds/rotations.jl +++ b/test/manifolds/rotations.jl @@ -169,40 +169,40 @@ include("../utils.jl") @testset "Test AbstractManifold Point and Tangent Vector checks" begin M = Rotations(2) for p in [1, [2.0 0.0; 0.0 1.0], [1.0 0.5; 0.0 1.0]] - @test_throws DomainError is_point(M, p, true) + @test_throws DomainError is_point(M, p; error=:error) @test !is_point(M, p) end p = one(zeros(2, 2)) @test is_point(M, p) - @test is_point(M, p, true) + @test is_point(M, p; error=:error) for X in [1, [0.0 1.0; 0.0 0.0]] - @test_throws DomainError is_vector(M, p, X, true) + @test_throws DomainError is_vector(M, p, X; error=:error) @test !is_vector(M, p, X) end X = [0.0 1.0; -1.0 0.0] @test is_vector(M, p, X) - @test is_vector(M, p, X, true) + @test is_vector(M, p, X; error=:error) end @testset "Project point" begin M = Rotations(2) p = Matrix{Float64}(I, 2, 2) p1 = project(M, p) - @test is_point(M, p1, true) + @test is_point(M, p1; error=:error) M = Rotations(3) p = collect(reshape(1.0:9.0, (3, 3))) p2 = project(M, p) - @test is_point(M, p2, true) + @test is_point(M, p2; error=:error) rng = MersenneTwister(44) x3 = project(M, randn(rng, 3, 3)) - @test is_point(M, x3, true) + @test is_point(M, x3; error=:error) end @testset "Convert from Lie algebra representation of tangents to Riemannian submanifold representation" begin M = Rotations(3) p = project(M, collect(reshape(1.0:9.0, (3, 3)))) x = [[0, -1, 3] [1, 0, 2] [-3, -2, 0]] - @test is_vector(M, p, x, true) + @test is_vector(M, p, x; error=:error) @test embed(M, p, x) == p * x Y = zeros((3, 3)) embed!(M, Y, p, x) diff --git a/test/manifolds/shape_space.jl b/test/manifolds/shape_space.jl index b64829ad15..777b298914 100644 --- a/test/manifolds/shape_space.jl +++ b/test/manifolds/shape_space.jl @@ -19,12 +19,12 @@ include("../utils.jl") 0.3248027612629014 0.440253011955812 -0.7650557732187135 0.26502337825226757 -0.06175142812400016 -0.20327195012826738 ] - @test_throws DomainError is_point(M, [1 0 1; 1 -1 0] / 2, true) + @test_throws DomainError is_point(M, [1 0 1; 1 -1 0] / 2; error=:error) @test_throws DomainError is_vector( M, [-1 0 1.0; 0 0 0] / sqrt(2), - [1.0 0 1; 1 -1 0], - true, + [1.0 0 1; 1 -1 0]; + error=:error, ) test_manifold( M, @@ -85,8 +85,8 @@ end @test abs(norm(M, p1, X1) - norm(M, p1, X1h)) < 1e-16 end - @test_throws ManifoldDomainError is_point(M, [1 0 1; 1 -1 0], true) - @test_throws ManifoldDomainError is_vector(M, p1, [1 0 1; 1 -1 0], true) + @test_throws ManifoldDomainError is_point(M, [1 0 1; 1 -1 0]; error=:error) + @test_throws ManifoldDomainError is_vector(M, p1, [1 0 1; 1 -1 0]; error=:error) @testset "exp/distance/norm" begin q1 = exp(M, p1, X1) diff --git a/test/manifolds/skewhermitian.jl b/test/manifolds/skewhermitian.jl index 1cfae74130..79686d1a0d 100644 --- a/test/manifolds/skewhermitian.jl +++ b/test/manifolds/skewhermitian.jl @@ -24,13 +24,13 @@ end @test is_flat(M) @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{3,3}},ℝ} @test check_point(M, B_skewsym) === nothing - @test_throws DomainError is_point(M, A, true) - @test_throws ManifoldDomainError is_point(M, C, true) - @test_throws DomainError is_point(M, D, true) + @test_throws DomainError is_point(M, A; error=:error) + @test_throws ManifoldDomainError is_point(M, C; error=:error) + @test_throws DomainError is_point(M, D; error=:error) @test check_vector(M, B_skewsym, B_skewsym) === nothing - @test_throws DomainError is_vector(M, B_skewsym, A, true) - @test_throws ManifoldDomainError is_vector(M, A, B_skewsym, true) - @test_throws DomainError is_vector(M, B_skewsym, D, true) + @test_throws DomainError is_vector(M, B_skewsym, A; error=:error) + @test_throws ManifoldDomainError is_vector(M, A, B_skewsym; error=:error) + @test_throws DomainError is_vector(M, B_skewsym, D; error=:error) @test_throws ManifoldDomainError is_vector( M, B_skewsym, diff --git a/test/manifolds/spd_fixed_determinant.jl b/test/manifolds/spd_fixed_determinant.jl index 445d005a31..21f01506fa 100644 --- a/test/manifolds/spd_fixed_determinant.jl +++ b/test/manifolds/spd_fixed_determinant.jl @@ -7,13 +7,13 @@ include("../utils.jl") @test is_point(M, p) # Determinant is 4 @test !is_point(M, 2.0 .* p) - @test_throws DomainError is_point(M, 2.0 .* p, true) + @test_throws DomainError is_point(M, 2.0 .* p; error=:error) # X = [0.0 0.1; 0.1 0.0] @test is_vector(M, p, X) Y = [1.0 0.1; 0.1 1.0] @test !is_vector(M, p, Y) - @test_throws DomainError is_vector(M, p, Y, true) + @test_throws DomainError is_vector(M, p, Y; error=:error) @test project(M, 2.0 .* p) == p @test project(M, p, Y) == X diff --git a/test/manifolds/spectrahedron.jl b/test/manifolds/spectrahedron.jl index f40f67651a..ffbf6290f4 100644 --- a/test/manifolds/spectrahedron.jl +++ b/test/manifolds/spectrahedron.jl @@ -9,14 +9,14 @@ include("../utils.jl") @test !is_flat(M) q = [1.0 0.0; 0.0 1.0; 1.0 1.0; -1.0 1.0] q = q / norm(q) - @test is_point(M, q, true) + @test is_point(M, q; error=:error) @test base_manifold(M) === M qN = [2.0 0.0; 0.0 1.0; 1/sqrt(2) -1/sqrt(2); 1/sqrt(2) 1/sqrt(2)] - @test_throws DomainError is_point(M, qN, true) + @test_throws DomainError is_point(M, qN; error=:error) Y = [0.0 1.0; 1.0 0.0; 0.0 0.0; 0.0 0.0] - @test is_vector(M, q, Y, true) + @test is_vector(M, q, Y; error=:error) YN = [0.1 1.0; 1.0 0.1; 0.0 0.0; 0.0 0.0] - @test_throws DomainError is_vector(M, q, YN, true) + @test_throws DomainError is_vector(M, q, YN; error=:error) qE = similar(q) embed!(M, qE, q) qE2 = embed(M, q) diff --git a/test/manifolds/sphere.jl b/test/manifolds/sphere.jl index 4ef5e70340..3c04cb7be2 100644 --- a/test/manifolds/sphere.jl +++ b/test/manifolds/sphere.jl @@ -19,10 +19,15 @@ using ManifoldsBase: TFVector @test !is_default_metric(M, AffineInvariantMetric()) @test !is_point(M, [1.0, 0.0, 0.0, 0.0]) @test !is_vector(M, [1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]) - @test_throws DomainError is_point(M, [2.0, 0.0, 0.0], true) + @test_throws DomainError is_point(M, [2.0, 0.0, 0.0]; error=:error) @test !is_point(M, [2.0, 0.0, 0.0]) @test !is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) - @test_throws DomainError is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0], true) + @test_throws DomainError is_vector( + M, + [1.0, 0.0, 0.0], + [1.0, 0.0, 0.0]; + error=:error, + ) @test injectivity_radius(M, [1.0, 0.0, 0.0], ProjectionRetraction()) == π / 2 end types = [Vector{Float64}] diff --git a/test/manifolds/sphere_symmetric_matrices.jl b/test/manifolds/sphere_symmetric_matrices.jl index f5d63f5a6f..d6756bfb19 100644 --- a/test/manifolds/sphere_symmetric_matrices.jl +++ b/test/manifolds/sphere_symmetric_matrices.jl @@ -17,17 +17,17 @@ include("../utils.jl") @test !is_flat(M) @test typeof(get_embedding(M)) === ArraySphere{TypeParameter{Tuple{3,3}},ℝ} @test check_point(M, A) === nothing - @test_throws ManifoldDomainError is_point(M, B, true) - @test_throws ManifoldDomainError is_point(M, C, true) - @test_throws DomainError is_point(M, D, true) - @test_throws ManifoldDomainError is_point(M, E, true) + @test_throws ManifoldDomainError is_point(M, B; error=:error) + @test_throws ManifoldDomainError is_point(M, C; error=:error) + @test_throws DomainError is_point(M, D; error=:error) + @test_throws ManifoldDomainError is_point(M, E; error=:error) @test check_vector(M, A, zeros(3, 3)) === nothing - @test_throws ManifoldDomainError is_vector(M, A, B, true) - @test_throws ManifoldDomainError is_vector(M, A, C, true) - @test_throws ManifoldDomainError is_vector(M, A, D, true) - @test_throws ManifoldDomainError is_vector(M, D, A, true) - @test_throws ManifoldDomainError is_vector(M, A, E, true) - @test_throws DomainError is_vector(M, J, K, true) + @test_throws ManifoldDomainError is_vector(M, A, B; error=:error) + @test_throws ManifoldDomainError is_vector(M, A, C; error=:error) + @test_throws ManifoldDomainError is_vector(M, A, D; error=:error) + @test_throws ManifoldDomainError is_vector(M, D, A; error=:error) + @test_throws ManifoldDomainError is_vector(M, A, E; error=:error) + @test_throws DomainError is_vector(M, J, K; error=:error) @test manifold_dimension(M) == 5 A2 = similar(A) @test A == project!(M, A2, A) diff --git a/test/manifolds/stiefel.jl b/test/manifolds/stiefel.jl index 5c4d36f68a..d1a65bcea5 100644 --- a/test/manifolds/stiefel.jl +++ b/test/manifolds/stiefel.jl @@ -14,18 +14,18 @@ include("../utils.jl") @test !is_flat(M2) @test is_flat(Stiefel(2, 1)) base_manifold(M) === M - @test_throws ManifoldDomainError is_point(M, [1.0, 0.0, 0.0, 0.0], true) + @test_throws ManifoldDomainError is_point(M, [1.0, 0.0, 0.0, 0.0]; error=:error) @test_throws ManifoldDomainError is_point( M, - 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0], - true, + 1im * [1.0 0.0; 0.0 1.0; 0.0 0.0]; + error=:error, ) @test !is_vector(M, p, [0.0, 0.0, 1.0, 0.0]) @test_throws ManifoldDomainError is_vector( M, p, - 1 * im * zero_vector(M, p), - true, + 1 * im * zero_vector(M, p); + error=:error, ) @test default_retraction_method(M) === PolarRetraction() @test default_inverse_retraction_method(M) === PolarInverseRetraction() @@ -126,11 +126,11 @@ include("../utils.jl") pts = convert.(T, [x, y, z]) v = inverse_retract(M, x, y, PolarInverseRetraction()) @test !is_point(M, 2 * x) - @test_throws DomainError !is_point(M, 2 * x, true) + @test_throws DomainError !is_point(M, 2 * x; error=:error) @test !is_vector(M, 2 * x, v) - @test_throws ManifoldDomainError !is_vector(M, 2 * x, v, true) + @test_throws ManifoldDomainError !is_vector(M, 2 * x, v; error=:error) @test !is_vector(M, x, y) - @test_throws DomainError is_vector(M, x, y, true) + @test_throws DomainError is_vector(M, x, y; error=:error) test_manifold( M, pts, @@ -225,11 +225,11 @@ include("../utils.jl") pts = convert.(T, [x, y, z]) v = inverse_retract(M, x, y, PolarInverseRetraction()) @test !is_point(M, 2 * x) - @test_throws DomainError !is_point(M, 2 * x, true) + @test_throws DomainError !is_point(M, 2 * x; error=:error) @test !is_vector(M, 2 * x, v) - @test_throws ManifoldDomainError !is_vector(M, 2 * x, v, true) + @test_throws ManifoldDomainError !is_vector(M, 2 * x, v; error=:error) @test !is_vector(M, x, y) - @test_throws DomainError is_vector(M, x, y, true) + @test_throws DomainError is_vector(M, x, y; error=:error) test_manifold( M, pts, diff --git a/test/manifolds/symmetric.jl b/test/manifolds/symmetric.jl index 4a36cb8d19..efb47a2d13 100644 --- a/test/manifolds/symmetric.jl +++ b/test/manifolds/symmetric.jl @@ -19,13 +19,13 @@ include("../utils.jl") @test is_flat(M) @test typeof(get_embedding(M)) === Euclidean{TypeParameter{Tuple{3,3}},ℝ} @test check_point(M, B_sym) === nothing - @test_throws DomainError is_point(M, A, true) - @test_throws ManifoldDomainError is_point(M, C, true) - @test_throws ManifoldDomainError is_point(M, D, true) #embedding changes type + @test_throws DomainError is_point(M, A; error=:error) + @test_throws ManifoldDomainError is_point(M, C; error=:error) + @test_throws ManifoldDomainError is_point(M, D; error=:error) #embedding changes type @test check_vector(M, B_sym, B_sym) === nothing - @test_throws DomainError is_vector(M, B_sym, A, true) - @test_throws ManifoldDomainError is_vector(M, A, B_sym, true) - @test_throws ManifoldDomainError is_vector(M, B_sym, D, true) + @test_throws DomainError is_vector(M, B_sym, A; error=:error) + @test_throws ManifoldDomainError is_vector(M, A, B_sym; error=:error) + @test_throws ManifoldDomainError is_vector(M, B_sym, D; error=:error) @test_throws ManifoldDomainError is_vector( M, B_sym, diff --git a/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl b/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl index c0c6a3a54c..83eae0b079 100644 --- a/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl +++ b/test/manifolds/symmetric_positive_semidefinite_fixed_rank.jl @@ -9,7 +9,7 @@ include("../utils.jl") q = [1.0 0.0; 0.0 1.0; 0.0 0.0; 0.0 0.0] @test is_point(M, q) Y = [1.0 0.0; 0.0 0.0; 0.0 0.0; 0.0 0.0] - @test_throws DomainError is_point(M, Y, true) + @test_throws DomainError is_point(M, Y; error=:error) @test is_vector(M, q, Y) q2 = [2.0 1.0; 0.0 0.0; 0.0 1.0; 0.0 0.0] q3 = [0.0 0.0; 1.0 0.0; 0.0 1.0; 0.0 0.0] diff --git a/test/manifolds/symplectic.jl b/test/manifolds/symplectic.jl index b0ac697785..3ef22cd8be 100644 --- a/test/manifolds/symplectic.jl +++ b/test/manifolds/symplectic.jl @@ -80,12 +80,17 @@ using ManifoldDiff @test !is_flat(Sp_2) @test is_point(Sp_2, p_2) - @test_throws DomainError is_point(Sp_2, p_2 + I, true) + @test_throws DomainError is_point(Sp_2, p_2 + I; error=:error) @test is_vector(Sp_2, p_2, X1; atol=1.0e-6) @test is_vector(Sp_2, p_2, X2; atol=1.0e-12) @test is_vector(Sp_2, p_2, X1 + X2; atol=1.0e-6) - @test_throws DomainError is_vector(Sp_2, p_2, X1 + [0.1 0.1; -0.1 0.1], true) + @test_throws DomainError is_vector( + Sp_2, + p_2, + X1 + [0.1 0.1; -0.1 0.1]; + error=:error, + ) end @testset "Symplectic Inverse" begin I_2n = Array(I, 2, 2) diff --git a/test/manifolds/symplecticstiefel.jl b/test/manifolds/symplecticstiefel.jl index 8c01949fe8..319a4351d4 100644 --- a/test/manifolds/symplecticstiefel.jl +++ b/test/manifolds/symplecticstiefel.jl @@ -144,13 +144,13 @@ end @test !is_flat(SpSt_6_4) @test is_point(SpSt_6_4, p_6_4) - @test_throws DomainError is_point(SpSt_6_4, 2 * p_6_4, true) + @test_throws DomainError is_point(SpSt_6_4, 2 * p_6_4; error=:error) @test is_vector(SpSt_6_4, p_6_4, X1; atol=1.0e-12) @test is_vector(SpSt_6_4, p_6_4, X2; atol=1.0e-6) @test_throws DomainError is_vector(SpSt_6_4, p_6_4, X2, true; atol=1.0e-12) @test is_vector(SpSt_6_4, p_6_4, X1 + X2; atol=1.0e-6) - @test_throws DomainError is_vector(SpSt_6_4, p_6_4, X1 + p_6_4, true) + @test_throws DomainError is_vector(SpSt_6_4, p_6_4, X1 + p_6_4; error=:error) end @testset "Symplectic Inverse" begin I_2k = Array(I, 4, 4) diff --git a/test/manifolds/torus.jl b/test/manifolds/torus.jl index 27bfc94932..ddab72ceef 100644 --- a/test/manifolds/torus.jl +++ b/test/manifolds/torus.jl @@ -12,14 +12,14 @@ include("../utils.jl") @test manifold_dimension(M) == 2 @test is_flat(M) @test !is_point(M, 9.0) - @test_throws DomainError is_point(M, 9.0, true) + @test_throws DomainError is_point(M, 9.0; error=:error) @test !is_point(M, [9.0; 9.0]) - @test_throws CompositeManifoldError is_point(M, [9.0 9.0], true) - @test_throws CompositeManifoldError is_point(M, [9.0, 9.0], true) + @test_throws CompositeManifoldError is_point(M, [9.0 9.0]; error=:error) + @test_throws CompositeManifoldError is_point(M, [9.0, 9.0]; error=:error) @test !is_vector(M, [9.0; 9.0], 0.0) - @test_throws DomainError is_vector(M, 9.0, 0.0, true) # point false and checked + @test_throws DomainError is_vector(M, 9.0, 0.0; error=:error) # point false and checked @test !is_vector(M, [9.0; 9.0], [0.0; 0.0]) - @test_throws DomainError is_vector(M, [0.0, 0.0], 0.0, true) + @test_throws DomainError is_vector(M, [0.0, 0.0], 0.0; error=:error) @test injectivity_radius(M) ≈ π x = [1.0, 2.0] y = [-1.0, 2.0] diff --git a/test/manifolds/unitary_matrices.jl b/test/manifolds/unitary_matrices.jl index 49177569b8..6584e8b48e 100644 --- a/test/manifolds/unitary_matrices.jl +++ b/test/manifolds/unitary_matrices.jl @@ -12,11 +12,11 @@ using Quaternions @test injectivity_radius(M) == π * sqrt(2.0) @test !is_flat(M) p = project(M, ones(3, 3)) - @test is_point(M, p, true) - @test is_point(M, rand(M), true) + @test is_point(M, p; error=:error) + @test is_point(M, rand(M); error=:error) @test abs(rand(OrthogonalMatrices(1))[]) == 1 @test is_vector(M, p, rand(M; vector_at=p)) - @test is_point(M, rand(MersenneTwister(), M), true) + @test is_point(M, rand(MersenneTwister(), M); error=:error) @test abs(rand(MersenneTwister(), OrthogonalMatrices(1))[]) == 1 @test is_vector(M, p, rand(MersenneTwister(), M; vector_at=p)) end @@ -31,27 +31,27 @@ end @test injectivity_radius(M) == π # wrong length of size - @test_throws DomainError is_point(M, zeros(1), true) - @test_throws DomainError is_point(M, zeros(3, 3), true) + @test_throws DomainError is_point(M, zeros(1); error=:error) + @test_throws DomainError is_point(M, zeros(3, 3); error=:error) pF = 1 / 2 .* [1im 1im; -1im 1im] - @test_throws DomainError is_point(M, pF, true) + @test_throws DomainError is_point(M, pF; error=:error) # Determinant not one pF2 = [1im 1.0; 0.0 -1im] - @test_throws DomainError is_point(M, pF2, true) + @test_throws DomainError is_point(M, pF2; error=:error) p = [1im 0.0; 0.0 1im] - @test is_point(M, p, true) + @test is_point(M, p; error=:error) - @test_throws DomainError is_vector(M, p, zeros(1), true) - @test_throws DomainError is_vector(M, p, zeros(3, 3), true) + @test_throws DomainError is_vector(M, p, zeros(1); error=:error) + @test_throws DomainError is_vector(M, p, zeros(3, 3); error=:error) # not skew - @test_throws DomainError is_vector(M, p, ones(2, 2), true) + @test_throws DomainError is_vector(M, p, ones(2, 2); error=:error) X = [0.0 1.0; -1.0 0.0] - @test is_vector(M, p, X, true) + @test is_vector(M, p, X; error=:error) q = project(M, ones(2, 2)) - @test is_point(M, q, true) + @test is_point(M, q; error=:error) q2 = project(M, 1im * ones(2, 2)) - @test is_point(M, q2, true) + @test is_point(M, q2; error=:error) r = exp(M, p, X) X2 = log(M, p, r) @@ -96,11 +96,11 @@ end end # wrong length of size - @test_throws DomainError is_point(M, zeros(2, 2), true) + @test_throws DomainError is_point(M, zeros(2, 2); error=:error) # Determinant not one pF2 = [quat(0, 1, 0, 0) 1.0; 0.0 -quat(0, 1, 0, 0)] - @test_throws DomainError is_point(M, pF2, true) + @test_throws DomainError is_point(M, pF2; error=:error) p = QuaternionF64( 0.4815296357756736, 0.6041613272484806, @@ -111,9 +111,9 @@ end @test is_point(M, fill(p, 1, 1)) @test is_point(M, p) - @test_throws DomainError is_vector(M, p, zeros(2, 2), true) + @test_throws DomainError is_vector(M, p, zeros(2, 2); error=:error) # not skew - @test_throws DomainError is_vector(M, p, Quaternion(1, 0, 0, 0), true) + @test_throws DomainError is_vector(M, p, Quaternion(1, 0, 0, 0); error=:error) X = Quaternion(0.0, 0, 0, 1) @test is_vector(M, p, X) diff --git a/tutorials/getstarted.qmd b/tutorials/getstarted.qmd index 2e70ea10a1..dfb2f1b8e2 100644 --- a/tutorials/getstarted.qmd +++ b/tutorials/getstarted.qmd @@ -107,7 +107,7 @@ Note that the `LoadError:` is due to quarto, on `REPL` you would just get the `D ```{julia} #| error: true -is_point(M₂, [0, 0, 1.001], true) +is_point(M₂, [0, 0, 1.001]; error=:error) ``` #### 3. ``[The sphere](@ref SphereSection)``{=commonmark} @@ -152,7 +152,7 @@ and for these we again get informative error messages ```{julia} #| error: true -@expect_error is_vector(M₃, [1, 0, 0], [0.1, 1, 1], true) DomainError +@expect_error is_vector(M₃, [1, 0, 0], [0.1, 1, 1]; error=:error) DomainError ``` To learn about how to define a manifold youself check out the [🔗 How to define your own manifold](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/example.html) tutorial of [🔗 `ManifoldsBase.jl`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/)." @@ -229,7 +229,7 @@ p₃ = Manifolds.ArrayPartition([0, 0, 1], [0, 1, 0]) Here `ArrayPartition` taken from [🔗 `RecursiveArrayTools.jl`](https://github.com/SciML/RecursiveArrayTools.jl) to store the point on the product manifold efficiently in one array, still allowing efficient access to the product elements. ```{julia} -is_point(M₆, p₃, true) +is_point(M₆, p₃; error=:error) ``` But accessing single components still works the same." From f26d329dc0887fea91d9314063d83e28d5b6f886 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 16 Oct 2023 19:23:44 +0200 Subject: [PATCH 48/81] replaced a bit too eagerly --- src/manifolds/StiefelSubmersionMetric.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifolds/StiefelSubmersionMetric.jl b/src/manifolds/StiefelSubmersionMetric.jl index d75d641c80..601db4d684 100644 --- a/src/manifolds/StiefelSubmersionMetric.jl +++ b/src/manifolds/StiefelSubmersionMetric.jl @@ -69,7 +69,7 @@ function exp!(M::MetricManifold{ℝ,<:Stiefel{<:Any,ℝ},<:StiefelSubmersionMetr mul!(C, X, p') C .-= C' mul!(tmp, p, A) - mul!(C, tmp, p', -(2α + 1) / (α + 1); error=:error) + mul!(C, tmp, p', -(2α + 1) / (α + 1), true) rmul!(A, α / (α + 1)) mul!(tmp, p, exp(A)) mul!(q, exp(C), tmp) From 8e97d94fa757169dd7c3e5ff17569acc658ad1ce Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 16 Oct 2023 21:18:42 +0200 Subject: [PATCH 49/81] fix a few more errors --- test/groups/general_linear.jl | 8 ++++---- test/groups/special_linear.jl | 28 ++++++++++++++-------------- test/manifolds/projective_space.jl | 8 ++++---- test/manifolds/symplecticstiefel.jl | 14 ++++++++++---- 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/test/groups/general_linear.jl b/test/groups/general_linear.jl index 04e5123b68..59eaa631ad 100644 --- a/test/groups/general_linear.jl +++ b/test/groups/general_linear.jl @@ -74,8 +74,8 @@ using NLsolve @test_throws ManifoldDomainError is_vector( G, Float64[0 1 1; 0 1 1; 1 0 0], - randn(3, 3), - true, + randn(3, 3); + error=:error, ) @test is_vector(G, Float64[0 0 1; 0 1 1; 1 1 1], randn(3, 3); error=:error) @@ -154,8 +154,8 @@ using NLsolve @test_throws ManifoldDomainError is_vector( G, ComplexF64[im im; im im], - randn(ComplexF64, 2, 2), - true, + randn(ComplexF64, 2, 2); + error=:error, ) @test is_vector(G, ComplexF64[1 im; im im], randn(ComplexF64, 2, 2); error=:error) diff --git a/test/groups/special_linear.jl b/test/groups/special_linear.jl index e98de6b6ca..887dfa0569 100644 --- a/test/groups/special_linear.jl +++ b/test/groups/special_linear.jl @@ -40,22 +40,22 @@ using NLsolve @test_throws ManifoldDomainError is_vector( G, Float64[2 3 2; 3 1 2; 1 1 1], - randn(3, 3), - true; + randn(3, 3); + error=:error, atol=1e-6, ) @test_throws DomainError is_vector( G, Float64[2 1 2; 3 2 2; 2 2 1], - Float64[2 1 -1; 2 2 1; 1 1 -1], - true; + Float64[2 1 -1; 2 2 1; 1 1 -1]; + error=:error, atol=1e-6, ) @test is_vector( G, Float64[2 1 2; 3 2 2; 2 2 1], - Float64[-1 -1 -1; 1 -1 2; -1 -1 2], - true; + Float64[-1 -1 -1; 1 -1 2; -1 -1 2]; + error=:error, atol=1e-6, ) @@ -130,8 +130,8 @@ using NLsolve @test_throws DomainError is_point(G, randn(2, 2); error=:error) @test_throws ManifoldDomainError is_point( G, - ComplexF64[1 0 im; im 0 0; 0 -1 0], - true, + ComplexF64[1 0 im; im 0 0; 0 -1 0]; + error=:error, ) @test_throws DomainError is_point(G, ComplexF64[1 im; im 1]; error=:error) @test is_point(G, ComplexF64[im 1; -2 im]; error=:error) @@ -139,22 +139,22 @@ using NLsolve @test_throws ManifoldDomainError is_vector( G, ComplexF64[-1+im -1; -im 1], - ComplexF64[1-im 1+im; 1 -1+im], - true; + ComplexF64[1-im 1+im; 1 -1+im]; + error=:error, atol=1e-6, ) @test_throws DomainError is_vector( G, ComplexF64[1 1+im; -1+im -1], - ComplexF64[1-im -1-im; -im im], - true; + ComplexF64[1-im -1-im; -im im]; + error=:error, atol=1e-6, ) @test is_vector( G, ComplexF64[1 1+im; -1+im -1], - ComplexF64[1-im 1+im; 1 -1+im], - true; + ComplexF64[1-im 1+im; 1 -1+im]; + error=:error, atol=1e-6, ) diff --git a/test/manifolds/projective_space.jl b/test/manifolds/projective_space.jl index eb6d21018e..140da634cc 100644 --- a/test/manifolds/projective_space.jl +++ b/test/manifolds/projective_space.jl @@ -120,14 +120,14 @@ include("../utils.jl") @test_throws DomainError is_vector( M, [1.0 + 0im, 0.0, 0.0], - [1.0 + 0im, 0.0, 0.0], - true, + [1.0 + 0im, 0.0, 0.0]; + error=:error, ) @test_throws DomainError is_vector( M, [1.0 + 0im, 0.0, 0.0], - [-0.5im, 0.0, 0.0], - true, + [-0.5im, 0.0, 0.0]; + error=:error, ) @test injectivity_radius(M) == π / 2 @test injectivity_radius(M, ExponentialRetraction()) == π / 2 diff --git a/test/manifolds/symplecticstiefel.jl b/test/manifolds/symplecticstiefel.jl index 319a4351d4..db18123d23 100644 --- a/test/manifolds/symplecticstiefel.jl +++ b/test/manifolds/symplecticstiefel.jl @@ -148,7 +148,13 @@ end @test is_vector(SpSt_6_4, p_6_4, X1; atol=1.0e-12) @test is_vector(SpSt_6_4, p_6_4, X2; atol=1.0e-6) - @test_throws DomainError is_vector(SpSt_6_4, p_6_4, X2, true; atol=1.0e-12) + @test_throws DomainError is_vector( + SpSt_6_4, + p_6_4, + X2; + error=:error, + atol=1.0e-12, + ) @test is_vector(SpSt_6_4, p_6_4, X1 + X2; atol=1.0e-6) @test_throws DomainError is_vector(SpSt_6_4, p_6_4, X1 + p_6_4; error=:error) end @@ -223,14 +229,14 @@ end ]) A_6_4_proj = similar(A_6_4) Manifolds.project!(SpSt_6_4, A_6_4_proj, p_6_4, A_6_4) - @test is_vector(SpSt_6_4, p_6_4, A_6_4_proj, true; atol=2.0e-12) + @test is_vector(SpSt_6_4, p_6_4, A_6_4_proj; error=:error, atol=2.0e-12) end @testset "Generate random points/tangent vectors" begin M_big = SymplecticStiefel(20, 10) p_big = rand(M_big) - @test is_point(M_big, p_big, true; atol=1.0e-14) + @test is_point(M_big, p_big; error=:error, atol=1.0e-14) X_big = rand(M_big; vector_at=p_big, hamiltonian_norm=1.0) - @test is_vector(M_big, p_big, X_big, true; atol=1.0e-14) + @test is_vector(M_big, p_big, X_big; error=:error, atol=1.0e-14) end @testset "test_manifold(Symplectic(6), ...)" begin types = [Matrix{Float64}] From 3910dcd097485374060deb6947f35a051c6de2fb Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 17 Oct 2023 11:45:55 +0200 Subject: [PATCH 50/81] remove deprecations, update NEWS --- NEWS.md | 5 ++++- src/deprecated.jl | 3 +-- test/test_deprecated.jl | 5 +---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3e4e6bc401..eedd76e525 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Vector bundles are generalized to fiber bundles. +- Vector bundles are generalized to fiber bundles. Old `BundleFibers` functionality was reworked to better match mathematical abstractions. Fiber bundle functionality is experimental and minor changes may happen without a breaking release, with the exception of `TangentBundle` which is considered to be stable. - `RotationTranslationAction` is introduced. ### Changed @@ -97,8 +97,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ``` - Argument order for type aliases `RotationActionOnVector` and `RotationTranslationActionOnVector`: most often dispatched on argument is now first. +- A more consistent handling of action direction was introduced. 4-valued `ActionDirection` was split into 2-valued `ActionDirection` (either left or right action) and `GroupActionSide` (action acting from the left or right side). See [https://github.com/JuliaManifolds/Manifolds.jl/issues/637](https://github.com/JuliaManifolds/Manifolds.jl/issues/637) for a design discussion. ### Removed - `ProductRepr` is removed; please use `ArrayPartition` instead. - Default methods throwing "not implemented" `ErrorException` for some group-related operations. Standard `MethodError` is now thrown instead. +- `LinearAffineMetric` was deprecated in a previous release and the symbol is now removed. + Please use `AffineInvariantMetric` instead. diff --git a/src/deprecated.jl b/src/deprecated.jl index 306e1334c0..8b13789179 100644 --- a/src/deprecated.jl +++ b/src/deprecated.jl @@ -1,2 +1 @@ -Base.@deprecate_binding LinearAffineMetric AffineInvariantMetric -export LinearAffineMetric + diff --git a/test/test_deprecated.jl b/test/test_deprecated.jl index 1947014240..5791392758 100644 --- a/test/test_deprecated.jl +++ b/test/test_deprecated.jl @@ -1,6 +1,3 @@ using Manifolds, ManifoldsBase, Test -@testset "Deprecation tests" begin - # Let's just test that for now it still works - @test LinearAffineMetric() === AffineInvariantMetric() -end +@testset "Deprecation tests" begin end From e3fd23a0dfc8456a5a2f22c75eb80f957db7433d Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 17 Oct 2023 15:01:30 +0200 Subject: [PATCH 51/81] Fix errors that changed in type. --- test/manifolds/centered_matrices.jl | 2 +- test/manifolds/fixed_rank.jl | 2 +- test/manifolds/probability_simplex.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/manifolds/centered_matrices.jl b/test/manifolds/centered_matrices.jl index 3113febd70..ae63d1f5f9 100644 --- a/test/manifolds/centered_matrices.jl +++ b/test/manifolds/centered_matrices.jl @@ -19,7 +19,7 @@ include("../utils.jl") @test_throws DomainError is_point(M, D; error=:error) @test check_vector(M, A, A) === nothing @test_throws DomainError is_vector(M, A, D; error=:error) - @test_throws ManifoldDomainError is_vector(M, D, A; error=:error) + @test_throws DomainError is_vector(M, D, A; error=:error) @test_throws ManifoldDomainError is_vector(M, A, B; error=:error) @test manifold_dimension(M) == 4 @test A == project!(M, A, A) diff --git a/test/manifolds/fixed_rank.jl b/test/manifolds/fixed_rank.jl index 71c083bd5e..f779c06444 100644 --- a/test/manifolds/fixed_rank.jl +++ b/test/manifolds/fixed_rank.jl @@ -68,7 +68,7 @@ include("../utils.jl") UMVTVector(zeros(2, 1), zeros(1, 2), zeros(2, 2)), ) @test !is_vector(M, SVDMPoint([1.0 0.0; 0.0 0.0], 2), X) - @test_throws ManifoldDomainError is_vector( + @test_throws DomainError is_vector( M, SVDMPoint([1.0 0.0; 0.0 0.0], 2), X; diff --git a/test/manifolds/probability_simplex.jl b/test/manifolds/probability_simplex.jl index f0d99d2e1e..d2b6a5445d 100644 --- a/test/manifolds/probability_simplex.jl +++ b/test/manifolds/probability_simplex.jl @@ -18,7 +18,7 @@ include("../utils.jl") @test !is_flat(M) @test is_vector(M, p, X) @test is_vector(M, p, Y) - @test_throws ManifoldDomainError is_vector(M, p .+ 1, X; error=:error) + @test_throws DomainError is_vector(M, p .+ 1, X; error=:error) @test_throws ManifoldDomainError is_vector(M, p, zeros(4); error=:error) @test_throws DomainError is_vector(M, p, Y .+ 1; error=:error) From d39ca6e45cb1f3985a73cd0347bf784d4a9c5153 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 17 Oct 2023 15:19:58 +0200 Subject: [PATCH 52/81] Adapt a few more error tests. --- test/manifolds/essential_manifold.jl | 4 ++-- test/manifolds/grassmann.jl | 4 ++-- test/manifolds/hyperbolic.jl | 2 +- test/manifolds/multinomial_matrices.jl | 4 ++-- test/manifolds/oblique.jl | 4 ++-- test/manifolds/product_manifold.jl | 9 ++++++++- test/manifolds/skewhermitian.jl | 6 +++--- test/manifolds/sphere_symmetric_matrices.jl | 2 +- test/manifolds/stiefel.jl | 4 ++-- test/manifolds/symmetric.jl | 6 +++--- 10 files changed, 26 insertions(+), 19 deletions(-) diff --git a/test/manifolds/essential_manifold.jl b/test/manifolds/essential_manifold.jl index 26c3e34db4..a05e3edd98 100644 --- a/test/manifolds/essential_manifold.jl +++ b/test/manifolds/essential_manifold.jl @@ -31,8 +31,8 @@ include("../utils.jl") @test_throws DomainError is_vector( M, p1, - [0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0], - true, + [0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]; + error=:error, ) @test !is_vector(M, np1, [0.0 0.0 0.0; 0.0 0.0 0.0; 0.0 0.0 0.0]) @test !is_vector(M, p1, p2) diff --git a/test/manifolds/grassmann.jl b/test/manifolds/grassmann.jl index 4750b35a32..500ad62f5a 100644 --- a/test/manifolds/grassmann.jl +++ b/test/manifolds/grassmann.jl @@ -177,8 +177,8 @@ include("../utils.jl") @test_throws ManifoldDomainError is_vector( M, [1.0 0.0; 0.0 1.0; 0.0 0.0], - ones(3, 2), - true, + ones(3, 2); + error=:error, ) @test is_vector( M, diff --git a/test/manifolds/hyperbolic.jl b/test/manifolds/hyperbolic.jl index 26633060bd..887627371c 100644 --- a/test/manifolds/hyperbolic.jl +++ b/test/manifolds/hyperbolic.jl @@ -19,7 +19,7 @@ include("../utils.jl") @test_throws DomainError is_point(M, [2.0, 0.0, 0.0]; error=:error) @test !is_point(M, [2.0, 0.0, 0.0]) @test !is_vector(M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]) - @test_throws ManifoldDomainError is_vector( + @test_throws DomainError is_vector( M, [1.0, 0.0, 0.0], [1.0, 0.0, 0.0]; diff --git a/test/manifolds/multinomial_matrices.jl b/test/manifolds/multinomial_matrices.jl index 68f96f586b..c04d07410f 100644 --- a/test/manifolds/multinomial_matrices.jl +++ b/test/manifolds/multinomial_matrices.jl @@ -25,8 +25,8 @@ include("../utils.jl") @test_throws CompositeManifoldError{ComponentManifoldError{Int64,DomainError}} is_vector( M, p2, - [-1.0, 0.0, 0.0], - true, + [-1.0, 0.0, 0.0]; + error=:error, ) @test !is_vector(M, p2, [-1.0, 0.0, 0.0]) @test_throws DomainError is_vector(M, p, [-1.0, 0.0, 0.0]; error=:error) diff --git a/test/manifolds/oblique.jl b/test/manifolds/oblique.jl index 2463653660..4f51907aea 100644 --- a/test/manifolds/oblique.jl +++ b/test/manifolds/oblique.jl @@ -22,8 +22,8 @@ include("../utils.jl") @test_throws CompositeManifoldError{ComponentManifoldError{Int64,DomainError}} is_vector( M, p2, - [0.0, 0.0, 0.0], - true, + [0.0, 0.0, 0.0]; + error=:error, ) @test !is_vector(M, p2, [0.0, 0.0, 0.0]) @test_throws DomainError is_vector(M, p, [0.0, 0.0, 0.0]; error=:error) # p wrong diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index d9d74cd1e6..0f4bbbcc3d 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -37,7 +37,14 @@ using RecursiveArrayTools: ArrayPartition # test that arrays are not points @test_throws DomainError is_point(Mse, [1, 2]; error=:error) @test check_point(Mse, [1, 2]) isa DomainError - @test_throws DomainError is_vector(Mse, 1, [1, 2], true; check_base_point=false) + @test_throws DomainError is_vector( + Mse, + 1, + [1, 2]; + error=:error, + true; + check_base_point=false, + ) @test check_vector(Mse, 1, [1, 2]; check_base_point=false) isa DomainError #default fallbacks for check_size, Product not working with Arrays @test Manifolds.check_size(Mse, zeros(2)) isa DomainError diff --git a/test/manifolds/skewhermitian.jl b/test/manifolds/skewhermitian.jl index 79686d1a0d..7fd5a7600e 100644 --- a/test/manifolds/skewhermitian.jl +++ b/test/manifolds/skewhermitian.jl @@ -29,13 +29,13 @@ end @test_throws DomainError is_point(M, D; error=:error) @test check_vector(M, B_skewsym, B_skewsym) === nothing @test_throws DomainError is_vector(M, B_skewsym, A; error=:error) - @test_throws ManifoldDomainError is_vector(M, A, B_skewsym; error=:error) + @test_throws DomainError is_vector(M, A, B_skewsym; error=:error) @test_throws DomainError is_vector(M, B_skewsym, D; error=:error) @test_throws ManifoldDomainError is_vector( M, B_skewsym, - 1 * im * zero_vector(M, B_skewsym), - true, + 1 * im * zero_vector(M, B_skewsym); + error=:error, ) @test manifold_dimension(M) == 3 @test manifold_dimension(M_complex) == 9 diff --git a/test/manifolds/sphere_symmetric_matrices.jl b/test/manifolds/sphere_symmetric_matrices.jl index d6756bfb19..255a35e0bc 100644 --- a/test/manifolds/sphere_symmetric_matrices.jl +++ b/test/manifolds/sphere_symmetric_matrices.jl @@ -25,7 +25,7 @@ include("../utils.jl") @test_throws ManifoldDomainError is_vector(M, A, B; error=:error) @test_throws ManifoldDomainError is_vector(M, A, C; error=:error) @test_throws ManifoldDomainError is_vector(M, A, D; error=:error) - @test_throws ManifoldDomainError is_vector(M, D, A; error=:error) + @test_throws DomainError is_vector(M, D, A; error=:error) @test_throws ManifoldDomainError is_vector(M, A, E; error=:error) @test_throws DomainError is_vector(M, J, K; error=:error) @test manifold_dimension(M) == 5 diff --git a/test/manifolds/stiefel.jl b/test/manifolds/stiefel.jl index d1a65bcea5..6154ce2a28 100644 --- a/test/manifolds/stiefel.jl +++ b/test/manifolds/stiefel.jl @@ -128,7 +128,7 @@ include("../utils.jl") @test !is_point(M, 2 * x) @test_throws DomainError !is_point(M, 2 * x; error=:error) @test !is_vector(M, 2 * x, v) - @test_throws ManifoldDomainError !is_vector(M, 2 * x, v; error=:error) + @test_throws DomainError !is_vector(M, 2 * x, v; error=:error) @test !is_vector(M, x, y) @test_throws DomainError is_vector(M, x, y; error=:error) test_manifold( @@ -227,7 +227,7 @@ include("../utils.jl") @test !is_point(M, 2 * x) @test_throws DomainError !is_point(M, 2 * x; error=:error) @test !is_vector(M, 2 * x, v) - @test_throws ManifoldDomainError !is_vector(M, 2 * x, v; error=:error) + @test_throws DomainError !is_vector(M, 2 * x, v; error=:error) @test !is_vector(M, x, y) @test_throws DomainError is_vector(M, x, y; error=:error) test_manifold( diff --git a/test/manifolds/symmetric.jl b/test/manifolds/symmetric.jl index efb47a2d13..5e0ce75a1e 100644 --- a/test/manifolds/symmetric.jl +++ b/test/manifolds/symmetric.jl @@ -24,13 +24,13 @@ include("../utils.jl") @test_throws ManifoldDomainError is_point(M, D; error=:error) #embedding changes type @test check_vector(M, B_sym, B_sym) === nothing @test_throws DomainError is_vector(M, B_sym, A; error=:error) - @test_throws ManifoldDomainError is_vector(M, A, B_sym; error=:error) + @test_throws DomainError is_vector(M, A, B_sym; error=:error) @test_throws ManifoldDomainError is_vector(M, B_sym, D; error=:error) @test_throws ManifoldDomainError is_vector( M, B_sym, - 1 * im * zero_vector(M, B_sym), - true, + 1 * im * zero_vector(M, B_sym); + error=:error, ) @test manifold_dimension(M) == 6 @test manifold_dimension(M_complex) == 9 From b6402641dde9bf4280b818ac2f10b81febb7c030 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 17 Oct 2023 15:27:20 +0200 Subject: [PATCH 53/81] Fix a. few more errors. --- test/groups/general_linear.jl | 4 ++-- test/groups/general_unitary_groups.jl | 6 +++--- test/manifolds/symmetric.jl | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/groups/general_linear.jl b/test/groups/general_linear.jl index 59eaa631ad..b3e76768fb 100644 --- a/test/groups/general_linear.jl +++ b/test/groups/general_linear.jl @@ -71,7 +71,7 @@ using NLsolve @test_throws DomainError is_point(G, Float64[0 0 0; 0 1 1; 1 1 1]; error=:error) @test is_point(G, Float64[0 0 1; 0 1 1; 1 1 1]; error=:error) @test is_point(G, Identity(G); error=:error) - @test_throws ManifoldDomainError is_vector( + @test_throws DomainError is_vector( G, Float64[0 1 1; 0 1 1; 1 0 0], randn(3, 3); @@ -151,7 +151,7 @@ using NLsolve Float64[0 0 0; 0 1 1; 1 1 1]; error=:error, ) - @test_throws ManifoldDomainError is_vector( + @test_throws DomainError is_vector( G, ComplexF64[im im; im im], randn(ComplexF64, 2, 2); diff --git a/test/groups/general_unitary_groups.jl b/test/groups/general_unitary_groups.jl index 1a7c69bdb7..1c23d4a7db 100644 --- a/test/groups/general_unitary_groups.jl +++ b/test/groups/general_unitary_groups.jl @@ -160,14 +160,14 @@ include("group_utils.jl") Xe = ones(2, 2) X = project(SU2, q, Xe) @test is_vector(SU2, q, X) - @test_throws ManifoldDomainError is_vector(SU2, p, X, true; error=:error) # base point wrong + @test_throws DomainError is_vector(SU2, p, X, true; error=:error) # base point wrong @test_throws DomainError is_vector(SU2, q, Xe, true; error=:error) # Xe not skew hermitian @test_throws DomainError is_vector( SU2, Identity(AdditionOperation()), Xe, - true, - true, + true; + error = :error, ) # base point wrong e = Identity(MultiplicationOperation()) @test_throws DomainError is_vector(SU2, e, Xe, true; error=:error) # Xe not skew hermitian diff --git a/test/manifolds/symmetric.jl b/test/manifolds/symmetric.jl index 5e0ce75a1e..3132b77a07 100644 --- a/test/manifolds/symmetric.jl +++ b/test/manifolds/symmetric.jl @@ -26,7 +26,7 @@ include("../utils.jl") @test_throws DomainError is_vector(M, B_sym, A; error=:error) @test_throws DomainError is_vector(M, A, B_sym; error=:error) @test_throws ManifoldDomainError is_vector(M, B_sym, D; error=:error) - @test_throws ManifoldDomainError is_vector( + @test_throws DomainError is_vector( M, B_sym, 1 * im * zero_vector(M, B_sym); From eb3f9202dbca27474b4cf4833a746a1e78182309 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 17 Oct 2023 15:30:15 +0200 Subject: [PATCH 54/81] Update test/groups/general_unitary_groups.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- test/groups/general_unitary_groups.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/groups/general_unitary_groups.jl b/test/groups/general_unitary_groups.jl index 1c23d4a7db..a3829e2990 100644 --- a/test/groups/general_unitary_groups.jl +++ b/test/groups/general_unitary_groups.jl @@ -167,7 +167,7 @@ include("group_utils.jl") Identity(AdditionOperation()), Xe, true; - error = :error, + error=:error, ) # base point wrong e = Identity(MultiplicationOperation()) @test_throws DomainError is_vector(SU2, e, Xe, true; error=:error) # Xe not skew hermitian From 26296466afc74c7aff29a1accf7ec0eefa94ce5a Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 17 Oct 2023 18:32:18 +0200 Subject: [PATCH 55/81] some fixes --- src/groups/group.jl | 32 +++++++++++++++++++++--------- src/manifolds/MetricManifold.jl | 10 ++++------ test/groups/special_linear.jl | 4 ++-- test/groups/special_orthogonal.jl | 2 ++ test/manifolds/product_manifold.jl | 9 +-------- test/manifolds/symmetric.jl | 2 +- test/manifolds/symplectic.jl | 4 ++-- 7 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/groups/group.jl b/src/groups/group.jl index ed54b34796..198279ad2b 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -384,12 +384,18 @@ end function is_point( ::TraitList{<:IsGroupManifold}, G::AbstractDecoratorManifold, - e::Identity, - te::Bool=false; + e::Identity; + error::Symbol=:none, kwargs..., ) ie = is_identity(G, e; kwargs...) - (te && !ie) && throw(DomainError(e, "The provided identity is not a point on $G.")) + if !ie + s = "The provided identity is not a point on $G." + (error === :error) && throw(DomainError(e, s)) + (error === :info) && @info s + (error === :warn) && @warn s + end + return ie end @@ -398,16 +404,24 @@ function is_vector( G::AbstractDecoratorManifold, e::Identity, X, - te::Bool=false, - cbp=true; + cbp::Bool=true; + error::Symbol=:none, kwargs..., ) if cbp - # pass te down so this throws an error if te=true - # if !te and is_point was false -> return false, otherwise continue - (!te && !is_point(G, e, te; kwargs...)) && return false + # pass te down so this throws an error if error=:error + # if error is not `:error` and is_point was false -> return false, otherwise continue + (!is_point(G, e; error=error, kwargs...)) && return false end - return is_vector(next_trait(t), G, identity_element(G), X, te, false; kwargs...) + return is_vector( + next_trait(t), + G, + identity_element(G), + X, + false; + error=error, + kwargs..., + ) end @doc raw""" diff --git a/src/manifolds/MetricManifold.jl b/src/manifolds/MetricManifold.jl index e83539a050..87ba053734 100644 --- a/src/manifolds/MetricManifold.jl +++ b/src/manifolds/MetricManifold.jl @@ -426,11 +426,10 @@ is_default_metric(::AbstractManifold, ::AbstractMetric) = false function is_point( ::TraitList{IsMetricManifold}, M::MetricManifold{𝔽,TM,G}, - p, - te::Bool=false; + p; kwargs..., ) where {𝔽,G<:AbstractMetric,TM<:AbstractManifold} - return is_point(M.manifold, p, te; kwargs...) + return is_point(M.manifold, p; kwargs...) end function is_vector( @@ -438,11 +437,10 @@ function is_vector( M::MetricManifold{𝔽,TM,G}, p, X, - te::Bool=false, - cbp=true; + cbp::Bool=true; kwargs..., ) where {𝔽,G<:AbstractMetric,TM<:AbstractManifold} - return is_vector(M.manifold, p, X, te, cbp; kwargs...) + return is_vector(M.manifold, p, X, cbp; kwargs...) end @doc raw""" diff --git a/test/groups/special_linear.jl b/test/groups/special_linear.jl index 887dfa0569..107ee1f9cf 100644 --- a/test/groups/special_linear.jl +++ b/test/groups/special_linear.jl @@ -37,7 +37,7 @@ using NLsolve @test_throws DomainError is_point(G, Float64[1 3 3; 1 1 2; 1 2 3]; error=:error) @test is_point(G, Float64[1 1 1; 2 2 1; 2 3 3]; error=:error) @test is_point(G, Identity(G); error=:error) - @test_throws ManifoldDomainError is_vector( + @test_throws DomainError is_vector( G, Float64[2 3 2; 3 1 2; 1 1 1], randn(3, 3); @@ -136,7 +136,7 @@ using NLsolve @test_throws DomainError is_point(G, ComplexF64[1 im; im 1]; error=:error) @test is_point(G, ComplexF64[im 1; -2 im]; error=:error) @test is_point(G, Identity(G); error=:error) - @test_throws ManifoldDomainError is_vector( + @test_throws DomainError is_vector( G, ComplexF64[-1+im -1; -im 1], ComplexF64[1-im 1+im; 1 -1+im]; diff --git a/test/groups/special_orthogonal.jl b/test/groups/special_orthogonal.jl index 9f3f0faa06..1359cbf9bd 100644 --- a/test/groups/special_orthogonal.jl +++ b/test/groups/special_orthogonal.jl @@ -1,6 +1,8 @@ include("../utils.jl") include("group_utils.jl") +using Manifolds: LeftForwardAction, RightBackwardAction + @testset "Special Orthogonal group" begin for n in [2, 3] G = SpecialOrthogonal(n) diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index 0f4bbbcc3d..c1bf2d6486 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -37,14 +37,7 @@ using RecursiveArrayTools: ArrayPartition # test that arrays are not points @test_throws DomainError is_point(Mse, [1, 2]; error=:error) @test check_point(Mse, [1, 2]) isa DomainError - @test_throws DomainError is_vector( - Mse, - 1, - [1, 2]; - error=:error, - true; - check_base_point=false, - ) + @test_throws DomainError is_vector(Mse, 1, [1, 2]; error=:error, check_base_point=false) @test check_vector(Mse, 1, [1, 2]; check_base_point=false) isa DomainError #default fallbacks for check_size, Product not working with Arrays @test Manifolds.check_size(Mse, zeros(2)) isa DomainError diff --git a/test/manifolds/symmetric.jl b/test/manifolds/symmetric.jl index 3132b77a07..5e0ce75a1e 100644 --- a/test/manifolds/symmetric.jl +++ b/test/manifolds/symmetric.jl @@ -26,7 +26,7 @@ include("../utils.jl") @test_throws DomainError is_vector(M, B_sym, A; error=:error) @test_throws DomainError is_vector(M, A, B_sym; error=:error) @test_throws ManifoldDomainError is_vector(M, B_sym, D; error=:error) - @test_throws DomainError is_vector( + @test_throws ManifoldDomainError is_vector( M, B_sym, 1 * im * zero_vector(M, B_sym); diff --git a/test/manifolds/symplectic.jl b/test/manifolds/symplectic.jl index 3ef22cd8be..e618d11968 100644 --- a/test/manifolds/symplectic.jl +++ b/test/manifolds/symplectic.jl @@ -190,9 +190,9 @@ using ManifoldDiff @testset "Generate random points/tangent vectors" begin M_big = Symplectic(20) p_big = rand(M_big) - @test is_point(M_big, p_big, true; atol=1.0e-12) + @test is_point(M_big, p_big; error=:error, atol=1.0e-12) X_big = rand(M_big; vector_at=p_big) - @test is_vector(M_big, p_big, X_big, true; atol=1.0e-12) + @test is_vector(M_big, p_big, X_big; error=:error, atol=1.0e-12) end @testset "test_manifold(Symplectic(6), ...)" begin @testset "Type $(Matrix{Float64})" begin From 09cdd06fdbb23dab9f3b1d661487a1060760f052 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 17 Oct 2023 18:33:22 +0200 Subject: [PATCH 56/81] More fixes. --- src/groups/group.jl | 11 ++++------- test/manifolds/symmetric.jl | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/groups/group.jl b/src/groups/group.jl index ed54b34796..825eae4267 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -398,16 +398,13 @@ function is_vector( G::AbstractDecoratorManifold, e::Identity, X, - te::Bool=false, cbp=true; kwargs..., ) - if cbp - # pass te down so this throws an error if te=true - # if !te and is_point was false -> return false, otherwise continue - (!te && !is_point(G, e, te; kwargs...)) && return false - end - return is_vector(next_trait(t), G, identity_element(G), X, te, false; kwargs...) + # Check POint on group + cbp && (!is_point(G, e; kwargs...)) && (return false) + # set check to false on next trait to not check again + return is_vector(next_trait(t), G, identity_element(G), X, false; kwargs...) end @doc raw""" diff --git a/test/manifolds/symmetric.jl b/test/manifolds/symmetric.jl index 3132b77a07..5e0ce75a1e 100644 --- a/test/manifolds/symmetric.jl +++ b/test/manifolds/symmetric.jl @@ -26,7 +26,7 @@ include("../utils.jl") @test_throws DomainError is_vector(M, B_sym, A; error=:error) @test_throws DomainError is_vector(M, A, B_sym; error=:error) @test_throws ManifoldDomainError is_vector(M, B_sym, D; error=:error) - @test_throws DomainError is_vector( + @test_throws ManifoldDomainError is_vector( M, B_sym, 1 * im * zero_vector(M, B_sym); From 7317d206f637cbb6b96262d9b60a6155e2a2b6ee Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Tue, 17 Oct 2023 19:14:07 +0200 Subject: [PATCH 57/81] Fix the remaining failing tests for group --- src/groups/GroupManifold.jl | 26 ++++++++++++++++++-------- src/groups/group.jl | 7 +++---- test/groups/circle_group.jl | 2 +- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/groups/GroupManifold.jl b/src/groups/GroupManifold.jl index 86fa9fb2d3..c8983731be 100644 --- a/src/groups/GroupManifold.jl +++ b/src/groups/GroupManifold.jl @@ -69,12 +69,17 @@ end function is_point( ::TraitList{<:IsGroupManifold}, G::GroupManifold, - e::Identity, - te::Bool=false; + e::Identity; + error::Symbol=:none, kwargs..., ) ie = is_identity(G, e; kwargs...) - (te && !ie) && throw(DomainError(e, "The provided identity is not a point on $G.")) + if !ie + s = "The provided identity is not a point on $G." + (error === :error) && throw(DomainError(e, s)) + (error === :info) && @info s + (error === :warn) && @warn s + end return ie end @@ -83,16 +88,21 @@ function is_vector( G::GroupManifold, e::Identity, X, - te::Bool=false, - cbp=true; + cbp::Bool; + error::Symbol=:none, kwargs..., ) if cbp ie = is_identity(G, e; kwargs...) - (te && !ie) && throw(DomainError(e, "The provided identity is not a point on $G.")) - (!te && !ie) && return false + if !ie + s = "The provided identity is not a point on $G." + (error === :error) && throw(DomainError(e, s)) + (error === :info) && @info s + (error === :warn) && @warn s + return false + end end - return is_vector(G.manifold, identity_element(G), X, te, false; kwargs...) + return is_vector(G.manifold, identity_element(G), X, false, te; kwargs...) end Base.show(io::IO, G::GroupManifold) = print(io, "GroupManifold($(G.manifold), $(G.op))") diff --git a/src/groups/group.jl b/src/groups/group.jl index 198279ad2b..b56ee94cd0 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -391,11 +391,10 @@ function is_point( ie = is_identity(G, e; kwargs...) if !ie s = "The provided identity is not a point on $G." - (error === :error) && throw(DomainError(e, s)) - (error === :info) && @info s - (error === :warn) && @warn s + (error == :error) && throw(DomainError(e, s)) + (error == :info) && @info s + (error == :warn) && @warn s end - return ie end diff --git a/test/groups/circle_group.jl b/test/groups/circle_group.jl index f14ceeac1c..c14f1c5d8b 100644 --- a/test/groups/circle_group.jl +++ b/test/groups/circle_group.jl @@ -28,7 +28,7 @@ using Manifolds: @test !is_point(G, Identity(AdditionOperation())) ef = Identity(AdditionOperation()) @test_throws DomainError is_point(G, ef; error=:error) - @test_throws DomainError is_vector(G, ef, X, true; check_base_point=true) + @test_throws DomainError is_vector(G, ef, X, true; error=:error) end @testset "scalar points" begin From 37324993f2b5af0814929c1203a898a36a3b7ee5 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Tue, 17 Oct 2023 19:43:24 +0200 Subject: [PATCH 58/81] === --- src/groups/group.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/groups/group.jl b/src/groups/group.jl index b56ee94cd0..977a26a4de 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -391,9 +391,9 @@ function is_point( ie = is_identity(G, e; kwargs...) if !ie s = "The provided identity is not a point on $G." - (error == :error) && throw(DomainError(e, s)) - (error == :info) && @info s - (error == :warn) && @warn s + (error === :error) && throw(DomainError(e, s)) + (error === :info) && @info s + (error === :warn) && @warn s end return ie end From cb78c99693a1ada2ee8b85ada98bbc88f1b2d66b Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 18 Oct 2023 12:37:29 +0200 Subject: [PATCH 59/81] formatting --- src/groups/special_euclidean.jl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/groups/special_euclidean.jl b/src/groups/special_euclidean.jl index e7cab2b454..337ffa424e 100644 --- a/src/groups/special_euclidean.jl +++ b/src/groups/special_euclidean.jl @@ -763,8 +763,11 @@ function get_coordinates( get_coordinates(M2.manifold, p.x[2], X.x[2], basis), ) end -function hat(M::SpecialEuclidean{TypeParameter{Tuple{2}}}, p::ArrayPartition, c::AbstractVector) - +function hat( + M::SpecialEuclidean{TypeParameter{Tuple{2}}}, + p::ArrayPartition, + c::AbstractVector, +) M1, M2 = M.manifold.manifolds return ArrayPartition( get_vector_orthogonal(M1.manifold, p.x[1], c[SOneTo(2)], ℝ), @@ -783,7 +786,11 @@ function get_vector( ) end -function hat(M::SpecialEuclidean{TypeParameter{Tuple{3}}}, p::ArrayPartition, c::AbstractVector) +function hat( + M::SpecialEuclidean{TypeParameter{Tuple{3}}}, + p::ArrayPartition, + c::AbstractVector, +) M1, M2 = M.manifold.manifolds return ArrayPartition( get_vector_orthogonal(M1.manifold, p.x[1], c[SOneTo(3)], ℝ), From 0f523130ad4427edc1e28e13203089322df78588 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Wed, 18 Oct 2023 12:56:02 +0200 Subject: [PATCH 60/81] update some docs --- src/groups/group.jl | 68 +++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/groups/group.jl b/src/groups/group.jl index 977a26a4de..18d3536510 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -1,7 +1,7 @@ @doc raw""" AbstractGroupOperation -Abstract type for smooth binary operations $∘$ on elements of a Lie group $\mathcal{G}$: +Abstract type for smooth binary operations ``∘`` on elements of a Lie group ``\mathcal{G}``: ```math ∘ : \mathcal{G} × \mathcal{G} → \mathcal{G} ``` @@ -436,7 +436,7 @@ The formula reads ````math \operatorname{Ad}_p(X) = dΨ_p(e)[X] ```` -where $e$ is the identity element of `G`. +where ``e`` is the identity element of `G`. Note that the adjoint representation of a Lie group isn't generally faithful. Notably the adjoint representation of SO(2) is trivial. @@ -488,9 +488,9 @@ end @doc raw""" inv(G::AbstractDecoratorManifold, 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) -element of $\mathcal{G}$. +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) +element of ``\mathcal{G}``. """ inv(::AbstractDecoratorManifold, ::Any...) @trait_function Base.inv(G::AbstractDecoratorManifold, p) @@ -633,7 +633,7 @@ Base.transpose(e::Identity) = e @doc raw""" hat(M::AbstractDecoratorManifold{𝔽,O}, ::Identity{O}, Xⁱ) where {𝔽,O<:AbstractGroupOperation} -Given a basis $e_i$ on the tangent space at a the [`Identity`](@ref) and tangent +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: @@ -675,8 +675,8 @@ end @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 +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 @@ -734,9 +734,9 @@ _action_order(BG::AbstractDecoratorManifold, p, q, ::RightBackwardAction) = (q, @doc raw""" translate(G::AbstractDecoratorManifold, p, q, conv::ActionDirectionAndSide=LeftForwardAction()]) -Translate group element $q$ by $p$ with the translation $τ_p$ with the specified -`conv`ention, either left forward ($L_p$), left backward ($R'_p$), right backward ($R_p$) -or right forward ($L'_p$), defined as +Translate group element ``q`` by ``p`` with the translation ``τ_p`` with the specified +`conv`ention, either left forward (``L_p``), left backward (``R'_p``), right backward (``R_p``) +or right forward (``L'_p``), defined as ```math \begin{aligned} L_p &: q ↦ p \circ q\\ @@ -786,14 +786,16 @@ end @doc raw""" inverse_translate(G::AbstractDecoratorManifold, p, q, conv::ActionDirectionAndSide=LeftForwardAction()) -Inverse translate group element $q$ by $p$ with the inverse translation $τ_p^{-1}$ with the -specified `conv`ention, either left ($L_p^{-1}$) or right ($R_p^{-1}$), defined as +Inverse translate group element ``q`` by ``p`` with the translation ``τ_p^{-1}`` +with the specified `conv`ention, either left forward (``L_p^{-1}``), left backward +(``R'_p^{-1}``), right backward (``R_p^{-1}``) or right forward (``L'_p^{-1}``), defined as ```math \begin{aligned} L_p^{-1} &: q ↦ p^{-1} \circ q\\ -R_p^{-1} &: q ↦ q \circ p^{-1}. +L'_p^{-1} &: q ↦ p \circ q\\ +R_p^{-1} &: q ↦ q \circ p^{-1}\\ +R'_p^{-1} &: q ↦ q \circ p. \end{aligned} -``` """ inverse_translate(::AbstractDecoratorManifold, ::Any...) @trait_function inverse_translate( @@ -835,8 +837,8 @@ end @doc raw""" translate_diff(G::AbstractDecoratorManifold, p, q, X, conv::ActionDirectionAndSide=LeftForwardAction()) -For group elements $p, q ∈ \mathcal{G}$ and tangent vector $X ∈ T_q \mathcal{G}$, compute -the action of the differential of the translation $τ_p$ by $p$ on $X$, with the specified +For group elements ``p, q ∈ \mathcal{G}`` and tangent vector ``X ∈ T_q \mathcal{G}``, compute +the action of the differential of the translation ``τ_p`` by ``p`` on ``X``, with the specified left or right `conv`ention. The differential transports vectors: ```math (\mathrm{d}τ_p)_q : T_q \mathcal{G} → T_{τ_p q} \mathcal{G}\\ @@ -875,8 +877,8 @@ end @doc raw""" inverse_translate_diff(G::AbstractDecoratorManifold, p, q, X, conv::ActionDirectionAndSide=LeftForwardAction()) -For group elements $p, q ∈ \mathcal{G}$ and tangent vector $X ∈ T_q \mathcal{G}$, compute -the action on $X$ of the differential of the inverse translation $τ_p$ by $p$, with the +For group elements ``p, q ∈ \mathcal{G}`` and tangent vector ``X ∈ T_q \mathcal{G}``, compute +the action on ``X`` of the differential of the inverse translation ``τ_p`` by ``p``, with the specified left or right `conv`ention. The differential transports vectors: ```math (\mathrm{d}τ_p^{-1})_q : T_q \mathcal{G} → T_{τ_p^{-1} q} \mathcal{G}\\ @@ -930,13 +932,13 @@ end 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 -the group $\mathcal{G}$, and $𝔤$ is its Lie algebra, the group exponential is the map +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 \exp : 𝔤 → \mathcal{G}, ```` -such that for $t,s ∈ ℝ$, $γ(t) = \exp (t X)$ defines a one-parameter subgroup with the +such that for ``t,s ∈ ℝ``, ``γ(t) = \exp (t X)`` defines a one-parameter subgroup with the following properties. Note that one-parameter subgroups are commutative (see [Suhubi:2013](@cite), section 3.5), even if the Lie group itself is not commutative. @@ -980,9 +982,9 @@ end 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 -[`exp_lie`](@ref), that is, the element $\log q = X ∈ 𝔤 = T_e \mathcal{G}$, such that -$q = \exp X$ +Given an element ``q ∈ \mathcal{G}``, compute the right inverse of the group exponential map +[`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 @@ -994,8 +996,8 @@ $q = \exp X$ \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 -identity matrix $I_m$ for matrix $q ∈ ℝ^{m × m}$. +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 also depends on the group operation, make sure to implement either @@ -1089,15 +1091,15 @@ direction_and_side(::GroupLogarithmicInverseRetraction{D}) where {D} = D() 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 +With a group translation ([`translate`](@ref)) ``τ_p`` in a specified direction, the retraction is ````math \operatorname{retr}_p = τ_p \circ \exp \circ (\mathrm{d}τ_p^{-1})_p, ```` -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 +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( @@ -1159,15 +1161,15 @@ end 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 +With a group translation ([`translate`](@ref)) ``τ_p`` in a specified direction, the retraction is ````math \operatorname{retr}_p^{-1} = (\mathrm{d}τ_p)_e \circ \log \circ τ_p^{-1}, ```` -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$ +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( From 6d2c04e5b9a621e4840329d746cd42c7cbeff12e Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sat, 21 Oct 2023 19:38:29 +0200 Subject: [PATCH 61/81] better docs for group actions --- src/groups/group.jl | 19 ++++++++-------- src/groups/group_action.jl | 33 ++++++++++++++-------------- src/groups/group_operation_action.jl | 7 +++++- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/groups/group.jl b/src/groups/group.jl index 18d3536510..b9c6608b20 100644 --- a/src/groups/group.jl +++ b/src/groups/group.jl @@ -13,7 +13,8 @@ number system `𝔽` or in general, by defining for an operation `Op` the follow _compose!(::AbstractDecoratorManifold, x, p, q) Note that a manifold is connected with an operation by wrapping it with a decorator, -[`AbstractDecoratorManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/decorator.html#ManifoldsBase.AbstractDecoratorManifold) using the [`IsGroupManifold`](@ref) to specify the operation. +[`AbstractDecoratorManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/decorator.html#ManifoldsBase.AbstractDecoratorManifold) +using the [`IsGroupManifold`](@ref) to specify the operation. For a concrete case the concrete wrapper [`GroupManifold`](@ref) can be used. """ abstract type AbstractGroupOperation end @@ -30,7 +31,7 @@ see [`GroupManifold`](@ref). # Constructor - IsGroupManifold(op) + IsGroupManifold(op::AbstractGroupOperation) """ struct IsGroupManifold{O<:AbstractGroupOperation} <: AbstractTrait op::O @@ -78,10 +79,10 @@ end """ is_group_manifold(G::GroupManifold) - is_group_manifoldd(G::AbstractManifold, o::AbstractGroupOperation) + is_group_manifold(G::AbstractManifold, o::AbstractGroupOperation) -returns whether an [`AbstractDecoratorManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/decorator.html#ManifoldsBase.AbstractDecoratorManifold) is a group manifold with -[`AbstractGroupOperation`](@ref) `o`. +returns whether an [`AbstractDecoratorManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/decorator.html#ManifoldsBase.AbstractDecoratorManifold) +is a group manifold with [`AbstractGroupOperation`](@ref) `o`. For a [`GroupManifold`](@ref) `G` this checks whether the right operations is stored within `G`. """ is_group_manifold(::AbstractManifold, ::AbstractGroupOperation) = false @@ -128,13 +129,13 @@ struct LeftAction <: ActionDirection end """ RightAction() -Right action of a group on a manifold. For an action ``α: G × X → X`` it is characterized by +Right action of a group on a manifold. For a forward action ``α: G × X → X`` it is characterized by ```math α(g, α(h, x)) = α(hg, x) ``` for all ``g, h ∈ G`` and ``x ∈ X``. -Note that a right action may still act from the left side in an expression. +Note that a right action may act from either left or right side in an expression. """ struct RightAction <: ActionDirection end @@ -155,7 +156,7 @@ struct LeftSide <: GroupActionSide end """ RightSide() -An action of a group on a manifold that acts from the right side, i.e. ``α: X × G→ X``. +An action of a group on a manifold that acts from the right side, i.e. ``α: X × G → X``. """ struct RightSide <: GroupActionSide end @@ -226,7 +227,7 @@ Identity(::Type{O}) where {O<:AbstractGroupOperation} = Identity{O}() number_eltype(::Identity) = Bool @doc raw""" - identity_element(G) + identity_element(G::AbstractDecoratorManifold) Return a point representation of the [`Identity`](@ref) on the [`IsGroupManifold`](@ref) `G`. By default this representation is the default array or number representation. diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index 1d74946582..d0464e8fec 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -1,14 +1,15 @@ """ - AbstractGroupAction + AbstractGroupAction{AD<:ActionDirection} -An abstract group action on a manifold. +An abstract group action on a manifold. [`ActionDirection``](@ref)`AD` indicates whether it +is a left or right action. """ abstract type AbstractGroupAction{AD<:ActionDirection} end """ base_group(A::AbstractGroupAction) -The group that acts in action `A`. +The group that acts in [`AbstractGroupAction`](@ref) `A`. """ base_group(A::AbstractGroupAction) @@ -26,7 +27,7 @@ end """ direction(::AbstractGroupAction{AD}) -> AD -Get the direction of the action +Get the direction of the action: either [`LeftAction`](@ref) or [`RightAction`](@ref). """ direction(::AbstractGroupAction{AD}) where {AD} = AD() @@ -44,7 +45,7 @@ adjoint_apply_diff_group(A::AbstractGroupAction, a, X, p) @doc raw""" apply(A::AbstractGroupAction, a, p) -Apply action `a` to the point `p` using map $τ_a$, specified by `A`. +Apply action `a` to the point `p` using map ``τ_a``, specified by `A`. Unless otherwise specified, the right action is defined in terms of the left action: ````math @@ -96,9 +97,9 @@ end @doc raw""" apply_diff(A::AbstractGroupAction, a, p, X) -For point $p ∈ \mathcal M$ and tangent vector $X ∈ T_p \mathcal M$, compute the action -on $X$ of the differential of the action of $a ∈ \mathcal{G}$, specified by rule `A`. -Written as $(\mathrm{d}τ_a)_p$, with the specified left or right convention, the +For point ``p ∈ \mathcal M`` and tangent vector ``X ∈ T_p \mathcal M``, compute the action +on ``X`` of the differential of the action of ``a ∈ \mathcal{G}``, specified by rule `A`. +Written as ``(\mathrm{d}τ_a)_p``, with the specified left or right convention, the differential transports vectors ````math @@ -133,10 +134,10 @@ apply_diff_group(A::AbstractGroupAction, a, X, p) @doc raw""" inverse_apply_diff(A::AbstractGroupAction, a, p, X) -For group point $p ∈ \mathcal M$ and tangent vector $X ∈ T_p \mathcal M$, compute the action -on $X$ of the differential of the inverse action of $a ∈ \mathcal{G}$, specified by rule -`A`. Written as $(\mathrm{d}τ_a^{-1})_p$, with the specified left or right convention, -the differential transports vectors +For group point ``p ∈ \mathcal M`` and tangent vector ``X ∈ T_p \mathcal M``, compute the +action on ``X`` of the differential of the inverse action of ``a ∈ \mathcal{G}``, specified +by rule `A`. Written as ``(\mathrm{d}τ_a^{-1})_p``, with the specified left or right +convention, the differential transports vectors. ````math (\mathrm{d}τ_a^{-1})_p : T_p \mathcal M → T_{τ_a^{-1} p} \mathcal M @@ -167,12 +168,12 @@ end @doc raw""" optimal_alignment(A::AbstractGroupAction, p, q) -Calculate an action element $a$ of action `A` that acts upon `p` to produce +Calculate an action element ``a`` of action `A` that acts upon `p` to produce the element closest to `q` in the metric of the G-manifold: ```math \arg\min_{a ∈ \mathcal{G}} d_{\mathcal M}(τ_a p, q) ``` -where $\mathcal{G}$ is the group that acts on the G-manifold $\mathcal M$. +where ``\mathcal{G}`` is the group that acts on the G-manifold ``\mathcal M``. """ optimal_alignment(A::AbstractGroupAction, p, q) @@ -195,11 +196,11 @@ end mean_method::AbstractEstimationMethod = GradientDescentEstimation(), ) -Calculate an action element $a$ of action `A` that is the mean element of the orbit of `p` +Calculate an action element ``a`` of action `A` that is the mean element of the orbit of `p` with respect to given set of points `pts`. The [`mean`](@ref) is calculated using the method `mean_method`. -The orbit of $p$ with respect to the action of a group $\mathcal{G}$ is the set +The orbit of ``p`` with respect to the action of a group ``\mathcal{G}`` is the set ````math O = \{ τ_a p : a ∈ \mathcal{G} \}. ```` diff --git a/src/groups/group_operation_action.jl b/src/groups/group_operation_action.jl index 8a8fb19c68..87b3585581 100644 --- a/src/groups/group_operation_action.jl +++ b/src/groups/group_operation_action.jl @@ -1,7 +1,12 @@ @doc raw""" GroupOperationAction{AD<:ActionDirection,AS<:GroupActionSide,G<:AbstractDecoratorManifold} <: AbstractGroupAction{AD} -Action of a group upon itself via left or right translation. +Action of a group upon itself via left or right translation, either from left or right side. +An element `p` of the group can act upon another another element by either: +* left action from the left side: ``L_p: q ↦ p \circ q``, +* right action from the left side: ``L'_p: q ↦ p^{-1} \circ q``, +* right action from the right side: ``R_p: q ↦ q \circ p``, +* left action from the right side: ``R'_p &: q ↦ q \circ p^{-1}``. # Constructor From 96c3ed529269d447465b17f095c7b03d9263c745 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sat, 21 Oct 2023 22:29:56 +0200 Subject: [PATCH 62/81] bump versions --- Project.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index 2338851467..8c5a5e0c7f 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.81" +version = "0.9.0" [deps] Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" @@ -49,8 +49,8 @@ Einsum = "0.4" Graphs = "1.4" HybridArrays = "0.4" Kronecker = "0.4, 0.5" -ManifoldDiff = "0.3.6" -ManifoldsBase = "0.14.12" +ManifoldDiff = "0.3.7" +ManifoldsBase = "0.15.0" MatrixEquations = "2.2" OrdinaryDiffEq = "6.31" Plots = "1" From 5174442fb36eb184d9e5a57cce2d40cae938c16d Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sat, 21 Oct 2023 22:42:18 +0200 Subject: [PATCH 63/81] bump docs compat --- docs/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Project.toml b/docs/Project.toml index c051de4de2..2a2eac142a 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -30,7 +30,7 @@ FiniteDifferences = "0.12" Graphs = "1.4" HybridArrays = "0.4" IJulia = "1" -ManifoldsBase = "0.14.1" +ManifoldsBase = "0.15.0" OrdinaryDiffEq = "6" Plots = "1" PythonPlot = "1" From 7399d05ce31de8bb70e518ec26be78f1b95eaed7 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 15:13:49 +0200 Subject: [PATCH 64/81] randomly trying to fix docs? --- docs/make.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 8dfca558e6..847085ba98 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -21,11 +21,11 @@ if "--quarto" ∈ ARGS using CondaPkg CondaPkg.withenv() do @info "Rendering Quarto" - tutorials_folder = (@__DIR__) * "/../tutorials" - # instantiate the tutorials environment if necessary - Pkg.activate(tutorials_folder) - Pkg.resolve() - Pkg.instantiate() + # tutorials_folder = (@__DIR__) * "/../tutorials" + # # instantiate the tutorials environment if necessary + # Pkg.activate(tutorials_folder) + # Pkg.resolve() + # Pkg.instantiate() Pkg.activate(@__DIR__) # but return to the docs one before run(`quarto render $(tutorials_folder)`) return nothing From 7acb501b044c64d4fa75b2bf6e5696fde86ed7c8 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 15:14:11 +0200 Subject: [PATCH 65/81] f --- tutorials/Project.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tutorials/Project.toml b/tutorials/Project.toml index 769b502744..f96dc8ad08 100644 --- a/tutorials/Project.toml +++ b/tutorials/Project.toml @@ -16,11 +16,12 @@ SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [compat] +BoundaryValueDiffEq = "4" CSV = "0.10" DataFrames = "1" Distributions = "0.22.6, 0.23, 0.24, 0.25" IJulia = "1" -Manifolds = "0.8.71" -ManifoldsBase = "0.14.10" +Manifolds = "0.9.0" +ManifoldsBase = "0.15.0" MultivariateStats = "0.10" StaticArrays = "1" From 9da6dbff4b0cd9f8468b773cb1df05c1eafbfa33 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 15:41:04 +0200 Subject: [PATCH 66/81] try this --- docs/make.jl | 10 +++++----- tutorials/getstarted.qmd | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 847085ba98..8dfca558e6 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -21,11 +21,11 @@ if "--quarto" ∈ ARGS using CondaPkg CondaPkg.withenv() do @info "Rendering Quarto" - # tutorials_folder = (@__DIR__) * "/../tutorials" - # # instantiate the tutorials environment if necessary - # Pkg.activate(tutorials_folder) - # Pkg.resolve() - # Pkg.instantiate() + tutorials_folder = (@__DIR__) * "/../tutorials" + # instantiate the tutorials environment if necessary + Pkg.activate(tutorials_folder) + Pkg.resolve() + Pkg.instantiate() Pkg.activate(@__DIR__) # but return to the docs one before run(`quarto render $(tutorials_folder)`) return nothing diff --git a/tutorials/getstarted.qmd b/tutorials/getstarted.qmd index dfb2f1b8e2..9f47a8e470 100644 --- a/tutorials/getstarted.qmd +++ b/tutorials/getstarted.qmd @@ -9,6 +9,7 @@ title: 🚀 Get Started with `Manifolds.jl` using Pkg; cd(@__DIR__) Pkg.activate("."); # for reproducibility use the local tutorial environment. +Pkg.develop(path="../") # a trick to work on the local dev version using Markdown ``` From 7660641dc4c57d5a6878b1bd561ed6d8b4930df9 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 16:01:51 +0200 Subject: [PATCH 67/81] try this, try that... --- tutorials/Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/Project.toml b/tutorials/Project.toml index f96dc8ad08..20f5eaf955 100644 --- a/tutorials/Project.toml +++ b/tutorials/Project.toml @@ -21,7 +21,7 @@ CSV = "0.10" DataFrames = "1" Distributions = "0.22.6, 0.23, 0.24, 0.25" IJulia = "1" -Manifolds = "0.9.0" -ManifoldsBase = "0.15.0" +Manifolds = "0.8.81, 0.9.0" +ManifoldsBase = "0.14, 0.15.0" MultivariateStats = "0.10" StaticArrays = "1" From f6162eb960cdde1046aa321dcca2619741dedf25 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 16:32:42 +0200 Subject: [PATCH 68/81] fix docs --- docs/src/features/utilities.md | 3 +-- docs/src/manifolds/power.md | 2 +- docs/src/manifolds/vector_bundle.md | 3 +-- docs/src/misc/notation.md | 4 ++-- src/Manifolds.jl | 3 ++- src/atlases.jl | 3 ++- src/manifolds/FiberBundle.jl | 23 +++++++++++++++-------- src/manifolds/ProductManifold.jl | 19 ++++++++++--------- src/manifolds/VectorBundle.jl | 2 +- 9 files changed, 35 insertions(+), 27 deletions(-) diff --git a/docs/src/features/utilities.md b/docs/src/features/utilities.md index bd1663df5f..f72a22b481 100644 --- a/docs/src/features/utilities.md +++ b/docs/src/features/utilities.md @@ -19,10 +19,9 @@ Order = [:type, :function] # Public documentation -The following functions are of interest for extending and using the [`ProductManifold`](@ref). +The following functions are of interest for extending and using the [`ProductManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.ProductManifold). ```@docs -submanifold_component submanifold_components ``` diff --git a/docs/src/manifolds/power.md b/docs/src/manifolds/power.md index 1326cf5be8..3b9f70a3ee 100644 --- a/docs/src/manifolds/power.md +++ b/docs/src/manifolds/power.md @@ -70,7 +70,7 @@ which is again a valid point so `is_point(M, p)` here also yields true. A disadvantage might be that with nested arrays one loses a little bit of performance. The data however is nicely encapsulated. Accessing the first data item is just `p[1]`. -For accessing points on power manifolds in both representations you can use [`get_component`](@ref) and [`set_component!`](@ref) functions. +For accessing points on power manifolds in both representations you can use [`get_component`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.get_component-Tuple%7BAbstractPowerManifold,%20Any,%20Vararg%7BAny%7D%7D) and [`set_component!`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.set_component!-Tuple%7BAbstractPowerManifold,%20Any,%20Any,%20Vararg%7BAny%7D%7D) functions. They work work both point representations. ```@example 3 diff --git a/docs/src/manifolds/vector_bundle.md b/docs/src/manifolds/vector_bundle.md index 8bc8f208f7..19d732ca6d 100644 --- a/docs/src/manifolds/vector_bundle.md +++ b/docs/src/manifolds/vector_bundle.md @@ -5,8 +5,7 @@ Vector bundle $E$ is a special case of a [fiber bundle](@ref FiberBundleSection) Tangent bundle is a simple example of a vector bundle, where each fiber is the tangent space at the specified point $p$. An object representing a tangent bundle can be obtained using the constructor called `TangentBundle`. -There is also another type, [`VectorSpaceFiber`](@ref), that represents a specific fiber at a given point. -This distinction is made to reduce the need to repeatedly construct objects of type [`VectorSpaceFiber`](@ref) in certain usage scenarios. +There is also another type, [`VectorSpaceFiber`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.VectorSpaceFiber), that represents a specific fiber at a given point. This is also considered a manifold. ## FVector diff --git a/docs/src/misc/notation.md b/docs/src/misc/notation.md index bcc046c85b..ee7975861d 100644 --- a/docs/src/misc/notation.md +++ b/docs/src/misc/notation.md @@ -11,7 +11,7 @@ Within the documented functions, the utf8 symbols are used whenever possible, as |:--:|:--------------- |:--:|:-- | | ``\tau_p`` | action map by group element ``p`` | ``\mathrm{L}_p``, ``\mathrm{R}_p`` | either left or right | | ``\operatorname{Ad}_p(X)`` | adjoint action of element ``p`` of a Lie group on the element ``X`` of the corresponding Lie algebra | | | -| ``\times`` | Cartesian product of two manifolds | | see [`ProductManifold`](@ref) | +| ``\times`` | Cartesian product of two manifolds | | see [`ProductManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.ProductManifold) | | ``^{\wedge}`` | (n-ary) Cartesian power of a manifold | | see [`PowerManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/manifolds.html#ManifoldsBase.PowerManifold) | | ``\cdot^\mathrm{H}`` | conjugate/Hermitian transpose | | | ``a`` | coordinates of a point in a chart | | see [`get_parameters`](@ref) | @@ -22,7 +22,7 @@ Within the documented functions, the utf8 symbols are used whenever possible, as | ``n`` | dimension (of a manifold) | ``n_1,n_2,\ldots,m, \dim(\mathcal M)``| for the real dimension sometimes also ``\dim_{\mathbb R}(\mathcal M)``| | ``d(\cdot,\cdot)`` | (Riemannian) distance | ``d_{\mathcal M}(\cdot,\cdot)`` | | | ``\exp_p X`` | exponential map at ``p \in \mathcal M`` of a vector ``X \in T_p \mathcal M`` | ``\exp_p(X)`` | | -| ``F`` | a fiber | | see [`Fiber`](@ref) | +| ``F`` | a fiber | | see [`Fiber`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#Fiber) | | ``\mathbb F`` | a field, usually ``\mathbb F \in \{\mathbb R,\mathbb C, \mathbb H\}``, i.e. the real, complex, and quaternion numbers, respectively. | |field a manifold or a basis is based on | | ``\gamma`` | a geodesic | ``\gamma_{p;q}``, ``\gamma_{p,X}`` | connecting two points ``p,q`` or starting in ``p`` with velocity ``X``. | | ``\operatorname{grad} f(p)`` | (Riemannian) gradient of function ``f \colon \mathcal{M} \to \mathbb{R}`` at ``p \in \mathcal{M}`` | | | diff --git a/src/Manifolds.jl b/src/Manifolds.jl index d0e48f4337..bce05384dd 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -512,7 +512,8 @@ Base.in(p, M::AbstractManifold; kwargs...) = is_point(M, p, false; kwargs...) X ∈ TangentSpace(M, p) Check whether `X` is a tangent vector from (in) the tangent space $T_p\mathcal M$, i.e. -the [`TangentSpace`](@ref) at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. +the [`TangentSpace`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.TangentSpace) +at `p` on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) `M`. This method uses [`is_vector`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions.html#ManifoldsBase.is_vector) deactivating the error throw option. """ function Base.in(X, TpM::TangentSpace; kwargs...) diff --git a/src/atlases.jl b/src/atlases.jl index c5f87e5069..b1ca2e4a0e 100644 --- a/src/atlases.jl +++ b/src/atlases.jl @@ -352,7 +352,8 @@ end The basis induced by chart with index `i` from an [`AbstractAtlas`](@ref) `A` of vector space of type `vs`. -For the `vs` a [`TangentSpace`](@ref) this works as follows: +For the `vs` a [`TangentSpace`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.TangentSpace) +this works as follows: Let ``n`` denote the dimension of the manifold ``\mathcal M``. diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index 3885f02b3d..3a3c2ebfac 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -20,12 +20,13 @@ and the topological vector space isometric to the fiber. FiberBundleProductVectorTransport( M::AbstractManifold=DefaultManifold(); - vector_tansport_method_point = default_vector_transport_method(M), - vector_transport_method_fiber = default_vector_transport_method(M), + vector_transport_method_point::AbstractVectorTransportMethod = default_vector_transport_method(M), + vector_transport_method_fiber::AbstractVectorTransportMethod = default_vector_transport_method(M), ) Construct the `FiberBundleProductVectorTransport` using the [`default_vector_transport_method`](@ref), -which uses [`ParallelTransport`](@ref) if no manifold is provied. +which uses [`ParallelTransport`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/vector_transports/#ManifoldsBase.ParallelTransport) +if no manifold is provided. """ struct FiberBundleProductVectorTransport{ TMP<:AbstractVectorTransportMethod, @@ -36,11 +37,15 @@ struct FiberBundleProductVectorTransport{ end function FiberBundleProductVectorTransport( M::AbstractManifold=ManifoldsBase.DefaultManifold(); - vector_tansport_method_point=default_vector_transport_method(M), - vector_transport_method_fiber=default_vector_transport_method(M), + vector_transport_method_point::AbstractVectorTransportMethod=default_vector_transport_method( + M, + ), + vector_transport_method_fiber::AbstractVectorTransportMethod=default_vector_transport_method( + M, + ), ) return FiberBundleProductVectorTransport( - vector_tansport_method_point, + vector_transport_method_point, vector_transport_method_fiber, ) end @@ -48,11 +53,13 @@ end """ FiberBundle{𝔽,TVS<:FiberType,TM<:AbstractManifold{𝔽},TVT<:FiberBundleProductVectorTransport} <: AbstractManifold{𝔽} -Fiber bundle on a [`AbstractManifold`](@ref) `M` of type [`FiberType`](@ref). +Fiber bundle on a [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types/#ManifoldsBase.AbstractManifold) +`M` of type [`FiberType`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.FiberType). Examples include vector bundles, principal bundles or unit tangent bundles, see also [📖 Fiber Bundle](https://en.wikipedia.org/wiki/Fiber_bundle). # Fields -* `manifold` – the [`AbstractManifold`](@ref) manifold the Fiber bundle is defined on +* `manifold` – the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types/#ManifoldsBase.AbstractManifold) + manifold the Fiber bundle is defined on, * `type` – representing the type of fiber we use. # Constructor diff --git a/src/manifolds/ProductManifold.jl b/src/manifolds/ProductManifold.jl index 5070073f62..c906158e83 100644 --- a/src/manifolds/ProductManifold.jl +++ b/src/manifolds/ProductManifold.jl @@ -74,9 +74,9 @@ end flat(M::ProductManifold, p, X::FVector{TangentSpaceType}) use the musical isomorphism to transform the tangent vector `X` from the tangent space at -`p` on the [`ProductManifold`](@ref) `M` to a cotangent vector. -This can be done elementwise for every entry of `X` (with respect to the corresponding -entry in `p`) separately. +`p` on the [`ProductManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.ProductManifold) +`M` to a cotangent vector. This can be done elementwise for every entry of `X` (with respect +to the corresponding entry in `p`) separately. """ flat(::ProductManifold, ::Any...) @@ -117,8 +117,8 @@ end """ manifold_volume(M::ProductManifold) -Return the volume of [`ProductManifold`](@ref) `M`, i.e. product of volumes of the -manifolds `M` is constructed from. +Return the volume of [`ProductManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.ProductManifold) +`M`, i.e. product of volumes of the manifolds `M` is constructed from. """ manifold_volume(M::ProductManifold) = mapreduce(manifold_volume, *, M.manifolds) @@ -218,8 +218,9 @@ end sharp(M::ProductManifold, p, ξ::FVector{CotangentSpaceType}) Use the musical isomorphism to transform the cotangent vector `ξ` from the tangent space at -`p` on the [`ProductManifold`](@ref) `M` to a tangent vector. -This can be done elementwise for every entry of `ξ` (and `p`) separately +`p` on the [`ProductManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.ProductManifold) +`M` to a tangent vector. This can be done elementwise for every entry of `ξ` (and `p`) +separately """ sharp(::ProductManifold, ::Any...) @@ -241,8 +242,8 @@ end @doc raw""" volume_density(M::ProductManifold, p, X) -Return volume density on the [`ProductManifold`](@ref) `M`, i.e. product of constituent -volume densities. +Return volume density on the [`ProductManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.ProductManifold) +`M`, i.e. product of constituent volume densities. """ function volume_density(M::ProductManifold, p, X) dens = map( diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 340b847ba2..da11be360b 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -40,7 +40,7 @@ const VectorBundle{𝔽,TVS,TM,TVT} = FiberBundle{ Tangent bundle for manifold of type `M`, as a manifold with the Sasaki metric [Sasaki:1958](@cite). Exact retraction and inverse retraction can be approximated using [`FiberBundleProductRetraction`](@ref), -[`FiberBundleInverseProductRetraction`](@ref) and [`SasakiRetraction`](@ref). +[`FiberBundleInverseProductRetraction`](@ref) and [`SasakiRetraction`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/retractions/#ManifoldsBase.SasakiRetraction). [`FiberBundleProductVectorTransport`](@ref) can be used as a vector transport. # Constructors From c11f82f6d06d08f0a1934aa12de78ae904198d69 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 17:40:27 +0200 Subject: [PATCH 69/81] fixing again --- docs/src/manifolds/product.md | 2 +- docs/src/misc/internals.md | 4 +--- src/groups/group_action.jl | 2 +- src/manifolds/VectorBundle.jl | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/src/manifolds/product.md b/docs/src/manifolds/product.md index 46546ee098..cd1e38aafb 100644 --- a/docs/src/manifolds/product.md +++ b/docs/src/manifolds/product.md @@ -1,7 +1,7 @@ # [Product manifold](@id ProductManifoldSection) Product manifold $\mathcal M = \mathcal{M}_1 × \mathcal{M}_2 × … × \mathcal{M}_n$ of manifolds $\mathcal{M}_1, \mathcal{M}_2, …, \mathcal{M}_n$. -Points on the product manifold can be constructed using `ArrayPartition` (from `RecursiveArrayTools.jl`) with canonical projections $Π_i : \mathcal{M} → \mathcal{M}_i$ for $i ∈ 1, 2, …, n$ provided by [`submanifold_component`](@ref). +Points on the product manifold can be constructed using `ArrayPartition` (from `RecursiveArrayTools.jl`) with canonical projections $Π_i : \mathcal{M} → \mathcal{M}_i$ for $i ∈ 1, 2, …, n$ provided by [`submanifold_component`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.submanifold_component-Tuple). ```@autodocs Modules = [Manifolds] diff --git a/docs/src/misc/internals.md b/docs/src/misc/internals.md index 3d5d10b2b4..835cdb838e 100644 --- a/docs/src/misc/internals.md +++ b/docs/src/misc/internals.md @@ -13,14 +13,12 @@ Manifolds.mul!_safe Manifolds.nzsign Manifolds.realify Manifolds.realify! -Manifolds.select_from_tuple Manifolds.symmetrize Manifolds.symmetrize! Manifolds.unrealify! Manifolds.usinc Manifolds.usinc_from_cos Manifolds.vec2skew! -Manifolds.ziptuples ``` ## Types in Extensions @@ -29,4 +27,4 @@ Manifolds.ziptuples Modules = [Manifolds] Pages = ["../ext/ManifoldsOrdinaryDiffEqDiffEqCallbacksExt.jl"] Order = [:type, :function] -``` \ No newline at end of file +``` diff --git a/src/groups/group_action.jl b/src/groups/group_action.jl index d0464e8fec..782ce45ecb 100644 --- a/src/groups/group_action.jl +++ b/src/groups/group_action.jl @@ -1,7 +1,7 @@ """ AbstractGroupAction{AD<:ActionDirection} -An abstract group action on a manifold. [`ActionDirection``](@ref)`AD` indicates whether it +An abstract group action on a manifold. [`ActionDirection`](@ref)`AD` indicates whether it is a left or right action. """ abstract type AbstractGroupAction{AD<:ActionDirection} end diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index da11be360b..3db173d1ec 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -19,7 +19,7 @@ fiber_bundle_transport(::VectorSpaceType, ::AbstractManifold) = ParallelTranspor VectorBundle{𝔽,TVS,TM,VTV} = = FiberBundle{𝔽,VectorSpaceFiberType{TVS},TM,TVT} Alias for [`FiberBundle`](@ref) when fiber type is a `TVS` of type -[`VectorSpaceType`](@ref). +[`VectorSpaceType`]https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases/#ManifoldsBase.VectorSpaceType). `VectorSpaceFiberType` is used to encode vector spaces as fiber types. """ From 0d38cf35cda31048e9322b74e5f0028fd017d39f Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 18:21:11 +0200 Subject: [PATCH 70/81] Maybe fix? --- docs/src/features/utilities.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/docs/src/features/utilities.md b/docs/src/features/utilities.md index f72a22b481..b1ecffe3c5 100644 --- a/docs/src/features/utilities.md +++ b/docs/src/features/utilities.md @@ -1,13 +1,14 @@ -# Ease of notation +# Utilities + +## Ease of notation The following terms introduce a nicer notation for some operations, for example using the ∈ operator, $p ∈ \mathcal M$, to determine whether $p$ is a point on the [`AbstractManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/types.html#ManifoldsBase.AbstractManifold) $\mathcal M$. ````@docs in -TangentSpace ```` -# Fallback for the exponential map: Solving the corresponding ODE +## Fallback for the exponential map: Solving the corresponding ODE When additionally loading [`NLSolve.jl`](https://github.com/JuliaNLSolvers/NLsolve.jl) the following fallback for the exponential map is available. @@ -17,15 +18,9 @@ Pages = ["nlsolve.jl"] Order = [:type, :function] ``` -# Public documentation - -The following functions are of interest for extending and using the [`ProductManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ManifoldsBase.ProductManifold). - -```@docs -submanifold_components -``` +## Public documentation -## Specific exception types +### Specific exception types For some manifolds it is useful to keep an extra index, at which point on the manifold, the error occurred as well as to collect all errors that occurred on a manifold. This page contains the manifold-specific error messages this package introduces. From 2cc48b87298b6b4990fb722e25a53f23ff241223 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 19:25:39 +0200 Subject: [PATCH 71/81] cut some redundant tests --- test/manifolds/power_manifold.jl | 85 -------------------------------- 1 file changed, 85 deletions(-) diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index 84621be366..da9a68406c 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -55,11 +55,8 @@ end @test Ms^(5,) === Ms1 @test Mr^(5, 7) === Mr2 - types_s1 = [Array{Float64,2}, HybridArray{Tuple{3,Dynamic()},Float64,2}] types_s2 = [Array{Float64,3}, HybridArray{Tuple{3,Dynamic(),Dynamic()},Float64,3}] - types_r1 = [Array{Float64,3}, HybridArray{Tuple{3,3,Dynamic()},Float64,3}] - types_rn1 = [Vector{Matrix{Float64}}] TEST_STATIC_SIZED && push!(types_rn1, Vector{MMatrix{3,3,Float64,9}}) @@ -173,45 +170,6 @@ end trim(s::String) = s[1:min(length(s), 20)] basis_types = (DefaultOrthonormalBasis(), ProjectedOrthonormalBasis(:svd)) - for T in types_s1 - @testset "Type $(trim(string(T)))..." begin - pts1 = [convert(T, rand(power_s1_pt_dist)) for _ in 1:3] - @test injectivity_radius(Ms1, pts1[1]) == π - basis_diag = get_basis( - Ms1, - pts1[1], - DiagonalizingOrthonormalBasis(log(Ms1, pts1[1], pts1[2])), - ) - basis_arb = get_basis(Ms1, pts1[1], DefaultOrthonormalBasis()) - test_manifold( - Ms1, - pts1; - test_musical_isomorphisms=true, - test_injectivity_radius=false, - test_default_vector_transport=true, - test_project_point=true, - test_project_tangent=true, - vector_transport_methods=[ - ParallelTransport(), - SchildsLadderTransport(), - PoleLadderTransport(), - ], - test_vee_hat=true, - retraction_methods=retraction_methods, - inverse_retraction_methods=inverse_retraction_methods, - point_distributions=[power_s1_pt_dist], - tvector_distributions=[power_s1_tv_dist], - basis_types_to_from=(basis_diag, basis_arb, basis_types...), - rand_tvector_atol_multiplier=600.0, - retraction_atol_multiplier=12.0, - is_tangent_atol_multiplier=500.0, - exp_log_atol_multiplier=20 * prod(power_dimensions(Ms1)), - test_inplace=true, - test_rand_point=true, - test_rand_tvector=true, - ) - end - end for T in types_s2 @testset "Type $(trim(string(T)))..." begin pts2 = [convert(T, rand(power_s2_pt_dist)) for _ in 1:3] @@ -234,29 +192,6 @@ end end end - for T in types_r1 - @testset "Type $(trim(string(T)))..." begin - pts1 = [convert(T, rand(power_r1_pt_dist)) for _ in 1:3] - test_manifold( - Mr1, - pts1; - test_injectivity_radius=false, - test_musical_isomorphisms=true, - test_vee_hat=true, - retraction_methods=retraction_methods, - inverse_retraction_methods=inverse_retraction_methods, - point_distributions=[power_r1_pt_dist], - tvector_distributions=[power_r1_tv_dist], - basis_types_to_from=basis_types, - rand_tvector_atol_multiplier=8.0, - retraction_atol_multiplier=12, - is_tangent_atol_multiplier=12.0, - exp_log_atol_multiplier=2e2 * prod(power_dimensions(Mr2)), - test_inplace=true, - ) - end - end - for T in types_rn1 @testset "Type $(trim(string(T)))..." begin pts1 = [convert(T, rand(power_rn1_pt_dist)) for _ in 1:3] @@ -364,26 +299,6 @@ end """ end - @testset "Power manifold of Circle" begin - pts_t = [[0.0, 1.0, 2.0], [1.0, 1.0, 2.4], [0.0, 2.0, 1.0]] - MT = PowerManifold(Circle(), 3) - @test representation_size(MT) == (3,) - @test pts_t[1][MT, 2] == 1.0 - @test HybridVector{3}(pts_t[1])[MT, 2] == 1.0 - test_manifold( - MT, - pts_t; - test_injectivity_radius=false, - test_musical_isomorphisms=true, - retraction_methods=retraction_methods, - inverse_retraction_methods=inverse_retraction_methods, - rand_tvector_atol_multiplier=5.0, - retraction_atol_multiplier=12, - is_tangent_atol_multiplier=12.0, - test_inplace=true, - ) - end - @testset "Atlas & Induced Basis" begin M = PowerManifold(Euclidean(2), NestedPowerRepresentation(), 2) p = [zeros(2), ones(2)] From 34396b3cef2d50e7588d921b0d86c11ebb091ff5 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 19:48:21 +0200 Subject: [PATCH 72/81] cut repeated tests of ProductManifold (they exist in ManifoldsBase.jl) --- test/manifolds/product_manifold.jl | 293 ----------------------------- 1 file changed, 293 deletions(-) diff --git a/test/manifolds/product_manifold.jl b/test/manifolds/product_manifold.jl index c1bf2d6486..f9233a984e 100644 --- a/test/manifolds/product_manifold.jl +++ b/test/manifolds/product_manifold.jl @@ -58,115 +58,6 @@ using RecursiveArrayTools: ArrayPartition LogarithmicInverseRetraction(), ] - @testset "get_component, set_component!, getindex and setindex!" begin - p1 = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) - @test get_component(Mse, p1, 1) == p1.x[1] - @test get_component(Mse, p1, Val(1)) == p1.x[1] - @test p1[Mse, 1] == p1.x[1] - @test p1[Mse, Val(1)] == p1.x[1] - @test p1[Mse, 1] isa Vector - @test p1[Mse, Val(1)] isa Vector - p2 = [10.0, 12.0] - set_component!(Mse, p1, p2, 2) - @test get_component(Mse, p1, 2) == p2 - p1[Mse, 2] = 2 * p2 - @test p1[Mse, 2] == 2 * p2 - p3 = [11.0, 15.0] - set_component!(Mse, p1, p3, Val(2)) - @test get_component(Mse, p1, Val(2)) == p3 - p1[Mse, Val(2)] = 2 * p3 - @test p1[Mse, Val(2)] == 2 * p3 - - p1ap = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) - @test get_component(Mse, p1ap, 1) == p1ap.x[1] - @test get_component(Mse, p1ap, Val(1)) == p1ap.x[1] - @test p1ap[Mse, 1] == p1ap.x[1] - @test p1ap[Mse, Val(1)] == p1ap.x[1] - @test p1ap[Mse, 1] isa Vector - @test p1ap[Mse, Val(1)] isa Vector - set_component!(Mse, p1ap, p2, 2) - @test get_component(Mse, p1ap, 2) == p2 - p1ap[Mse, 2] = 2 * p2 - @test p1ap[Mse, 2] == 2 * p2 - p3 = [11.0, 15.0] - set_component!(Mse, p1ap, p3, Val(2)) - @test get_component(Mse, p1ap, Val(2)) == p3 - p1ap[Mse, Val(2)] = 2 * p3 - @test p1ap[Mse, Val(2)] == 2 * p3 - - p1c = copy(p1) - p1c.x[1][1] = -123.0 - @test p1c.x[1][1] == -123.0 - @test p1.x[1][1] == 0.0 - copyto!(p1c, p1) - @test p1c.x[1][1] == 0.0 - - p1c.x[1][1] = -123.0 - copyto!(p1ap, p1c) - @test p1ap.x[1][1] == -123.0 - end - - @testset "some ArrayPartition functions" begin - p = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) - q = allocate(p) - @test q.x[1] isa Vector - p = ArrayPartition([[0.0, 1.0, 0.0]], [0.0, 0.0]) - q = allocate(p, Int) - @test q.x[1] isa Vector{Vector{Int}} - end - - @testset "allocate on PowerManifold of ProductManifold" begin - p = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) - q = allocate([p]) - @test q[1] isa ArrayPartition - @test q[1].x[1] isa Vector - - p = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) - q = allocate([p]) - @test q[1] isa ArrayPartition - @test q[1].x[1] isa Vector - end - - @testset "Broadcasting" begin - p1 = ArrayPartition([0.0, 1.0, 0.0], [0.0, 1.0]) - p2 = ArrayPartition([3.0, 4.0, 5.0], [2.0, 5.0]) - br_result = p1 .+ 2.0 .* p2 - @test br_result isa ArrayPartition - @test br_result.x[1] ≈ [6.0, 9.0, 10.0] - @test br_result.x[2] ≈ [4.0, 11.0] - - br_result .= 2.0 .* p1 .+ p2 - @test br_result.x[1] ≈ [3.0, 6.0, 5.0] - @test br_result.x[2] ≈ [2.0, 7.0] - - br_result .= p1 - @test br_result.x[1] ≈ [0.0, 1.0, 0.0] - @test br_result.x[2] ≈ [0.0, 1.0] - - @test axes(p1) == (Base.OneTo(5),) - - # errors - p3 = ArrayPartition([3.0, 4.0, 5.0], [2.0, 5.0], [3.0, 2.0]) - @test_throws DimensionMismatch p1 .+ p3 - @test_throws DimensionMismatch p1 .= p3 - end - - @testset "CompositeManifoldError" begin - Mpr = Sphere(2) × Sphere(2) - p1 = [1.0, 0.0, 0.0] - p2 = [0.0, 1.0, 0.0] - X1 = [0.0, 1.0, 0.2] - X2 = [1.0, 0.0, 0.2] - p = ArrayPartition(p1, p2) - X = ArrayPartition(X1, X2) - pf = ArrayPartition(p1, X1) - Xf = ArrayPartition(X1, p2) - @test is_point(Mpr, p; error=:error) - @test_throws CompositeManifoldError is_point(Mpr, X; error=:error) - @test_throws ComponentManifoldError is_vector(Mpr, pf, X; error=:error) - @test_throws ComponentManifoldError is_vector(Mpr, p, Xf; error=:error) - end - @testset "arithmetic" begin Mee = ProductManifold(Euclidean(3), Euclidean(2)) p1 = ArrayPartition([0.0, 1.0, 0.0], [0.0, 1.0]) @@ -180,28 +71,6 @@ using RecursiveArrayTools: ArrayPartition @test isapprox(Mee, p1 / 2, ArrayPartition([0.0, 0.5, 0.0], [0.0, 0.5])) end - @testset "Show methods" begin - Mse2 = ProductManifold(M1, M1, M2, M2) - @test sprint(show, Mse2) == "ProductManifold($(M1), $(M1), $(M2), $(M2))" - withenv("LINES" => 10, "COLUMNS" => 100) do - @test sprint(show, "text/plain", ProductManifold(M1)) == - "ProductManifold with 1 submanifold:\n $(M1)" - @test sprint(show, "text/plain", Mse2) == - "ProductManifold with 4 submanifolds:\n $(M1)\n $(M1)\n $(M2)\n $(M2)" - return nothing - end - withenv("LINES" => 7, "COLUMNS" => 100) do - @test sprint(show, "text/plain", Mse2) == - "ProductManifold with 4 submanifolds:\n $(M1)\n ⋮\n $(M2)" - return nothing - end - - @test sprint(show, "text/plain", ProductManifold(Mse, Mse)) == """ - ProductManifold with 2 submanifolds: - ProductManifold(Sphere(2, ℝ), Euclidean(2; field=ℝ)) - ProductManifold(Sphere(2, ℝ), Euclidean(2; field=ℝ))""" - end - M3 = Rotations(2) Mser = ProductManifold(M1, M2, M3) @@ -226,101 +95,6 @@ using RecursiveArrayTools: ArrayPartition test_rand_tvector=true, ) - @testset "product vector transport" begin - p = ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]) - q = ArrayPartition([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 - - @testset "Implicit product vector transport" begin - p = ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]) - q = ArrayPartition([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 = ArrayPartition(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 = ArrayPartition(Z1, Z2) - @test isapprox(Mse, q, Y, Z) - end - end - @testset "Parallel transport" begin - p = ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]) - q = ArrayPartition([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 = ArrayPartition(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 = ArrayPartition(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 "ArrayPartition" begin - p = ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]) - @test submanifold_component(Mse, p, 1) === p.x[1] - @test submanifold_component(Mse, p, Val(1)) === p.x[1] - @test submanifold_component(p, 1) === p.x[1] - @test submanifold_component(p, Val(1)) === p.x[1] - @test submanifold_components(Mse, p) === p.x - @test submanifold_components(p) === p.x - end - @testset "manifold tests (static size)" begin Ts = SizedVector{3,Float64} Tr2 = SizedVector{2,Float64} @@ -418,61 +192,6 @@ using RecursiveArrayTools: ArrayPartition @test isapprox(X, X2) end - @testset "get_coordinates" begin - # make sure `get_coordinates` does not return an `ArrayPartition` - p1 = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) - X1 = ArrayPartition([1.0, 0.0, -1.0], [1.0, 0.0]) - Tp1Mse = TangentSpace(Mse, p1) - c = get_coordinates(Tp1Mse, p1, X1, DefaultOrthonormalBasis()) - @test c isa Vector - - p1ap = ArrayPartition([0.0, 1.0, 0.0], [0.0, 0.0]) - X1ap = ArrayPartition([1.0, 0.0, -1.0], [1.0, 0.0]) - Tp1apMse = TangentSpace(Mse, p1ap) - cap = get_coordinates(Tp1apMse, p1ap, X1ap, DefaultOrthonormalBasis()) - @test cap isa Vector - end - - @testset "Basis printing" begin - p = ArrayPartition([1.0, 0.0, 0.0], [1.0, 0.0]) - B = DefaultOrthonormalBasis() - Bc = get_basis(Mse, p, B) - Bc_components_s = sprint.(show, "text/plain", Bc.data.parts) - @test sprint(show, "text/plain", Bc) == """ - $(typeof(B)) for a product manifold - Basis for component 1: - $(Bc_components_s[1]) - Basis for component 2: - $(Bc_components_s[2]) - """ - end - - @testset "Basis-related errors" begin - a = ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]) - B = CachedBasis(DefaultOrthonormalBasis(), ProductBasisData(([],))) - @test_throws AssertionError get_vector!( - Mse, - a, - ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]), - [1.0, 2.0, 3.0, 4.0, 5.0], # this is one element too long, hence assertionerror - B, - ) - @test_throws MethodError get_vector!( - Mse, - a, - ArrayPartition([1.0, 0.0, 0.0], [0.0, 0.0]), - [1.0, 2.0, 3.0, 4.0], - B, # empty elements yield a submanifold MethodError - ) - end - - @testset "allocation promotion" begin - M2c = Euclidean(2; field=ℂ) - Msec = ProductManifold(M1, M2c) - @test Manifolds.allocation_promotion_function(Msec, get_vector, ()) === complex - @test Manifolds.allocation_promotion_function(Mse, get_vector, ()) === identity - end - @testset "empty allocation" begin p = allocate_result(Mse, uniform_distribution) @test isa(p, ArrayPartition) @@ -573,18 +292,6 @@ using RecursiveArrayTools: ArrayPartition ) end - @testset "Riemann tensor" begin - p = ArrayPartition([0.0, 1.0, 0.0], [2.0, 3.0]) - X = ArrayPartition([1.0, 0.0, 0.0], [2.0, 3.0]) - Y = ArrayPartition([0.0, 0.0, 3.0], [-2.0, 3.0]) - Z = ArrayPartition([-1.0, 0.0, 2.0], [2.0, -3.0]) - Xresult = ArrayPartition([6.0, 0.0, 3.0], [0.0, 0.0]) - @test isapprox(riemann_tensor(Mse, p, X, Y, Z), Xresult) - Xresult2 = allocate(Xresult) - riemann_tensor!(Mse, Xresult2, p, X, Y, Z) - @test isapprox(Xresult2, Xresult) - end - @testset "ManifoldDiff" begin p = ArrayPartition([0.0, 1.0, 0.0], [2.0, 3.0]) q = ArrayPartition([1.0, 0.0, 0.0], [-2.0, 3.0]) From 9ea0d836027cd5329c3a397ff1a6dde6d92cbed9 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 21:26:13 +0200 Subject: [PATCH 73/81] a bit of testing --- src/groups/metric.jl | 3 +++ test/groups/circle_group.jl | 3 +++ test/groups/metric.jl | 2 ++ test/manifolds/power_manifold.jl | 4 ++++ 4 files changed, 12 insertions(+) diff --git a/src/groups/metric.jl b/src/groups/metric.jl index f9aa547caf..ed3552421b 100644 --- a/src/groups/metric.jl +++ b/src/groups/metric.jl @@ -392,3 +392,6 @@ end ) return merge_traits(HasRightInvariantMetric(), IsExplicitDecorator()) end + +direction(::LeftInvariantMetric) = LeftAction() +direction(::RightInvariantMetric) = RightAction() diff --git a/test/groups/circle_group.jl b/test/groups/circle_group.jl index c14f1c5d8b..b11672ab6f 100644 --- a/test/groups/circle_group.jl +++ b/test/groups/circle_group.jl @@ -166,4 +166,7 @@ end # issue #489 @test vee(G, [0.0], [1.5]) isa Vector + + # basis + @test number_of_coordinates(G, DefaultOrthonormalBasis()) == 1 end diff --git a/test/groups/metric.jl b/test/groups/metric.jl index 7a18ecf934..f759d20426 100644 --- a/test/groups/metric.jl +++ b/test/groups/metric.jl @@ -140,6 +140,8 @@ end for inv_metric in [LeftInvariantMetric(), RightInvariantMetric()] G = MetricManifold(SpecialEuclidean(3), inv_metric) + @test direction(G) == direction(inv_metric) + M = base_manifold(G) Rn = Rotations(3) p = Matrix(I, 3, 3) diff --git a/test/manifolds/power_manifold.jl b/test/manifolds/power_manifold.jl index da9a68406c..bace6ba9b7 100644 --- a/test/manifolds/power_manifold.jl +++ b/test/manifolds/power_manifold.jl @@ -263,6 +263,7 @@ end pts_t = [[0.0, 1.0, 2.0], [1.0, 1.0, 2.4], [0.0, 2.0, 1.0]] MT = PowerManifold(Circle(), 3) @test representation_size(MT) == (3,) + @test pts_t[2][MT, 3] == 2.4 test_manifold( MT, pts_t; @@ -279,6 +280,9 @@ end test_rand_point=true, test_rand_tvector=true, ) + + ph = HybridVector{3}(pts_t[1]) + @test Manifolds._read(MT, (), ph, 3) == 2.0 end @testset "Basis printing" begin From a86aa3f77df13a08d72615736d63ddba4b6b40da Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 21:35:49 +0200 Subject: [PATCH 74/81] not sure what these are for --- src/groups/circle_group.jl | 1 - src/groups/translation_group.jl | 1 - 2 files changed, 2 deletions(-) diff --git a/src/groups/circle_group.jl b/src/groups/circle_group.jl index 58cbba4278..34c97fb430 100644 --- a/src/groups/circle_group.jl +++ b/src/groups/circle_group.jl @@ -49,7 +49,6 @@ end has_biinvariant_metric(::CircleGroup) = true -has_invariant_metric(::CircleGroup) = true has_invariant_metric(::CircleGroup, ::ActionDirectionAndSide) = true identity_element(G::CircleGroup) = 1.0 diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl index 6f59d59154..cbc752acab 100644 --- a/src/groups/translation_group.jl +++ b/src/groups/translation_group.jl @@ -37,7 +37,6 @@ exp!(::TranslationGroup, q, ::Identity{AdditionOperation}, X) = copyto!(q, X) has_biinvariant_metric(::TranslationGroup) = true -has_invariant_metric(::TranslationGroup) = true has_invariant_metric(::TranslationGroup, ::ActionDirectionAndSide) = true identity_element!(::TranslationGroup, p) = fill!(p, 0) From d98622a0db20bb7ce21aa6b42cac491ba5baeb76 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Sun, 22 Oct 2023 21:50:09 +0200 Subject: [PATCH 75/81] a bit of fiber bundle cleanup --- src/manifolds/FiberBundle.jl | 24 +++++++++++++----------- src/manifolds/VectorBundle.jl | 29 +++++++++++++++-------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/manifolds/FiberBundle.jl b/src/manifolds/FiberBundle.jl index 3a3c2ebfac..97e5ae41d5 100644 --- a/src/manifolds/FiberBundle.jl +++ b/src/manifolds/FiberBundle.jl @@ -9,8 +9,8 @@ Vector transport type on [`FiberBundle`](@ref). # Fields -* `method_point` – vector transport method of the point part -* `method_fiber` – vector transport method of the fiber part. +* `method_horizonal` – vector transport method of the horizontal part (related to manifold M) +* `method_vertical` – vector transport method of the vertical part (related to fibers). The vector transport is derived as a product manifold-style vector transport. The considered product manifold is the product between the manifold ``\mathcal M`` @@ -20,8 +20,8 @@ and the topological vector space isometric to the fiber. FiberBundleProductVectorTransport( M::AbstractManifold=DefaultManifold(); - vector_transport_method_point::AbstractVectorTransportMethod = default_vector_transport_method(M), - vector_transport_method_fiber::AbstractVectorTransportMethod = default_vector_transport_method(M), + vector_transport_method_horizontal::AbstractVectorTransportMethod = default_vector_transport_method(M), + vector_transport_method_vertical::AbstractVectorTransportMethod = default_vector_transport_method(M), ) Construct the `FiberBundleProductVectorTransport` using the [`default_vector_transport_method`](@ref), @@ -32,21 +32,23 @@ struct FiberBundleProductVectorTransport{ TMP<:AbstractVectorTransportMethod, TMV<:AbstractVectorTransportMethod, } <: AbstractVectorTransportMethod - method_point::TMP - method_fiber::TMV + method_horizontal::TMP + method_vertical::TMV end function FiberBundleProductVectorTransport( - M::AbstractManifold=ManifoldsBase.DefaultManifold(); - vector_transport_method_point::AbstractVectorTransportMethod=default_vector_transport_method( + M::AbstractManifold=ManifoldsBase.DefaultManifold(), + fiber::FiberType=ManifoldsBase.TangentSpaceType(); + vector_transport_method_horizontal::AbstractVectorTransportMethod=default_vector_transport_method( M, ), - vector_transport_method_fiber::AbstractVectorTransportMethod=default_vector_transport_method( + vector_transport_method_vertical::AbstractVectorTransportMethod=fiber_bundle_transport( M, + fiber, ), ) return FiberBundleProductVectorTransport( - vector_transport_method_point, - vector_transport_method_fiber, + vector_transport_method_horizontal, + vector_transport_method_vertical, ) end diff --git a/src/manifolds/VectorBundle.jl b/src/manifolds/VectorBundle.jl index 3db173d1ec..d030f6c2c5 100644 --- a/src/manifolds/VectorBundle.jl +++ b/src/manifolds/VectorBundle.jl @@ -7,16 +7,17 @@ Deprecated: an alias for `FiberBundleProductVectorTransport`. const VectorBundleVectorTransport = FiberBundleProductVectorTransport """ - fiber_bundle_transport(fiber::FiberType, M::AbstractManifold) + fiber_bundle_transport(M::AbstractManifold, fiber::FiberType) -Determine the vector tranport used for [`exp`](@ref exp(::FiberBundle, ::Any...)) and +Determine the vector transport used for [`exp`](@ref exp(::FiberBundle, ::Any...)) and [`log`](@ref log(::FiberBundle, ::Any...)) maps on a vector bundle with fiber type `fiber` and manifold `M`. """ -fiber_bundle_transport(::VectorSpaceType, ::AbstractManifold) = ParallelTransport() +fiber_bundle_transport(M::AbstractManifold, ::FiberType) = + default_vector_transport_method(M) """ - VectorBundle{𝔽,TVS,TM,VTV} = = FiberBundle{𝔽,VectorSpaceFiberType{TVS},TM,TVT} + VectorBundle{𝔽,TVS,TM,VTV} = FiberBundle{𝔽,TVS,TM,TVT} where {TVS<:VectorSpaceType} Alias for [`FiberBundle`](@ref) when fiber type is a `TVS` of type [`VectorSpaceType`]https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases/#ManifoldsBase.VectorSpaceType). @@ -65,7 +66,7 @@ function CotangentBundle(M::AbstractManifold, vtm::FiberBundleProductVectorTrans end function bundle_transport_to!(B::TangentBundle, Y, p, X, q) - return vector_transport_to!(B.manifold, Y, p, X, q, B.vector_transport.method_fiber) + return vector_transport_to!(B.manifold, Y, p, X, q, B.vector_transport.method_vertical) end function bundle_transport_tangent_direction!( @@ -277,7 +278,7 @@ function retract_product!(B::VectorBundle, q, p, X, t::Number) xp, Xp + VXF, VXM, - B.vector_transport.method_point, + B.vector_transport.method_horizontal, ) copyto!(B.manifold, xq, xqt) return q @@ -339,8 +340,8 @@ 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) - vector_transport_direction!(M.manifold, VYM, px, VXM, dx, m.method_point), - vector_transport_direction!(M.manifold, VYF, px, VXF, dx, m.method_fiber), + vector_transport_direction!(M.manifold, VYM, px, VXM, dx, m.method_horizontal), + vector_transport_direction!(M.manifold, VYF, px, VXF, dx, m.method_vertical), return Y end @@ -377,8 +378,8 @@ function vector_transport_to!( VXM, VXF = submanifold_components(M.manifold, X) VYM, VYF = submanifold_components(M.manifold, Y) qx, qVx = submanifold_components(M.manifold, q) - vector_transport_to!(M.manifold, VYM, px, VXM, qx, m.method_point) - bundle_transport_tangent_to!(M, VYF, px, pVx, VXF, qx, m.method_fiber) + vector_transport_to!(M.manifold, VYM, px, VXM, qx, m.method_horizontal) + bundle_transport_tangent_to!(M, VYF, px, pVx, VXF, qx, m.method_vertical) return Y end @@ -393,8 +394,8 @@ function _vector_transport_direction( VXM, VXF = submanifold_components(M.manifold, X) dx, dVx = submanifold_components(M.manifold, d) return ArrayPartition( - vector_transport_direction(M.manifold, px, VXM, dx, m.method_point), - bundle_transport_tangent_direction(M, px, pVx, VXF, dx, m.method_fiber), + vector_transport_direction(M.manifold, px, VXM, dx, m.method_horizontal), + bundle_transport_tangent_direction(M, px, pVx, VXF, dx, m.method_vertical), ) end @@ -409,8 +410,8 @@ function _vector_transport_to( VXM, VXF = submanifold_components(M.manifold, X) qx, qVx = submanifold_components(M.manifold, q) return ArrayPartition( - vector_transport_to(M.manifold, px, VXM, qx, m.method_point), - bundle_transport_tangent_to(M, px, pVx, VXF, qx, m.method_fiber), + vector_transport_to(M.manifold, px, VXM, qx, m.method_horizontal), + bundle_transport_tangent_to(M, px, pVx, VXF, qx, m.method_vertical), ) end From db2c0c3736b79b86274d12e83f9fad275e9dffdf Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 23 Oct 2023 09:37:46 +0200 Subject: [PATCH 76/81] improve coverage a little --- test/manifolds/symmetric_positive_definite.jl | 1 + test/manifolds/vector_bundle.jl | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/test/manifolds/symmetric_positive_definite.jl b/test/manifolds/symmetric_positive_definite.jl index f099737bb0..254f187f69 100644 --- a/test/manifolds/symmetric_positive_definite.jl +++ b/test/manifolds/symmetric_positive_definite.jl @@ -266,6 +266,7 @@ include("../utils.jl") @test isapprox(exp!(M, pS, p, zero_vector(M, p)), p) @test ismissing(pS.sqrt) @test ismissing(pS.sqrt_inv) + @test allocate_result(M1, zero_vector, p) isa Matrix end @testset "test BigFloat" begin diff --git a/test/manifolds/vector_bundle.jl b/test/manifolds/vector_bundle.jl index b9a0a07a8d..370aacbafa 100644 --- a/test/manifolds/vector_bundle.jl +++ b/test/manifolds/vector_bundle.jl @@ -141,6 +141,13 @@ struct TestVectorSpaceType <: VectorSpaceType end test_rand_point=true, test_rand_tvector=true, ) + + @test Manifolds.bundle_transport_to( + TB, + p, + convert(T, [0.0, -1.0, -1.0]), + convert(T, [0.0, 1.0, 0.0]), + ) ≈ convert(T, [1.0, 0.0, -1.0]) end end From 74817206285a4f43cd88741ff3d4392b6a2bae43 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 23 Oct 2023 09:57:34 +0200 Subject: [PATCH 77/81] some docs --- src/groups/rotation_translation_action.jl | 55 ++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/groups/rotation_translation_action.jl b/src/groups/rotation_translation_action.jl index ba6627e83d..485879cb77 100644 --- a/src/groups/rotation_translation_action.jl +++ b/src/groups/rotation_translation_action.jl @@ -5,7 +5,7 @@ AD::ActionDirection = LeftAction(), ) -Space of actions of the [`SpecialEuclidean`](@ref) group $\mathrm{SE}(n)$ on a +Space of actions of the [`SpecialEuclidean`](@ref) group ``\mathrm{SE}(n)`` on a Euclidean-like manifold `M` of dimension `n`. Left actions corresponds to active transformations while right actions @@ -46,6 +46,11 @@ function switch_direction(A::RotationTranslationAction{TAD}) where {TAD<:ActionD return RotationTranslationAction(A.manifold, A.SEn, switch_direction(TAD())) end +""" + apply(::RotationTranslationActionOnVector{LeftAction}, a::ArrayPartition, p) + +Rotate point `p` by `a.x[2]` and translate it by `a.x[1]`. +""" function apply(::RotationTranslationActionOnVector{LeftAction}, a::ArrayPartition, p) return a.x[2] * p + a.x[1] end @@ -56,6 +61,11 @@ function apply( ) return p end +""" + apply(::RotationTranslationActionOnVector{RightAction}, a::ArrayPartition, p) + +Translate point `p` by `-a.x[1]` and rotate it by inverse of `a.x[2]`. +""" function apply(::RotationTranslationActionOnVector{RightAction}, a::ArrayPartition, p) return a.x[2] \ (p - a.x[1]) end @@ -82,6 +92,11 @@ function apply!( return q end +""" + inverse_apply(::RotationTranslationActionOnVector{LeftAction}, a::ArrayPartition, p) + +Translate point `p` by `-a.x[1]` and rotate it by inverse of `a.x[2]`. +""" function inverse_apply( ::RotationTranslationActionOnVector{LeftAction}, a::ArrayPartition, @@ -89,6 +104,11 @@ function inverse_apply( ) return a.x[2] \ (p - a.x[1]) end +""" + inverse_apply(::RotationTranslationActionOnVector{RightAction}, a::ArrayPartition, p) + +Rotate point `p` by `a.x[2]` and translate it by `a.x[1]`. +""" function inverse_apply( ::RotationTranslationActionOnVector{RightAction}, a::ArrayPartition, @@ -97,6 +117,17 @@ function inverse_apply( return a.x[2] * p + a.x[1] end +""" + apply_diff( + ::RotationTranslationActionOnVector{LeftAction}, + a::ArrayPartition, + p, + X, + ) + +Compute differential of `apply` on left [`RotationTranslationActionOnVector`](@ref), +with respect to `p`, i.e. left-multiply vector `X` tangent at `p` by `a.x[2]`. +""" function apply_diff( ::RotationTranslationActionOnVector{LeftAction}, a::ArrayPartition, @@ -113,6 +144,17 @@ function apply_diff( ) return X end +""" + apply_diff( + ::RotationTranslationActionOnVector{RightAction}, + a::ArrayPartition, + p, + X, + ) + +Compute differential of `apply` on right [`RotationTranslationActionOnVector`](@ref), +with respect to `p`, i.e. left-divide vector `X` tangent at `p` by `a.x[2]`. +""" function apply_diff( ::RotationTranslationActionOnVector{RightAction}, a::ArrayPartition, @@ -168,6 +210,17 @@ function apply_diff!( return copyto!(Y, X) end +""" + apply_diff_group( + ::RotationTranslationActionOnVector{LeftAction}, + ::SpecialEuclideanIdentity, + X, + p, + ) + +Compute differential of `apply` on left [`RotationTranslationActionOnVector`](@ref), +with respect to `a` at identity, i.e. left-multiply point `p` by `X.x[2]`. +""" function apply_diff_group( ::RotationTranslationActionOnVector{LeftAction}, ::SpecialEuclideanIdentity, From 697df39f7fd15f6484b10265e7592d2ee64cb703 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 23 Oct 2023 11:09:52 +0200 Subject: [PATCH 78/81] docs improvements; split group actions to a separate page --- docs/make.jl | 1 + docs/src/features/group_actions.md | 63 ++++++++++++++++++++++ docs/src/manifolds/group.md | 66 +---------------------- src/Manifolds.jl | 1 + src/groups/group_operation_action.jl | 2 +- src/groups/rotation_action.jl | 2 +- src/groups/rotation_translation_action.jl | 7 +++ 7 files changed, 75 insertions(+), 67 deletions(-) create mode 100644 docs/src/features/group_actions.md diff --git a/docs/make.jl b/docs/make.jl index 8dfca558e6..7d3b3a66ee 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -155,6 +155,7 @@ makedocs(; "Atlases and charts" => "features/atlases.md", "Differentiation" => "features/differentiation.md", "Distributions" => "features/distributions.md", + "Group actions" => "features/group_actions.md", "Integration" => "features/integration.md", "Statistics" => "features/statistics.md", "Testing" => "features/testing.md", diff --git a/docs/src/features/group_actions.md b/docs/src/features/group_actions.md new file mode 100644 index 0000000000..22bf676ba9 --- /dev/null +++ b/docs/src/features/group_actions.md @@ -0,0 +1,63 @@ +# Group actions + +Group actions represent actions of a given group on a specified manifold. +The following operations are available: + +* [`action_side`](@ref): whether action acts from the [`LeftSide`](@ref) or [`RightSide`](@ref) (not to be confused with action direction). +* [`apply`](@ref): performs given action of an element of the group on an object of compatible type. +* [`apply_diff`](@ref): differential of [`apply`](@ref) with respect to the object it acts upon. +* [`direction`](@ref): tells whether a given action is [`LeftAction`](@ref), [`RightAction`](@ref). +* [`inverse_apply`](@ref): performs given action of the inverse of an element of the group on an object of compatible type. By default inverts the element and calls [`apply`](@ref) but it may be have a faster implementation for some actions. +* [`inverse_apply_diff`](@ref): counterpart of [`apply_diff`](@ref) for [`inverse_apply`](@ref). +* [`optimal_alignment`](@ref): determine the element of a group that, when it acts upon a point, produces the element closest to another given point in the metric of the G-manifold. + +Furthermore, group operation action features the following: + +* [`translate`](@ref Main.Manifolds.translate): an operation that performs either ([`LeftAction`](@ref)) on the [`LeftSide`](@ref) or ([`RightAction`](@ref)) on the [`RightSide`](@ref) translation, or actions by inverses of elements ([`RightAction`](@ref) on the [`LeftSide`](@ref) and [`LeftAction`](@ref) on the [`RightSide`](@ref)). This is by default performed by calling [`compose`](@ref) with appropriate order of arguments. This function is separated from `compose` mostly to easily represent its differential, [`translate_diff`](@ref). +* [`translate_diff`](@ref): differential of [`translate`](@ref Main.Manifolds.translate) with respect to the point being translated. +* [`adjoint_action`](@ref): adjoint action of a given element of a Lie group on an element of its Lie algebra. +* [`lie_bracket`](@ref): Lie bracket of two vectors from a Lie algebra corresponding to a given group. + +The following group actions are available: + +* Group operation action [`GroupOperationAction`](@ref) that describes action of a group on itself. +* [`RotationAction`](@ref), that is action of [`SpecialOrthogonal`](@ref) group on different manifolds. +* [`TranslationAction`](@ref), which is the action of [`TranslationGroup`](@ref) group on different manifolds. + +```@autodocs +Modules = [Manifolds] +Pages = ["groups/group_action.jl"] +Order = [:type, :function] +``` + +## Group operation action + +```@autodocs +Modules = [Manifolds] +Pages = ["groups/group_operation_action.jl"] +Order = [:type, :function] +``` + +## Rotation action + +```@autodocs +Modules = [Manifolds] +Pages = ["groups/rotation_action.jl"] +Order = [:type, :function] +``` + +## Translation action + +```@autodocs +Modules = [Manifolds] +Pages = ["groups/translation_action.jl"] +Order = [:type, :function] +``` + +## Rotation-translation action (special Euclidean) + +```@autodocs +Modules = [Manifolds] +Pages = ["groups/rotation_translation_action.jl"] +Order = [:type, :const, :function] +``` diff --git a/docs/src/manifolds/group.md b/docs/src/manifolds/group.md index 78d7593db4..c7c328fcf2 100644 --- a/docs/src/manifolds/group.md +++ b/docs/src/manifolds/group.md @@ -1,4 +1,4 @@ -# [Group manifolds and actions](@id GroupManifoldSection) +# [Group manifolds](@id GroupManifoldSection) Lie groups, groups that are Riemannian manifolds with a smooth binary group operation [`AbstractGroupOperation`](@ref), are implemented as [`AbstractDecoratorManifold`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/decorator.html#ManifoldsBase.AbstractDecoratorManifold) and specifying the group operation using the [`IsGroupManifold`](@ref) or by decorating an existing manifold with a group operation using [`GroupManifold`](@ref). @@ -180,70 +180,6 @@ Pages = ["groups/translation_group.jl"] Order = [:constant, :type, :function] ``` -## Group actions - -Group actions represent actions of a given group on a specified manifold. -The following operations are available: - -* [`action_side`](@ref): whether action acts from the [`LeftSide`](@ref) or [`RightSide`](@ref) (not to be confused with action direction). -* [`apply`](@ref): performs given action of an element of the group on an object of compatible type. -* [`apply_diff`](@ref): differential of [`apply`](@ref) with respect to the object it acts upon. -* [`direction`](@ref): tells whether a given action is [`LeftAction`](@ref), [`RightAction`](@ref). -* [`inverse_apply`](@ref): performs given action of the inverse of an element of the group on an object of compatible type. By default inverts the element and calls [`apply`](@ref) but it may be have a faster implementation for some actions. -* [`inverse_apply_diff`](@ref): counterpart of [`apply_diff`](@ref) for [`inverse_apply`](@ref). -* [`optimal_alignment`](@ref): determine the element of a group that, when it acts upon a point, produces the element closest to another given point in the metric of the G-manifold. - -Furthermore, group operation action features the following: - -* [`translate`](@ref Main.Manifolds.translate): an operation that performs either ([`LeftAction`](@ref)) on the [`LeftSide`](@ref) or ([`RightAction`](@ref)) on the [`RightSide`](@ref) translation, or actions by inverses of elements ([`RightAction`](@ref) on the [`LeftSide`](@ref) and [`LeftAction`](@ref) on the [`RightSide`](@ref)). This is by default performed by calling [`compose`](@ref) with appropriate order of arguments. This function is separated from `compose` mostly to easily represent its differential, [`translate_diff`](@ref). -* [`translate_diff`](@ref): differential of [`translate`](@ref Main.Manifolds.translate) with respect to the point being translated. -* [`adjoint_action`](@ref): adjoint action of a given element of a Lie group on an element of its Lie algebra. -* [`lie_bracket`](@ref): Lie bracket of two vectors from a Lie algebra corresponding to a given group. - -The following group actions are available: - -* Group operation action [`GroupOperationAction`](@ref) that describes action of a group on itself. -* [`RotationAction`](@ref), that is action of [`SpecialOrthogonal`](@ref) group on different manifolds. -* [`TranslationAction`](@ref), which is the action of [`TranslationGroup`](@ref) group on different manifolds. - -```@autodocs -Modules = [Manifolds] -Pages = ["groups/group_action.jl"] -Order = [:type, :function] -``` - -### Group operation action - -```@autodocs -Modules = [Manifolds] -Pages = ["groups/group_operation_action.jl"] -Order = [:type, :function] -``` - -### Rotation action - -```@autodocs -Modules = [Manifolds] -Pages = ["groups/rotation_action.jl"] -Order = [:type, :function] -``` - -### Translation action - -```@autodocs -Modules = [Manifolds] -Pages = ["groups/translation_action.jl"] -Order = [:type, :function] -``` - -### Rotation-translation action (special Euclidean) - -```@autodocs -Modules = [Manifolds] -Pages = ["groups/rotation_translation_action.jl"] -Order = [:type, :function] -``` - ## Metrics on groups Lie groups by default typically forward all metric-related operations like exponential or logarithmic map to the underlying manifold, for example [`SpecialOrthogonal`](@ref) uses methods for [`Rotations`](@ref) (which is, incidentally, bi-invariant), or [`SpecialEuclidean`](@ref) uses product metric of the translation and rotation parts (which is not invariant under group operation). diff --git a/src/Manifolds.jl b/src/Manifolds.jl index bce05384dd..e56361fcac 100644 --- a/src/Manifolds.jl +++ b/src/Manifolds.jl @@ -932,6 +932,7 @@ export AbstractGroupAction, RightSide, RotationAction, RotationTranslationAction, + RotationTranslationActionOnVector, SemidirectProductGroup, SpecialEuclidean, SpecialLinear, diff --git a/src/groups/group_operation_action.jl b/src/groups/group_operation_action.jl index 87b3585581..34e6de888c 100644 --- a/src/groups/group_operation_action.jl +++ b/src/groups/group_operation_action.jl @@ -6,7 +6,7 @@ An element `p` of the group can act upon another another element by either: * left action from the left side: ``L_p: q ↦ p \circ q``, * right action from the left side: ``L'_p: q ↦ p^{-1} \circ q``, * right action from the right side: ``R_p: q ↦ q \circ p``, -* left action from the right side: ``R'_p &: q ↦ q \circ p^{-1}``. +* left action from the right side: ``R'_p: q ↦ q \circ p^{-1}``. # Constructor diff --git a/src/groups/rotation_action.jl b/src/groups/rotation_action.jl index 25f211177b..cebd3016a4 100644 --- a/src/groups/rotation_action.jl +++ b/src/groups/rotation_action.jl @@ -274,7 +274,7 @@ of computation are described in Section 2.2.1 of [SrivastavaKlassen:2016](@cite) The formula reads ```math O^{*} = \begin{cases} -UV^T & \text{if } \operatorname{det}(p q^{\mathrm{T}})\\ +UV^T & \text{if } \operatorname{det}(p q^{\mathrm{T}}) \geq 0\\ U K V^{\mathrm{T}} & \text{otherwise} \end{cases} ``` diff --git a/src/groups/rotation_translation_action.jl b/src/groups/rotation_translation_action.jl index 485879cb77..ea706e3639 100644 --- a/src/groups/rotation_translation_action.jl +++ b/src/groups/rotation_translation_action.jl @@ -32,6 +32,13 @@ function Base.show(io::IO, A::RotationTranslationAction) return print(io, "RotationTranslationAction($(A.manifold), $(A.SEn), $(direction(A)))") end +""" + RotationTranslationActionOnVector{TAD,𝔽,TE,TSE} + +Alias for [`RotationTranslationAction`](@ref) where the manifold `M` is [`Euclidean`](@ref) +or [`TranslationGroup`](@ref) with size of type `TE`, and [`SpecialEuclidean`](@ref) +group has size type `TSE`. +""" const RotationTranslationActionOnVector{TAD,𝔽,TE,TSE} = RotationTranslationAction{ TAD, <:Union{Euclidean{TE,𝔽},TranslationGroup{TE,𝔽}}, From da0d4b37784bc2a970e4ead44c809487b11201f9 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 23 Oct 2023 14:49:36 +0200 Subject: [PATCH 79/81] Apply suggestions from code review Co-authored-by: Ronny Bergmann --- src/manifolds/Rotations.jl | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl index d1859026b3..89f87f326b 100644 --- a/src/manifolds/Rotations.jl +++ b/src/manifolds/Rotations.jl @@ -232,16 +232,25 @@ end Return a random point on the manifold [`Rotations`](@ref) `M` by generating a (Gaussian) random orthogonal matrix with determinant ``+1``. Let -``QR = A`` +```math +QR = A +``` be the QR decomposition of a random matrix ``A``, then the formula reads -``p = QD`` +```math +p = QD +``` where ``D`` is a diagonal matrix with the signs of the diagonal entries of ``R``, i.e. -``D_{ij}=\begin{cases} \operatorname{sgn}(R_{ij}) & \text{if} \; i=j \\ 0 & \, \text{otherwise} \end{cases}.`` +```math +D_{ij}=\begin{cases} + \operatorname{sgn}(R_{ij}) & \text{if} \; i=j \\ + 0 & \, \text{otherwise} +\end{cases}. +``` It can happen that the matrix gets -1 as a determinant. In this case, the first and second columns are swapped. From 489b2f700e648c3fabf8144ec428be5cda10aca1 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 23 Oct 2023 15:00:30 +0200 Subject: [PATCH 80/81] add changelog to docs --- docs/make.jl | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 7d3b3a66ee..0caf926f5a 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -41,23 +41,25 @@ using OrdinaryDiffEq, BoundaryValueDiffEq, DiffEqCallbacks using Test, FiniteDifferences ENV["GKSwstype"] = "100" -# (d) add contributing.md to docs +# (d) add CONTRIBUTING.md and NEWS.md to docs generated_path = joinpath(@__DIR__, "src", "misc") base_url = "https://github.com/JuliaManifolds/Manifolds.jl/blob/master/" isdir(generated_path) || mkdir(generated_path) -open(joinpath(generated_path, "contributing.md"), "w") do io - # Point to source license file - println( - io, - """ - ```@meta - EditURL = "$(base_url)CONTRIBUTING.md" - ``` - """, - ) - # Write the contents out below the meta block - for line in eachline(joinpath(dirname(@__DIR__), "CONTRIBUTING.md")) - println(io, line) +for fname in ["CONTRIBUTING.md", "NEWS.md"] + open(joinpath(generated_path, fname), "w") do io + # Point to source license file + println( + io, + """ + ```@meta + EditURL = "$(base_url)$(fname)" + ``` + """, + ) + # Write the contents out below the meta block + for line in eachline(joinpath(dirname(@__DIR__), fname)) + println(io, line) + end end end @@ -163,7 +165,8 @@ makedocs(; ], "Miscellanea" => [ "About" => "misc/about.md", - "Contributing" => "misc/contributing.md", + "Changelog" => "misc/NEWS.md", + "Contributing" => "misc/CONTRIBUTING.md", "Internals" => "misc/internals.md", "Notation" => "misc/notation.md", "References" => "misc/references.md", From 34fcb0a79acbe38c025dce515437f56aec5c37b1 Mon Sep 17 00:00:00 2001 From: Mateusz Baran Date: Mon, 23 Oct 2023 15:41:01 +0200 Subject: [PATCH 81/81] fix link --- docs/src/misc/about.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/misc/about.md b/docs/src/misc/about.md index 31c790515d..b94d74e017 100644 --- a/docs/src/misc/about.md +++ b/docs/src/misc/about.md @@ -14,4 +14,4 @@ See the [GitHub contributors page](https://github.com/JuliaManifolds/Manifolds.jl/graphs/contributors). -[Contributions](contributing.md) are welcome! +[Contributions](CONTRIBUTING.md) are welcome!