diff --git a/Project.toml b/Project.toml index 31c9281f..705d0bb1 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ManifoldsBase" uuid = "3362f125-f0bb-47a3-aa74-596ffd7ef2fb" authors = ["Seth Axen ", "Mateusz Baran ", "Ronny Bergmann ", "Antoine Levitt "] -version = "0.8.0" +version = "0.8.1" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" diff --git a/src/EmbeddedManifold.jl b/src/EmbeddedManifold.jl index 9a27feb1..41898a9d 100644 --- a/src/EmbeddedManifold.jl +++ b/src/EmbeddedManifold.jl @@ -6,7 +6,7 @@ A type used to specify properties of an [`AbstractEmbeddedManifold`](@ref). abstract type AbstractEmbeddingType end """ - AbstractEmbeddedManifold{T<:AbstractEmbeddingType,𝔽} <: AbstractDecoratorManifold{𝔽} + AbstractEmbeddedManifold{𝔽,T<:AbstractEmbeddingType,𝔽} <: AbstractDecoratorManifold{𝔽} An abstract type for embedded manifolds, which acts as an [`AbstractDecoratorManifold`](@ref). The functions of the manifold that is embedded can hence be just passed on to the embedding. @@ -20,7 +20,7 @@ abstract type AbstractEmbeddedManifold{𝔽,T<:AbstractEmbeddingType} <: AbstractDecoratorManifold{𝔽} end """ -DefaultEmbeddingType <: AbstractEmbeddingType + DefaultEmbeddingType <: AbstractEmbeddingType A type of default embedding that does not have any special properties. """ diff --git a/src/ManifoldsBase.jl b/src/ManifoldsBase.jl index 18d4c151..63fed2cf 100644 --- a/src/ManifoldsBase.jl +++ b/src/ManifoldsBase.jl @@ -737,8 +737,9 @@ shortest_geodesic(M::Manifold, p, q, T::AbstractVector) = geodesic(M, p, log(M, vector_transport_along(M::Manifold, p, X, c) vector_transport_along(M::Manifold, p, X, c, method::AbstractVectorTransportMethod) -Transport a vector `X` from a point `p` along the curve `c` such that `c(0)` is equal to `p` -to the point `c(1)` using the `method`, which defaults to [`ParallelTransport`](@ref). +Transport a vector `X` from the tangent space at a point `p` on the [`Manifold`](@ref) `M` +along the curve `c` such that `c(0)` is equal to `p` to the point `c(1)` using the `method`, +which defaults to [`ParallelTransport`](@ref). """ function vector_transport_along(M::Manifold, p, X, c) return vector_transport_along(M, p, X, c, ParallelTransport()) @@ -753,9 +754,9 @@ end vector_transport_along!(M::Manifold, Y, p, X, c) vector_transport_along!(M::Manifold, Y, p, X, c, method::AbstractVectorTransportMethod) -Transport a vector `X` from a point `p` along the curve `c` such that `c(0)` is equal to `p` -to the point `c(1)` using the `method`, which defaults to [`ParallelTransport`](@ref). -The result is saved to `Y`. +Transport a vector `X` from the tangent space at a point `p` on the [`Manifold`](@ref) `M` +along the curve `c` such that `c(0)` is equal to `p` to the point `c(1)` using the `method`, +which defaults to [`ParallelTransport`](@ref). The result is saved to `Y`. """ function vector_transport_along!(M::Manifold, Y, p, X, c) return vector_transport_along!(M, Y, p, X, c, ParallelTransport()) @@ -785,9 +786,10 @@ end vector_transport_direction(M::Manifold, p, X, d) vector_transport_direction(M::Manifold, p, X, d, method::AbstractVectorTransportMethod) -Transport a vector `X` from a point `p` in the direction indicated by the tangent vector `d` -at point `p`. By default, [`exp`](@ref) and [`vector_transport_to!`](@ref) are used with -the `method`, which defaults to [`ParallelTransport`](@ref). +Transport a vector `X` from the tangent space at a point `p` on the [`Manifold`](@ref) `M` +in the direction indicated by the tangent vector `d` at `p`. By default, [`exp`](@ref) and +[`vector_transport_to!`](@ref) are used with the `method`, which defaults +to [`ParallelTransport`](@ref). """ function vector_transport_direction(M::Manifold, p, X, d) return vector_transport_direction(M, p, X, d, ParallelTransport()) @@ -808,10 +810,10 @@ end vector_transport_direction!(M::Manifold, Y, p, X, d) vector_transport_direction!(M::Manifold, Y, p, X, d, method::AbstractVectorTransportMethod) -Transport a vector `X` from a point `p` in the direction indicated by the tangent vector `d` -at point `p`. The result is saved to `Y`. By default, [`exp`](@ref) and -[`vector_transport_to!`](@ref) are used with the `method`, which defaults to -[`ParallelTransport`](@ref). +Transport a vector `X` from the tangent space at a point `p` on the [`Manifold`](@ref) `M` +in the direction indicated by the tangent vector `d` at `p`. By default, [`exp`](@ref) and +[`vector_transport_to!`](@ref) are used with the `method`, which defaults +to [`ParallelTransport`](@ref). The result is saved to `Y`. """ function vector_transport_direction!(M::Manifold, Y, p, X, d) return vector_transport_direction!(M, Y, p, X, d, ParallelTransport()) @@ -832,7 +834,8 @@ end vector_transport_to(M::Manifold, p, X, q) vector_transport_to(M::Manifold, p, X, q, method::AbstractVectorTransportMethod) -Compute the vector transport of vector `X` at point `p` to point `q`. +Transport a vector `X` from the tangent space at a point `p` on the [`Manifold`](@ref) `M` +along the [`shortest_geodesic`](@ref) to the tangent space at another point `q`. By default, the [`AbstractVectorTransportMethod`](@ref) `method` is [`ParallelTransport`](@ref). """ @@ -849,9 +852,10 @@ end vector_transport_to!(M::Manifold, Y, p, X, q) vector_transport_to!(M::Manifold, Y, p, X, q, method::AbstractVectorTransportMethod) -Compute the vector transport of vector `X` at point `p` to point `q`. -The result is saved to `Y`. By default, the [`AbstractVectorTransportMethod`](@ref) `method` -is [`ParallelTransport`](@ref). +Transport a vector `X` from the tangent space at a point `p` on the [`Manifold`](@ref) `M` +along the [`shortest_geodesic`](@ref) to the tangent space at another point `q`. +By default, the [`AbstractVectorTransportMethod`](@ref) `method` is +[`ParallelTransport`](@ref). The result is saved to `Y`. """ function vector_transport_to!(M::Manifold, Y, p, q, X) return vector_transport_to!(M, Y, p, q, X, ParallelTransport()) @@ -859,9 +863,9 @@ end """ vector_transport_to!(M::Manifold, Y, p, X, q, method::ProjectionTransport) -Transport a vector `X` from the tangent space at `p` on a [`Manifold`](@ref) `M` by +Transport a vector `X` from the tangent space at `p` on the [`Manifold`](@ref) `M` by interpreting it as an element of the embedding and then projecting it onto the tangent space -at `q`. This method requires [`project`](@ref). +at `q`. This method requires [`project`](@ref project(M::Manifold, p, X)). """ function vector_transport_to!(M::Manifold, Y, p, X, q, ::ProjectionTransport) return project!(M, Y, q, X) @@ -977,6 +981,7 @@ export allocate, manifold_dimension, norm, number_eltype, + number_of_coordinates, number_system, project, project!, diff --git a/src/ValidationManifold.jl b/src/ValidationManifold.jl index 9c553d31..e7038c68 100644 --- a/src/ValidationManifold.jl +++ b/src/ValidationManifold.jl @@ -1,13 +1,13 @@ """ - ValidationManifold{M<:Manifold} <: Manifold + ValidationManifold{𝔽,M<:Manifold{𝔽}} <: AbstractDecoratorManifold{𝔽} -A manifold to encapsulate manifolds working on array representations of `MPoints` and -`TVectors` in a transparent way, such that for these manifolds it's not necessary to -introduce explicit types for the points and tangent vectors, but they are +A manifold to encapsulate manifolds working on array representations of [`MPoint`](@ref)s +and [`TVector`](@ref)s in a transparent way, such that for these manifolds it's not +necessary to introduce explicit types for the points and tangent vectors, but they are encapsulated/stripped automatically when needed. -This manifold is a decorator for a manifold, i.e. it decorates a manifold `M` with types -points, vectors, and covectors. +This manifold is a decorator for a manifold, i.e. it decorates a [`Manifold`](@ref) `M` +with types points, vectors, and covectors. """ struct ValidationManifold{𝔽,M<:Manifold{𝔽}} <: AbstractDecoratorManifold{𝔽} manifold::M @@ -183,7 +183,8 @@ function get_basis( kwargs..., ) where {𝔽} is_manifold_point(M, p, true; kwargs...) - Ξ = invoke(get_basis, Tuple{ValidationManifold,Any,AbstractOrthogonalBasis}, M, p, B; kwargs...) + get_basis_invoke_types = Tuple{ValidationManifold,Any,Union{AbstractOrthogonalBasis,CachedBasis{𝔽,<:AbstractOrthogonalBasis{𝔽}}} where {𝔽}} + Ξ = invoke(get_basis, get_basis_invoke_types, M, p, B; kwargs...) bvectors = get_vectors(M, p, Ξ) N = length(bvectors) for i = 1:N diff --git a/src/bases.jl b/src/bases.jl index fbd5d4e1..4ba22d00 100644 --- a/src/bases.jl +++ b/src/bases.jl @@ -117,8 +117,8 @@ struct DiagonalizingBasisData{D,V,ET} vectors::V end -const DefaultOrDiagonalizingBasis = - Union{DefaultOrthonormalBasis,DiagonalizingOrthonormalBasis} +const DefaultOrDiagonalizingBasis{𝔽} = + Union{DefaultOrthonormalBasis{𝔽},DiagonalizingOrthonormalBasis{𝔽}} """ CachedBasis{𝔽,V,<:AbstractBasis{𝔽}} <: AbstractBasis{𝔽} @@ -156,10 +156,6 @@ function get_vector end const all_uncached_bases = Union{AbstractBasis, DefaultBasis, DefaultOrthogonalBasis, DefaultOrthonormalBasis} const DISAMBIGUATION_BASIS_TYPES = [ CachedBasis, - CachedBasis{ℝ,<:AbstractBasis{ℝ}}, - CachedBasis{ℂ,<:AbstractBasis{ℂ}}, - CachedBasis{ℝ,<:AbstractOrthogonalBasis{ℝ}}, - CachedBasis{ℝ,<:AbstractOrthonormalBasis{ℝ}}, DefaultBasis, DefaultOrthonormalBasis, DefaultOrthogonalBasis, @@ -169,14 +165,20 @@ const DISAMBIGUATION_BASIS_TYPES = [ VeeOrthogonalBasis, ] -function allocate_result(M::Manifold, f::typeof(get_coordinates), p, X, B) +function allocate_result( + M::Manifold, + f::typeof(get_coordinates), + p, + X, + B::AbstractBasis, +) T = allocate_result_type(M, f, (p, X)) - return allocate(p, T, manifold_dimension(M)) + return allocate(p, T, number_of_coordinates(M, B)) end function allocate_result(M::Manifold, f::typeof(get_coordinates), p, X, B::CachedBasis) T = allocate_result_type(M, f, (p, X)) - return allocate(p, T, length(get_vectors(M, p, B))) + return allocate(p, T, number_of_coordinates(M, B)) end @inline function allocate_result_type( @@ -229,6 +231,10 @@ See also: [`get_coordinates`](@ref), [`get_vector`](@ref) function get_basis(M::Manifold, p, B::AbstractBasis) error("get_basis not implemented for manifold of type $(typeof(M)) a point of type $(typeof(p)) and basis of type $(typeof(B)).") end +@decorator_transparent_signature get_basis(M::AbstractDecoratorManifold, p, B::AbstractBasis) +function decorator_transparent_dispatch(::typeof(get_basis), ::Manifold, args...) + return Val(:parent) +end function get_basis(M::Manifold, p, B::DefaultOrthonormalBasis) dim = manifold_dimension(M) @@ -305,6 +311,11 @@ function get_basis( error("get_basis with bases $(typeof(B)) only found $(K) orthonormal basis vectors, but manifold dimension is $(dim).") end end +for BT in DISAMBIGUATION_BASIS_TYPES + eval(quote + @decorator_transparent_signature get_basis(M::AbstractDecoratorManifold, p, B::$BT) + end) +end """ get_coordinates(M::Manifold, p, X, B::AbstractBasis) @@ -354,11 +365,14 @@ end function get_coordinates!(M::Manifold, Y, p, X, B::DefaultOrthogonalBasis) return get_coordinates!(M, Y, p, X, DefaultOrthonormalBasis(number_system(B))) end -function get_coordinates!(M::Manifold{𝔾}, Y, p, X, C::CachedBasis{𝔽,B,V}) where {B,V,𝔾,𝔽} - map!(vb -> conj(inner(M, p, X, vb)), Y, get_vectors(M, p, C)) +function get_coordinates!(M::Manifold, Y, p, X, B::CachedBasis) + _get_coordinates!(M, number_system(M), Y, p, X, B, number_system(B)) +end +function _get_coordinates!(M::Manifold, ::ComplexNumbers, Y, p, X, B::CachedBasis,::RealNumbers) + map!(vb -> conj(inner(M, p, X, vb)), Y, get_vectors(M, p, B)) return Y end -function get_coordinates!(M::Manifold{𝔽}, Y, p, X, C::CachedBasis{𝔽,B,V}) where {B,V,𝔽} +function _get_coordinates!(M::Manifold, a::𝔽, Y, p, X, C::CachedBasis, b::𝔽) where {𝔽} map!(vb -> real(inner(M, p, X, vb)), Y, get_vectors(M, p, C)) return Y end @@ -417,6 +431,7 @@ function get_vector!(M::Manifold, Y, p, X, B::CachedBasis) # 2) guarantees a reasonable array type `Y` # (for example scalar * `SizedValidation` is an `SArray`) bvectors = get_vectors(M, p, B) + #print("hi.\nB:$(B)\n& X:$(X)\n\nyields\n $(bvectors).") if _get_vector_cache_broadcast(bvectors[1]) === Val(false) Xt = X[1] * bvectors[1] copyto!(Y, Xt) @@ -471,6 +486,20 @@ inverse. hat(M::Manifold, p, X) = get_vector(M, p, X, VeeOrthogonalBasis()) hat!(M::Manifold, Y, p, X) = get_vector!(M, Y, p, X, VeeOrthogonalBasis()) +""" + number_of_coordinates(M::Manifold, B::AbstractBasis) + +Compute the number of coordinates in basis `B` of manifold `M`. +This also corresponds to the number of vectors represented by `B`, +or stored within `B` in case of a [`CachedBasis`](@ref). +""" +function number_of_coordinates(M::Manifold{𝔽}, B::AbstractBasis{𝔾}) where {𝔽,𝔾} + return div(manifold_dimension(M), real_dimension(𝔽)) * real_dimension(𝔾) +end +function number_of_coordinates(M::Manifold{𝔽}, B::AbstractBasis{𝔽}) where {𝔽} + return manifold_dimension(M) +end + """ number_system(::AbstractBasis) diff --git a/src/numbers.jl b/src/numbers.jl index 2acc663f..6179d5de 100644 --- a/src/numbers.jl +++ b/src/numbers.jl @@ -9,6 +9,7 @@ the fields [`RealNumbers`](@ref) (`ℝ` for short) and [`ComplexNumbers`](@ref) abstract type AbstractNumbers end """ + RealNumbers <: AbstractNumbers ℝ = RealNumbers() The field of real numbers. @@ -16,6 +17,7 @@ The field of real numbers. struct RealNumbers <: AbstractNumbers end """ + ComplexNumbers <: AbstractNumbers ℂ = ComplexNumbers() The field of complex numbers. @@ -23,6 +25,7 @@ The field of complex numbers. struct ComplexNumbers <: AbstractNumbers end """ + QuaternionNumbers <: AbstractNumbers ℍ = QuaternionNumbers() The division algebra of quaternions. @@ -33,6 +36,26 @@ const ℝ = RealNumbers() const ℂ = ComplexNumbers() const ℍ = QuaternionNumbers() +""" + _unify_number_systems(𝔽s::AbstractNumbers...) + +Compute a number system that includes all given number systems (as sub-systems) and is +closed under addition and multiplication. +""" +function _unify_number_systems(a::AbstractNumbers, rest::AbstractNumbers...) + return _unify_number_systems(a, _unify_number_systems(rest...)) +end +_unify_number_systems(𝔽::AbstractNumbers) = 𝔽 +_unify_number_systems(r::RealNumbers, ::RealNumbers) = r +_unify_number_systems(::RealNumbers, c::ComplexNumbers) = c +_unify_number_systems(::RealNumbers, q::QuaternionNumbers) = q +_unify_number_systems(c::ComplexNumbers, ::RealNumbers) = c +_unify_number_systems(c::ComplexNumbers, ::ComplexNumbers) = c +_unify_number_systems(::ComplexNumbers, q::QuaternionNumbers) = q +_unify_number_systems(q::QuaternionNumbers, ::RealNumbers) = q +_unify_number_systems(q::QuaternionNumbers, ::ComplexNumbers) = q +_unify_number_systems(q::QuaternionNumbers, ::QuaternionNumbers) = q + Base.show(io::IO, ::RealNumbers) = print(io, "ℝ") Base.show(io::IO, ::ComplexNumbers) = print(io, "ℂ") Base.show(io::IO, ::QuaternionNumbers) = print(io, "ℍ") @@ -40,7 +63,7 @@ Base.show(io::IO, ::QuaternionNumbers) = print(io, "ℍ") @doc raw""" real_dimension(𝔽::AbstractNumbers) -Return the real dimension $\dim_ℝ 𝔽$ of the [`AbstractNumbers`] system `𝔽`. +Return the real dimension $\dim_ℝ 𝔽$ of the [`AbstractNumbers`](@ref) system `𝔽`. The real dimension is the dimension of a real vector space with which a number in `𝔽` can be identified. For example, [`ComplexNumbers`](@ref) have a real dimension of 2, and diff --git a/test/bases.jl b/test/bases.jl index bf420f84..52617566 100644 --- a/test/bases.jl +++ b/test/bases.jl @@ -13,6 +13,12 @@ ManifoldsBase.manifold_dimension(::ProjManifold) = 5 ManifoldsBase.get_vector(::ProjManifold, x, v, ::DefaultOrthonormalBasis) = reverse(v) @testset "Dispatch" begin + @test ManifoldsBase.decorator_transparent_dispatch( + get_basis, + DefaultManifold(3), + [0.0, 0.0, 0.0], + DefaultBasis(), + ) === Val(:parent) @test ManifoldsBase.decorator_transparent_dispatch( get_coordinates, DefaultManifold(3), @@ -200,6 +206,7 @@ DiagonalizingBasisProxy() = DiagonalizingOrthonormalBasis([1.0, 0.0, 0.0]) continue end v1 = log(M, pts[1], pts[2]) + @test ManifoldsBase.number_of_coordinates(M, BT()) == 3 if BT != DiagonalizingBasisProxy vb = get_coordinates(M, pts[1], v1, BT()) @@ -259,15 +266,17 @@ end X = [1.2, 2.2im, 2.3im] b = [Matrix{Float64}(I,3,3)[:,i] for i=1:3] Bℝ = CachedBasis(DefaultOrthonormalBasis{ℝ}(),b) - aℝ = get_coordinates(M,p,X,Bℝ) - Yℝ = get_vector(M,p,aℝ,Bℝ) + aℝ = get_coordinates(M, p, X, Bℝ) + Yℝ = get_vector(M, p, aℝ, Bℝ) @test Yℝ ≈ X + @test ManifoldsBase.number_of_coordinates(M, Bℝ) == 3 bℂ = [b...,(b.*1im)...] Bℂ = CachedBasis(DefaultOrthonormalBasis{ℂ}(), bℂ) - aℂ = get_coordinates(M,p,X,Bℂ) - Yℂ = get_vector(M,p,aℂ,Bℂ) + aℂ = get_coordinates(M, p, X, Bℂ) + Yℂ = get_vector(M, p, aℂ, Bℂ) @test Yℂ ≈ X + @test ManifoldsBase.number_of_coordinates(M, Bℂ) == 6 end @testset "Basis show methods" begin diff --git a/test/numbers.jl b/test/numbers.jl index ee22607c..ff87ec10 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -1,6 +1,6 @@ using Test using ManifoldsBase -using ManifoldsBase: AbstractNumbers, ℝ, ℂ, ℍ +using ManifoldsBase: AbstractNumbers, ℝ, ℂ, ℍ, _unify_number_systems struct NotImplementedNumbers <: ManifoldsBase.AbstractNumbers end @@ -21,4 +21,19 @@ struct NotImplementedNumbers <: ManifoldsBase.AbstractNumbers end @test ManifoldsBase.QuaternionNumbers() === ℍ @test real_dimension(ℍ) == 4 @test repr(ℍ) == "ℍ" + + @test (@inferred _unify_number_systems(ℝ)) === ℝ + @test (@inferred _unify_number_systems(ℂ)) === ℂ + @test (@inferred _unify_number_systems(ℍ)) === ℍ + @test (@inferred _unify_number_systems(ℝ, ℝ)) === ℝ + @test (@inferred _unify_number_systems(ℝ, ℂ)) === ℂ + @test (@inferred _unify_number_systems(ℝ, ℍ)) === ℍ + @test (@inferred _unify_number_systems(ℂ, ℝ)) === ℂ + @test (@inferred _unify_number_systems(ℂ, ℂ)) === ℂ + @test (@inferred _unify_number_systems(ℂ, ℍ)) === ℍ + @test (@inferred _unify_number_systems(ℍ, ℝ)) === ℍ + @test (@inferred _unify_number_systems(ℍ, ℂ)) === ℍ + @test (@inferred _unify_number_systems(ℍ, ℍ)) === ℍ + @test (@inferred _unify_number_systems(ℝ, ℂ, ℝ)) === ℂ + @test (@inferred _unify_number_systems(ℝ, ℝ, ℝ)) === ℝ end diff --git a/test/runtests.jl b/test/runtests.jl index 077d3460..8dcd2605 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,10 @@ using Test using ManifoldsBase - @testset "ManifoldsBase" begin - # This should remain at 0 - @test length(Test.detect_ambiguities(ManifoldsBase)) == 0 + + num_ambiguities = length(Test.detect_ambiguities(ManifoldsBase)) + #num_ambiguities > 0 && @warn "The number of ambiguities in ManifoldsBase is $(num_ambiguities)." + @test num_ambiguities == 0 include("allocation.jl") include("numbers.jl") include("bases.jl") diff --git a/test/validation_manifold.jl b/test/validation_manifold.jl index 938514c4..5f2cfaf2 100644 --- a/test/validation_manifold.jl +++ b/test/validation_manifold.jl @@ -142,9 +142,9 @@ end @test_throws ErrorException get_basis(A, x, CachedBasis(cb, [x, x, x])) @test_throws ErrorException get_basis(A, x, CachedBasis(cb, [2*x, x, x])) if BT <: ManifoldsBase.AbstractOrthogonalBasis - @test_throws ErrorException get_basis(A, x, CachedBasis(cb, [[1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 0.0, 1.0]])) + @test_throws ArgumentError get_basis(A, x, CachedBasis(cb, [[1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 0.0, 1.0]])) elseif BT <: ManifoldsBase.AbstractOrthonormalBasis - @test_throws ErrorException get_basis(A, x, CachedBasis(cb, [[2.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])) + @test_throws ArgumentError get_basis(A, x, CachedBasis(cb, [[2.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]])) end end end