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))