-
Notifications
You must be signed in to change notification settings - Fork 56
/
KendallsPreShapeSpace.jl
133 lines (107 loc) · 4.29 KB
/
KendallsPreShapeSpace.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@doc raw"""
KendallsPreShapeSpace{n,k} <: 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
is equal to 1 [Kendall:1984](@cite)[Kendall:1989](@cite).
The space can be interpreted as tuples of ``k`` points in ``ℝ^n`` up to simultaneous
translation and scaling of all points, so this can be thought of as a quotient manifold.
# Constructor
KendallsPreShapeSpace(n::Int, k::Int)
# See also
[`KendallsShapeSpace`](@ref), esp. for the references
"""
struct KendallsPreShapeSpace{n,k} <: AbstractSphere{ℝ} end
KendallsPreShapeSpace(n::Int, k::Int) = KendallsPreShapeSpace{n,k}()
function active_traits(f, ::KendallsPreShapeSpace, args...)
return merge_traits(IsEmbeddedSubmanifold())
end
representation_size(::KendallsPreShapeSpace{n,k}) where {n,k} = (n, k)
"""
check_point(M::KendallsPreShapeSpace, p; atol=sqrt(max_eps(X, Y)), kwargs...)
Check whether `p` is a valid point on [`KendallsPreShapeSpace`](@ref), i.e. whether
each row has zero mean. Other conditions are checked via embedding in [`ArraySphere`](@ref).
"""
function check_point(M::KendallsPreShapeSpace, p; atol=sqrt(eps(eltype(p))), kwargs...)
for p_row in eachrow(p)
if !isapprox(mean(p_row), 0; atol, kwargs...)
return DomainError(
mean(p_row),
"The point $(p) does not lie on the $(M) since one of the rows does not have zero mean.",
)
end
end
return nothing
end
"""
check_vector(M::KendallsPreShapeSpace, p, X; kwargs... )
Check whether `X` is a valid tangent vector on [`KendallsPreShapeSpace`](@ref), i.e. whether
each row has zero mean. Other conditions are checked via embedding in [`ArraySphere`](@ref).
"""
function check_vector(M::KendallsPreShapeSpace, p, X; atol=sqrt(eps(eltype(X))), kwargs...)
for X_row in eachrow(X)
if !isapprox(mean(X_row), 0; atol, kwargs...)
return DomainError(
mean(X_row),
"The vector $(X) is not a tangent vector to $(p) on $(M), since one of the rows does not have zero mean.",
)
end
end
return nothing
end
embed(::KendallsPreShapeSpace, p) = p
embed(::KendallsPreShapeSpace, p, X) = X
"""
get_embedding(M::KendallsPreShapeSpace)
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)
end
@doc raw"""
manifold_dimension(M::KendallsPreShapeSpace)
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
"""
project(M::KendallsPreShapeSpace, p)
Project point `p` from the embedding to [`KendallsPreShapeSpace`](@ref) by selecting
the right element from the orthogonal section representing the quotient manifold `M`.
See Section 3.7 of [SrivastavaKlassen:2016](@cite) for details.
The method computes the mean of the landmarks and moves them to make their mean zero;
afterwards the Frobenius norm of the landmarks (as a matrix) is normalised to fix the scaling.
"""
project(::KendallsPreShapeSpace, p)
function project!(::KendallsPreShapeSpace, q, p)
q .= p .- mean(p, dims=2)
q ./= norm(q)
return q
end
"""
project(M::KendallsPreShapeSpace, p, X)
Project tangent vector `X` at point `p` from the embedding to [`KendallsPreShapeSpace`](@ref)
by selecting the right element from the tangent space to orthogonal section representing the
quotient manifold `M`. See Section 3.7 of [SrivastavaKlassen:2016](@cite) for details.
"""
project(::KendallsPreShapeSpace, p, X)
function project!(::KendallsPreShapeSpace, Y, p, X)
Y .= X .- mean(X, dims=2)
Y .-= dot(p, Y) .* p
return Y
end
function Random.rand!(
rng::AbstractRNG,
M::KendallsPreShapeSpace,
pX;
vector_at=nothing,
σ=one(eltype(pX)),
)
if vector_at === nothing
project!(M, pX, randn(rng, representation_size(M)))
else
n = σ * randn(rng, size(pX)) # Gaussian in embedding
project!(M, pX, vector_at, n) #project to TpM (keeps Gaussianness)
end
return pX
end