Skip to content

Commit

Permalink
Change change_gradient to change_represender and introduce mutati…
Browse files Browse the repository at this point in the history
…ng variant
  • Loading branch information
kellertuer committed Sep 13, 2021
1 parent 5539f4b commit 2eac3ee
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 86 deletions.
3 changes: 3 additions & 0 deletions src/Manifolds.jl
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ export ×,
base_manifold,
bundle_projection,
change_metric,
change_metric!,
change_representer,
change_representer!,
check_point,
check_vector,
christoffel_symbols_first,
Expand Down
15 changes: 10 additions & 5 deletions src/manifolds/GeneralizedGrassmann.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ function GeneralizedGrassmann(
end

@doc raw"""
change_metric(M::GeneralizedGrassmann, ::EuclideanMetric, p, X)
change_representer(M::GeneralizedGrassmann, ::EuclideanMetric, p, X)
Change `X` to the corresponding representer of the gradient with respect to the scaled metric
of the [`GeneralizedGrassmann`](@ref) `M`, i.e. `M.B\X`.
"""
function change_gradient(M::GeneralizedGrassmann, ::EuclideanMetric, p, X)
return M.B \ X
change_representer(::GeneralizedGrassmann, ::EuclideanMetric, ::Any, ::Any)

function change_representer!(M::GeneralizedGrassmann, Y, ::EuclideanMetric, p, X)
return copyto!(M, Y, p, M.B \ X)
end

@doc raw"""
Expand All @@ -74,9 +76,12 @@ Change `X` to the corresponding vector with respect to the metric of the [`Gener
i.e. let ``B=LL'`` be the Cholesky decomposition of the matrix `M.B`, then the corresponding vector is ``L\X``.
"""
function change_metric(M::GeneralizedGrassmann, ::EuclideanMetric, p, X)
change_metric(M::GeneralizedGrassmann, ::EuclideanMetric, ::Any, ::Any)

function change_metric!(M::GeneralizedGrassmann, Y, ::EuclideanMetric, p, X)
C2 = cholesky(M.B).L
return C2 \ X
Y .= C2 \ X
return Y
end

@doc raw"""
Expand Down
8 changes: 5 additions & 3 deletions src/manifolds/HyperbolicHyperboloid.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
@doc raw"""
change_gradient(M::Hyperbolic, ::EuclideanMetric, p, X)
change_representer(M::Hyperbolic, ::EuclideanMetric, p, X)
Change a Eucliden gradient from the embedding that was already projected onto the tangent space at `p`.
We only have to correct for the metric, which means that the sign of the last entry changes.
"""
function change_gradient(::Hyperbolic, ::EuclideanMetric, p, X)
Y = copy(M, p, X)
change_representer(::Hyperbolic, ::EuclideanMetric, ::Any, ::Any)

function change_representer!(::Hyperbolic, Y, ::EuclideanMetric, p, X)
copyto!(M, Y, p, X)
Y[end] *= -1
return Y
end
Expand Down
25 changes: 19 additions & 6 deletions src/manifolds/HyperbolicPoincareBall.jl
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
@doc raw"""
change_gradient(M::Hyperbolic{n}, ::EuclideanMetric, p::PoincareBallPoint, X::PoincareBallTVector)
change_representer(M::Hyperbolic{n}, ::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 gradient reads `` Z = \frac{1}{α^2}X``.
"""
function change_gradient(
change_representer(
::Hyperbolic,
::EuclideanMetric,
::PoincareBallPoint,
::PoincareBallTVector,
)

function change_representer!(
::Hyperbolic,
Y::PoincareBallTVector,
::EuclideanMetric,
p::PoincareBallPoint,
X::PoincareBallTVector,
)
α = 2 / (1 - norm(p.value)^2)
return PoincareBallTVector(X.value ./ α^2)
Y.value .= X.value ./ α^2
return Y
end

@doc raw"""
change_metric(M::Hyperbolic{n}, ::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``.
the correction for the metric reads ``Z = \frac{1}{α}X``.
"""
function change_metric(
change_metric(::Hyperbolic, ::EuclideanMetric, ::PoincareBallPoint, ::PoincareBallTVector)

function change_metric!(
::Hyperbolic,
Y::PoincareBallTVector,
::EuclideanMetric,
p::PoincareBallPoint,
X::PoincareBallTVector,
)
α = 2 / (1 - norm(p.value)^2)
return PoincareBallTVector(X.value ./ α)
Y.value .= X.value ./ α
return Y
end

function check_point(M::Hyperbolic{N}, p::PoincareBallPoint; kwargs...) where {N}
Expand Down
81 changes: 52 additions & 29 deletions src/manifolds/MetricManifold.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,11 @@ inner product ``g(X, X) > 0`` whenever ``X`` is not the zero vector.
abstract type RiemannianMetric <: AbstractMetric end

@doc raw"""
change_gradient(M::AbstractManifold, G2::AbstractMetric, p, X)
change_representer(M::AbstractManifold, G2::AbstractMetric, p, X)
Convert the gradient `X` at `p` on the[`AbstractManifold`](@ref) `M` from one metric to another.
Assume that for a real-valued function ``f: \mathcal M \to ℝ`` the input `X` is the gradient or in
other words the [Riesz representer](https://en.wikipedia.org/wiki/Riesz_representation_theorem#Riesz_representation_theorem) of the differential ``Df(p)``` with respect to the metric ``g_2`` i.e.
```math
g_2(X,Y) = Df(p)[Y] \quad \text{for all } Y ∈ T_p\mathcal M.
```
(It could actually also be the Riesz representer of _any_ linear function, not just ``Df(p)``).
Convert the representer `X` of a linear function (in other words a cotangent vector at `p`)
in the tangent space at `p` on the[`AbstractManifold`](@ref) `M` given with respect to the
[`AbstractMetric`](@ref) `G2` into the representer with respect to the (implicit) metric of `M`.
In order to convert this into the gradient with respect to the (implicitly given) metric ``g_1`` of `M`,
we have to find the conversion function ``c: T_p\mathcal M \to \T_p\mathcal M`` such that
Expand All @@ -80,31 +73,50 @@ the same basis of the tangent space, the equation reads
x^{\mathrm{H}}G_2y = c(x)^{\mathrm{H}}G_1 y \quad \text{for all } y \in ℝ^d,
```
where `\cdot^{\mathrm{H}}`` denotes the conjugate transpose.
We obtain ``c(X) = (G_1\backslash G_2)^{\mathrm{H}X``
For example `X` could be the gradient ``\operatorname{grad}f`` of a real-valued function
``f: \mathcal M \to ℝ``, i.e.
```math
g_2(X,Y) = Df(p)[Y] \quad \text{for all } Y ∈ T_p\mathcal M.
```
and we obtain ``c(X) = (G_1\backslash G_2)^{\mathrm{H}X``
and we would change the Riesz representer of the gradient to the representer with respect to the metric ``g_1``.
# Examples
change_gradient(Sphere(2), EuclideanMetric(), p, X)
change_representer(Sphere(2), EuclideanMetric(), p, X)
Since the metric in ``T_p\mathbb S^2`` is the Euclidean metric from the embedding restricted to ``T_p\mathbb S^2``, this just returns `X`
change_gradient(SymmetricPOsitiveDefinite(3), EuclideanMetric(), p, X)
change_representer(SymmetricPositiveDefinite(3), EuclideanMetric(), p, X)
Here, the default metric in `\mathcal P(3)` is the [`LinearAffineMetric`](@ref) and the transformation can be computed as ``pXp``
Here, the default metric in ``\mathcal P(3)`` is the [`LinearAffineMetric`](@ref) and the transformation can be computed as ``pXp``
"""
change_gradient(::AbstractManifold, ::AbstractMetric, ::Any, ::Any)
change_representer(::AbstractManifold, ::AbstractMetric, ::Any, ::Any)

function change_gradient(M::AbstractManifold, G::AbstractMetric, p, X)
Y = allocate_result(M, change_metric, X, p) # this way we allocate a tangent
change_gradient!(M, G, Y, p, X)
function change_representer(M::AbstractManifold, G::AbstractMetric, p, X)
Y = allocate_result(M, change_representer, X, p) # this way we allocate a tangent
return change_representer!(M, G, Y, p, X)
end

@decorator_transparent_signature change_gradient(M::AbstractDecoratorManifold, G::AbstractMetric, X, p)
@decorator_transparent_signature change_gradient!(M::AbstractDecoratorManifold, Y, G::AbstractMetric, X, p)

@decorator_transparent_signature change_representer(
M::AbstractDecoratorManifold,
G::AbstractMetric,
X,
p,
)
@decorator_transparent_signature change_representer!(
M::AbstractDecoratorManifold,
Y,
G::AbstractMetric,
X,
p,
)

function change_gradient(M::AbstractManifold, G::AbstractMetric, p, X)
# Default fallback I: compute in local metric representations
function change_representer!(M::AbstractManifold, Y, G::AbstractMetric, p, X)
is_default_metric(M, G) && return copyto!(M, Y, p, X)
# TODO: For local metric, inverse_local metric, det_local_metric: Introduce a default basis?
B = DefaultOrthogonalBasis()
Expand All @@ -115,7 +127,8 @@ function change_gradient(M::AbstractManifold, G::AbstractMetric, p, X)
return get_vector!(M, Y, p, z, B)
end

function change_gradient!(
# Default fallback II: Identity if the metric is the same
function change_representer!(
::MetricManifold{𝔽,M,G},
Y,
::G,
Expand Down Expand Up @@ -151,14 +164,13 @@ Since the metric in ``T_p\mathbb S^2`` is the Euclidean metric from the embeddin
change_metric(SymmetricPOsitiveDefinite(3), EuclideanMetric, p, X)
Here, the default metric in `\mathcal P(3)` is the [`LinearAffineMetric`](@ref) and the transformation can be computed as ``B=p``
Here, the default metric in ``\mathcal P(3)`` is the [`LinearAffineMetric`](@ref) and the transformation can be computed as ``B=p``
"""
change_metric(::AbstractManifold, ::AbstractMetric, ::Any, ::Any)

function change_metric(M::AbstractManifold, G::AbstractMetric, p, X)
Y = allocate_result(M, change_metric, X, p) # this way we allocate a tangent
change_metric!(M, G, Y, p, X)
return change_metric!(M, G, Y, p, X)
end
function change_metric!(M::AbstractManifold, Y, G::AbstractMetric, p, X)
is_default_metric(M, G) && return copyto!(M, Y, p, X)
Expand All @@ -173,8 +185,19 @@ function change_metric!(M::AbstractManifold, Y, G::AbstractMetric, p, X)
return get_vector!(M, Y, p, z, B)
end

@decorator_transparent_signature change_metric(M::AbstractDecoratorManifold, G::AbstractMetric, X, p)
@decorator_transparent_signature change_metric!(M::AbstractDecoratorManifold, Y, G::AbstractMetric, X, p)
@decorator_transparent_signature change_metric(
M::AbstractDecoratorManifold,
G::AbstractMetric,
X,
p,
)
@decorator_transparent_signature change_metric!(
M::AbstractDecoratorManifold,
Y,
G::AbstractMetric,
X,
p,
)

function change_metric!(
::MetricManifold{<:M,<:G},
Expand Down
15 changes: 13 additions & 2 deletions src/manifolds/MultinomialDoublyStochastic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,24 @@ function MultinomialDoubleStochastic(n::Int)
end

"""
change_gradient(M::AbstractMultinomialDoublyStochastic, ::EuclideanMetric, p, X)
change_representer(M::AbstractMultinomialDoublyStochastic, ::EuclideanMetric, p, X)
Given a tangent vector with respect to the metric from the embedding, the [`EuclideanMetric`](@ref),
the representer of a linear functional on the tangent space is adapted as ``Z = p .* X``, since
this “compensates” for the divsion by ``p`` in the Riemannian metric on the [`ProbabilitySimplex`](@ref)
"""
change_gradient(::AbstractMultinomialDoublyStochastic, ::EuclideanMetric, p, X) = p .* X
change_representer(::AbstractMultinomialDoublyStochastic, ::EuclideanMetric, ::Any, ::Any)

function change_representer!(
::AbstractMultinomialDoublyStochastic,
Y,
::EuclideanMetric,
p,
X,
)
Y .= p .* X
return Y
end

@doc raw"""
check_point(M::MultinomialDoubleStochastic, p)
Expand Down
31 changes: 22 additions & 9 deletions src/manifolds/PositiveNumbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,35 @@ function check_point(M::PositiveNumbers, p; kwargs...)
end

@doc raw"""
change_metric(M::PositiveNumbers, E::EuclideanMetric, p, X)
change_representer(M::PositiveNumbers, E::EuclideanMetric, p, X)
Given a tangent vector ``X ∈ T_p\mathcal M``representing a gradient with respect to the [`EuclideanMetric`](@ref) `g_E`,
this function changes into the positivity metric representation of
[`PositiveNumbers`](@ref) `M` for the same gradient.
Given a tangent vector ``X ∈ T_p\mathcal M`` representing a linear function with respect
to the [`EuclideanMetric`](@ref) `g_E`, this function changes the representer into the one
with respect to the positivity metric representation of
[`PositiveNumbers`](@ref) `M`. It computes ``pX``.
"""
change_gradient(::PositiveNumbers, ::EuclideanMetric, p, X) = p .* X .* p
change_representer(::PositiveNumbers, ::EuclideanMetric, ::Any, ::Any)
change_representer(::PositiveNumbers, p::Real, X::Real) = p * X

function change_representer!(::PositiveNumbers, Y, ::EuclideanMetric, p, X)
Y .= p .* X .* p
return Y
end

@doc raw"""
change_metric(M::SymmetricPOsitiveDefinite, E::EuclideanMetric, p, X)
change_metric(M::SymmetricPositiveDefinite, E::EuclideanMetric, p, X)
Given a tangent vector ``X ∈ T_p\mathcal M`` with respect to the [`EuclideanMetric`](@ref) `g_E`,
this function changes into the positivity metric of [`PositiveNumbers`](@ref) `M` for the same gradient.
Given a tangent vector ``X ∈ T_p\mathcal M`` representing a linear function with respect to
the [`EuclideanMetric`](@ref) `g_E`,
this function changes the representer into the one with respect to the positivity metric
of [`PositiveNumbers`](@ref) `M`.
"""
change_metric(::PositiveNumbers, ::EuclideanMetric, p, X) = p .* X
change_metric(::PositiveNumbers, ::EuclideanMetric, ::Any, ::Any)

function change_metric!(::PositiveNumbers, Y, ::EuclideanMetric, p, X)
Y .= p .* X
return Y
end
"""
check_vector(M::PositiveNumbers, p, X; kwargs...)
Expand Down
30 changes: 23 additions & 7 deletions src/manifolds/PowerManifold.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,22 @@ end
default_metric_dispatch(::AbstractPowerManifold, ::PowerMetric) = Val(true)

"""
change_gradient(M::AbstractPowerManifold, ::AbstractMetric, p, X)
change_representer(M::AbstractPowerManifold, ::AbstractMetric, p, X)
Since the metric on a power manifold decouples, the change of a representer can be done elementwise
"""
function change_gradient(M::AbstractPowerManifold, G::AbstractMetric, p, X)
Z = copy(M, p, X)
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)
Z[i...] = change_gradient(M.manifold, G, p[i...], X[i...])
change_representer!(
M.manifold,
_write(M, rep_size, Y, i),
G,
_read(M, rep_size, P, i),
_read(M, rep_size, X, i),
)
end
end

Expand All @@ -95,10 +103,18 @@ end
Since the metric on a power manifold decouples, the change of a representer can be done elementwise
"""
function change_metric(::AbstractPowerManifold, M::AbstractMetric, p, X)
Z = copy(M, p, X)
change_metric(M::AbstractPowerManifold, ::AbstractMetric, ::Any, ::Any)

function change_metric!(M::AbstractPowerManifold, Y, ::AbstractMetric, p, X)
rep_size = representation_size(M.manifold)
for i in get_iterator(M)
Z[i...] = change_gradient(M.manifold, G, p[i...], X[i...])
change_metric!(
M.manifold,
_write(M, rep_size, Y, i),
G,
_read(M, rep_size, p, i),
_read(M, rep_size, X, i),
)
end
end

Expand Down
8 changes: 6 additions & 2 deletions src/manifolds/ProbabilitySimplex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,17 @@ See for example the [`ProbabilitySimplex`](@ref).
struct FisherRaoMetric <: AbstractMetric end

"""
change_gradient(M::ProbabilitySimplex, ::EuclideanMetric, ü, X)
change_representer(M::ProbabilitySimplex, ::EuclideanMetric, ü, X)
Given a tangent vector with respect to the metric from the embedding, the [`EuclideanMetric`](@ref),
the representer of a linear functional on the tangent space is adapted as ``Z = p .* X``, since
this “compensates” for the divsion by ``p`` in the Riemannian metric on the [`ProbabilitySimplex`](@ref)
"""
change_gradient(::ProbabilitySimplex, ::EuclideanMetric, p, X) = p .* X
change_representer(::ProbabilitySimplex, ::EuclideanMetric, ::Any, ::Any)

function change_representer!(::ProbabilitySimplex, Y, ::EuclideanMetric, p, X)
return Y .= p .* X
end

"""
check_point(M::ProbabilitySimplex, p; kwargs...)
Expand Down
Loading

0 comments on commit 2eac3ee

Please sign in to comment.