-
Notifications
You must be signed in to change notification settings - Fork 56
/
Metric.jl
314 lines (254 loc) · 10.9 KB
/
Metric.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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
@doc doc"""
Metric
Abstract type for the pseudo-Riemannian metric tensor $g$, a family of smoothly
varying inner products on the tangent space. See [`inner`](@ref).
"""
abstract type Metric end
@doc doc"""
RiemannianMetric <: Metric
Abstract type for Riemannian metrics, a family of positive definite inner
products. The positive definite property means that for $v \in T_x M$, the
inner product $g(v, v) > 0$ whenever $v$ is not the zero vector.
"""
abstract type RiemannianMetric <: Metric end
@doc doc"""
LorentzMetric <: Metric
Abstract type for Lorentz metrics, which have a single time dimension. These
metrics assume the spacelike convention with the time dimension being last,
giving the signature $(++...+-)$.
"""
abstract type LorentzMetric <: Metric end
"""
MetricManifold{M<:Manifold,G<:Metric} <: Manifold
Equip a [`Manifold`](@ref) explicitly with a [`Metric`](@ref) `G`.
For a Metric Manifold, by default, assumes, that you implement the linear form
from [`local_metric`](@ref) in order to evaluate the exponential map.
If the corresponding [`Metric`](@ref) `G` yields closed form formulae for e.g.
the exponential map and this is implemented directly (without solving the ode),
you of course can still implement that directly.
# Constructor
MetricManifold(manifold, metric)
"""
struct MetricManifold{M<:Manifold,G<:Metric} <: Manifold
manifold::M
metric::G
end
is_decorator_manifold(M::MetricManifold) = Val(true)
"""
is_default_metric(M,G)
indicate, whether the [`Metric`](@ref) `G` is the default metric for
the [`Manifold`](@ref) `M`. This means that any occurence of
[`MetricManifold`](@ref)(M,G) where `is_default_metric(M,G) = Val{true}`
falls back to just be called with `M` such that the [`manifold`](@ref) `M`
implicitly has this metric, for example if this was the first one implemented
or is the one most commonly assumed to be used.
"""
is_default_metric(M::Manifold,G::Metric) = Val(false)
# this automatically undecorates
convert(::Type{MT},M::MetricManifold{MT,GT}) where {MT <: Manifold,GT} = base_manifold(M)
# this should austomatically decorate at least for simple cases
convert(T::Type{MetricManifold{MT,GT}},M::MT) where {MT,GT} = _convert_with_default(T,M,is_default_metric(M,GT))
_convert_with_default(T::Type{Metric},M::Manifold,::Val{true}) where {MT <: Manifold} = MetricManifold(M,T())
_convert_with_default(T::Type{Metric},M::MT,::Val{false}) where {MT <: Manifold} = error("Can not convert $(M) to a MetricManifold{$(MT),$(T)}, since $(T) is not the default metric.")
@doc doc"""
metric(M::MetricManifold)
Get the metric $g$ of the manifold `M`.
"""
metric(M::MetricManifold) = M.metric
@doc doc"""
local_metric(M::MetricManifold, x)
Local matrix representation at the point `x` of the metric tensor $g$ on the
manifold `M`, usually written $g_{ij}$. The matrix has the property that
$g(v, w)=v^T [g_{ij}] w = g_{ij} v^i w^j$, where the latter expression uses
Einstein summation convention.
"""
function local_metric(M::MetricManifold, x)
error("Local metric not implemented on $(typeof(M)) for point $(typeof(x))")
end
@doc doc"""
inverse_local_metric(M::MetricManifold, x)
Local matrix representation of the inverse metric (cometric) tensor, usually
written $g^{ij}$
"""
inverse_local_metric(M::MetricManifold, x) = inv(local_metric(M, x))
@doc doc"""
det_local_metric(M::MetricManifold, x)
Determinant of local matrix representation of the metric tensor $g$
"""
det_local_metric(M::MetricManifold, x) = det(local_metric(M, x))
@doc doc"""
log_local_metric_density(M::MetricManifold, x)
Return the natural logarithm of the metric density $\rho$ of `M` at `x`, which
is given by $\rho=\log \sqrt{|\det [g_{ij}]|}$.
"""
log_local_metric_density(M::MetricManifold, x) = log(abs(det_local_metric(M, x))) / 2
@doc doc"""
local_metric_jacobian(M::MetricManifold, x)
Get partial derivatives of the local metric of `M` at `x` with respect to the
coordinates of `x`, $\frac{\partial}{\partial x^k} g_{ij} = g_{ij,k}$. The
dimensions of the resulting multi-dimensional array are ordered $(i,j,k)$.
"""
function local_metric_jacobian(M, x)
error("local_metric_jacobian not implemented on $(typeof(M)) for point $(typeof(x)). For a suitable default, enter `using ForwardDiff`.")
end
@doc doc"""
christoffel_symbols_first(M::MetricManifold, x)
Compute the Christoffel symbols of the first kind in local coordinates.
The Christoffel symbols are (in Einstein summation convention)
$\Gamma_{ijk} = \frac{1}{2} \left[g_{kj,i} + g_{ik,j} - g_{ij,k}\right],$
where $g_{ij,k}=\frac{\partial}{\partial x^k} g_{ij}$ is the coordinate
derivative of the local representation of the metric tensor. The dimensions of
the resulting multi-dimensional array are ordered $(i,j,k)$.
"""
function christoffel_symbols_first(M::MetricManifold, x)
∂g = local_metric_jacobian(M, x)
n = size(∂g, 1)
Γ = similar(∂g, Size(n, n, n))
@einsum Γ[i,j,k] = 1 / 2 * (∂g[k,j,i] + ∂g[i,k,j] - ∂g[i,j,k])
return Γ
end
@doc doc"""
christoffel_symbols_second(M::MetricManifold, x)
Compute the Christoffel symbols of the second kind in local coordinates.
The Christoffel symbols are (in Einstein summation convention)
$\Gamma^{l}_{ij} = g^{kl} \Gamma_{ijk},$
where $\Gamma_{ijk}$ are the Christoffel symbols of the first kind, and
$g^{kl}$ is the inverse of the local representation of the metric tensor.
The dimensions of the resulting multi-dimensional array are ordered $(l,i,j)$.
"""
function christoffel_symbols_second(M::MetricManifold, x)
ginv = inverse_local_metric(M, x)
Γ₁ = christoffel_symbols_first(M, x)
Γ₂ = similar(Γ₁)
@einsum Γ₂[l,i,j] = ginv[k,l] * Γ₁[i,j,k]
return Γ₂
end
@doc doc"""
christoffel_symbols_second_jacobian(M::MetricManifold, x)
Get partial derivatives of the Christoffel symbols of the second kind
for manifold `M` at `x` with respect to the coordinates of `x`,
$\frac{\partial}{\partial x^l} \Gamma^{k}_{ij} = \Gamma^{k}_{ij,l}.$
The dimensions of the resulting multi-dimensional array are ordered $(i,j,k,l)$.
"""
function christoffel_symbols_second_jacobian(M, x)
error("christoffel_symbols_second_jacobian not implemented on $(typeof(M)) for point $(typeof(x)). For a suitable default, enter `using ForwardDiff`.")
end
@doc doc"""
riemann_tensor(M::MetricManifold, x)
Compute the Riemann tensor $R^l_{ijk}$, also known as the Riemann curvature
tensor, at the point `x`. The dimensions of the resulting multi-dimensional
array are ordered $(l,i,j,k)$.
"""
function riemann_tensor(M::MetricManifold, x)
n = size(x, 1)
Γ = christoffel_symbols_second(M, x)
∂Γ = christoffel_symbols_second_jacobian(M, x) ./ n
R = similar(∂Γ, Size(n, n, n, n))
@einsum R[l,i,j,k] = ∂Γ[l,i,k,j] - ∂Γ[l,i,j,k] + Γ[s,i,k] * Γ[l,s,j] - Γ[s,i,j] * Γ[l,s,k]
return R
end
"""
ricci_tensor(M::MetricManifold, x)
Compute the Ricci tensor, also known as the Ricci curvature tensor,
of the manifold `M` at the point `x`.
"""
function ricci_tensor(M::MetricManifold, x)
R = riemann_tensor(M, x)
n = size(R, 1)
Ric = similar(R, Size(n, n))
@einsum Ric[i,j] = R[l,i,l,j]
return Ric
end
"""
ricci_curvature(M::MetricManifold, x)
Compute the Ricci scalar curvature of the manifold `M` at the point `x`.
"""
function ricci_curvature(M::MetricManifold, x)
ginv = inverse_local_metric(M, x)
Ric = ricci_tensor(M, x)
S = sum(ginv .* Ric)
return S
end
"""
gaussian_curvature(M::MetricManifold, x)
Compute the Gaussian curvature of the manifold `M` at the point `x`.
"""
gaussian_curvature(M::MetricManifold, x) = ricci_curvature(M, x) / 2
"""
einstein_tensor(M::MetricManifold, x)
Compute the Einstein tensor of the manifold `M` at the point `x`.
"""
function einstein_tensor(M::MetricManifold, x)
Ric = ricci_tensor(M, x)
g = local_metric(M, x)
ginv = inverse_local_metric(M, x)
S = sum(ginv .* Ric)
G = Ric - g .* S / 2
return G
end
@doc doc"""
solve_exp_ode(M::MetricManifold, x, v, tspan; solver=AutoVern9(Rodas5()), kwargs...)
Approximate the exponential map on the manifold over the provided timespan
assuming the Levi-Civita connection by solving the ordinary differential
equation
$\frac{d^2}{dt^2} x^k + \Gamma^k_{ij} \frac{d}{dt} x_i \frac{d}{dt} x_j = 0,$
where $\Gamma^k_{ij}$ are the Christoffel symbols of the second kind, and
the Einstein summation convention is assumed. The arguments `tspan` and
`solver` follow the `OrdinaryDiffEq` conventions. `kwargs...` specify keyword
arguments that will be passed to `OrdinaryDiffEq.solve`.
Currently, the numerical integration is only accurate when using a single
coordinate chart that covers the entire manifold. This excludes coordinates
in an embedded space.
"""
function solve_exp_ode(M, x, v, tspan; kwargs...)
error("solve_exp_ode not implemented on $(typeof(M)) for point $(typeof(x)), vector $(typeof(v)), and timespan $(typeof(tspan)). For a suitable default, enter `using OrdinaryDiffEq`.")
end
function exp(M::MMT, x, v, T::AbstractVector) where {MMT<:MetricManifold}
sol = solve_exp_ode(M, x, v, extrema(T); dense=false, saveat=T)
n = length(x)
return map(i -> sol.u[i][n+1:end], 1:length(T))
end
"""
exp(M::MetricManifold, x, v, args...)
Numerically integrate the exponential map assuming the Levi-Civita connection.
See [`solve_exp_ode`](@ref)
Currently, the numerical integration is only accurate when using a single
coordinate chart that covers the entire manifold. This excludes coordinates
in an embedded space.
"""
function exp!(M::MMT, y, x, v) where {MMT<:MetricManifold}
tspan = (0.0, 1.0)
sol = solve_exp_ode(M, x, v, tspan; dense=false, saveat=[1.0])
n = length(x)
y .= sol.u[1][n+1:end]
return y
end
inner(M::MMT, x, v, w) where {MMT<:MetricManifold} = dot(v, local_metric(M, x) * w)
#
function inner(B::VectorBundleFibers{<:CotangentSpaceType, MMT}, x, v, w) where {MMT<:MetricManifold}
ginv = inverse_local_metric(B.M, x)
return dot(v, ginv * w)
end
function flat!(M::MMT, v::FVector{CotangentSpaceType}, x, w::FVector{TangentSpaceType}) where {MMT<:MetricManifold}
g = local_metric(M, x)
copyto!(v.data, g*w.data)
return v
end
function sharp!(M::MMT, v::FVector{TangentSpaceType}, x, w::FVector{CotangentSpaceType}) where {MMT<:MetricManifold}
ginv = inverse_local_metric(M, x)
copyto!(v.data, ginv*w.data)
return v
end
# These are independent of the metric and hence can always fall back to M
function injectivity_radius(M::MMT, args...) where {MMT<:MetricManifold}
return injectivity_radius(base_manifold(M), args...)
end
function zero_tangent_vector!(M::MMT, v, x) where {MMT<:MetricManifold}
return zero_tangent_vector!(base_manifold(M), v, x)
end
function check_manifold_point(M::MMT, x; kwargs...) where {MMT<:MetricManifold}
return check_manifold_point(base_manifold(M), x; kwargs...)
end
function check_tangent_vector(M::MMT, x, v; kwargs...) where {MMT<:MetricManifold}
return check_tangent_vector(base_manifold(M), x, v; kwargs...)
end