/
SymmetricPositiveDefinite.jl
176 lines (151 loc) · 5.69 KB
/
SymmetricPositiveDefinite.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
@doc raw"""
SymmetricPositiveDefinite{N} <: AbstractEmbeddedManifold{ℝ,DefaultEmbeddingType}
The manifold of symmetric positive definite matrices, i.e.
````math
\mathcal P(n) =
\bigl\{
p ∈ ℝ^{n × n}\ \big|\ a^\mathrm{T}pa > 0 \text{ for all } a ∈ ℝ^{n}\backslash\{0\}
\bigr\}
````
# Constructor
SymmetricPositiveDefinite(n)
generates the manifold $\mathcal P(n) \subset ℝ^{n × n}$
"""
struct SymmetricPositiveDefinite{N} <: AbstractEmbeddedManifold{ℝ,DefaultEmbeddingType} end
SymmetricPositiveDefinite(n::Int) = SymmetricPositiveDefinite{n}()
@doc raw"""
check_manifold_point(M::SymmetricPositiveDefinite, p; kwargs...)
checks, whether `p` is a valid point on the [`SymmetricPositiveDefinite`](@ref) `M`, i.e. is a matrix
of size `(N,N)`, symmetric and positive definite.
The tolerance for the second to last test can be set using the `kwargs...`.
"""
function check_manifold_point(M::SymmetricPositiveDefinite{N}, p; kwargs...) where {N}
mpv =
invoke(check_manifold_point, Tuple{supertype(typeof(M)),typeof(p)}, M, p; kwargs...)
mpv === nothing || return mpv
if !isapprox(norm(p - transpose(p)), 0.0; kwargs...)
return DomainError(
norm(p),
"The point $(p) does not lie on $(M) since its not a symmetric matrix:",
)
end
if !all(eigvals(p) .> 0)
return DomainError(
norm(p),
"The point $p does not lie on $(M) since its not a positive definite matrix.",
)
end
return nothing
end
"""
check_tangent_vector(M::SymmetricPositiveDefinite, p, X; check_base_point = true, kwargs... )
Check whether `X` is a tangent vector to `p` on the [`SymmetricPositiveDefinite`](@ref) `M`,
i.e. atfer [`check_manifold_point`](@ref)`(M,p)`, `X` has to be of same dimension as `p`
and a symmetric matrix, i.e. this stores tangent vetors as elements of the corresponding
Lie group.
The optional parameter `check_base_point` indicates, whether to call [`check_manifold_point`](@ref) for `p`.
The tolerance for the last test can be set using the `kwargs...`.
"""
function check_tangent_vector(
M::SymmetricPositiveDefinite{N},
p,
X;
check_base_point = true,
kwargs...,
) where {N}
if check_base_point
mpe = check_manifold_point(M, p; kwargs...)
mpe === nothing || return mpe
end
mpv = invoke(
check_tangent_vector,
Tuple{supertype(typeof(M)),typeof(p),typeof(X)},
M,
p,
X;
check_base_point = false, # already checked above
kwargs...,
)
mpv === nothing || return mpv
if !isapprox(norm(X - transpose(X)), 0.0; kwargs...)
return DomainError(
size(X),
"The vector $(X) is not a tangent to a point on $(M) (represented as an element of the Lie algebra) since its not symmetric.",
)
end
return nothing
end
function decorated_manifold(M::SymmetricPositiveDefinite)
return Euclidean(representation_size(M)...; field = ℝ)
end
embed!(M::SymmetricPositiveDefinite, q, p) = (q .= p)
embed!(M::SymmetricPositiveDefinite, Y, p, X) = (Y .= X)
@doc raw"""
injectivity_radius(M::SymmetricPositiveDefinite[, p])
injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LinearAffineMetric}[, p])
injectivity_radius(M::MetricManifold{SymmetricPositiveDefinite,LogCholeskyMetric}[, p])
Return the injectivity radius of the [`SymmetricPositiveDefinite`](@ref).
Since `M` is a Hadamard manifold with respect to the [`LinearAffineMetric`](@ref) and the
[`LogCholeskyMetric`](@ref), the injectivity radius is globally $∞$.
"""
injectivity_radius(::SymmetricPositiveDefinite) = Inf
injectivity_radius(::SymmetricPositiveDefinite, ::ExponentialRetraction) = Inf
injectivity_radius(::SymmetricPositiveDefinite, ::Any) = Inf
injectivity_radius(::SymmetricPositiveDefinite, ::Any, ::ExponentialRetraction) = Inf
eval(
quote
@invoke_maker 1 Manifold injectivity_radius(
M::SymmetricPositiveDefinite,
rm::AbstractRetractionMethod,
)
end,
)
@doc raw"""
manifold_dimension(M::SymmetricPositiveDefinite)
returns the dimension of
[`SymmetricPositiveDefinite`](@ref) `M`$=\mathcal P(n), n ∈ ℕ$, i.e.
````math
\dim \mathcal P(n) = \frac{n(n+1)}{2}.
````
"""
@generated function manifold_dimension(M::SymmetricPositiveDefinite{N}) where {N}
return div(N * (N + 1), 2)
end
"""
mean(
M::SymmetricPositiveDefinite,
x::AbstractVector,
[w::AbstractWeights,]
method = GeodesicInterpolation();
kwargs...,
)
Compute the Riemannian [`mean`](@ref mean(M::Manifold, args...)) of `x` using
[`GeodesicInterpolation`](@ref).
"""
mean(::SymmetricPositiveDefinite, ::Any)
function Statistics.mean!(
M::SymmetricPositiveDefinite,
p,
x::AbstractVector,
w::AbstractVector;
kwargs...,
)
return mean!(M, p, x, w, GeodesicInterpolation(); kwargs...)
end
@doc raw"""
representation_size(M::SymmetricPositiveDefinite)
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 Base.show(io::IO, ::SymmetricPositiveDefinite{N}) where {N}
return print(io, "SymmetricPositiveDefinite($(N))")
end
@doc raw"""
zero_tangent_vector(M::SymmetricPositiveDefinite,x)
returns the zero tangent vector in the tangent space of the symmetric positive
definite matrix `x` on the [`SymmetricPositiveDefinite`](@ref) manifold `M`.
"""
zero_tangent_vector(::SymmetricPositiveDefinite, ::Any)
zero_tangent_vector!(M::SymmetricPositiveDefinite{N}, v, x) where {N} = fill!(v, 0)