@@ -82,6 +82,26 @@ function Grassmann(n::Int, k::Int, field::AbstractNumbers = ℝ; parameter::Symb
8282 return Grassmann {field, typeof(size)} (size)
8383end
8484
85+ @doc raw """
86+ GrassmannAtlas()
87+
88+ The standard atlas of the real Grassmann manifold ``\m athrm{Gr}(n,k)``. Its
89+ ``\b inom{n}{k}`` charts are indexed by ordered tuples `i` of `k` row indices.
90+ The chart indexed by `i` contains the subspaces whose corresponding `k`-by-`k`
91+ minor in the rows `i` is invertible.
92+
93+ For a Stiefel representative `p`, let `pᵢ` denote this minor and let `j` be
94+ the complementary row indices. The coordinate map is
95+
96+ ````math
97+ \v arphi_i([p]) = \o peratorname{vec}\l eft((p p_i^{-1})_j\r ight).
98+ ````
99+
100+ Its inverse inserts the reshaped coordinates into the rows `j`, inserts the
101+ identity into the rows `i`, and orthonormalizes the resulting matrix.
102+ """
103+ struct GrassmannAtlas <: AbstractAtlas{ℝ} end
104+
85105function allocation_promotion_function (:: Grassmann{ℂ} , f, args:: Tuple )
86106 return complex
87107end
@@ -239,3 +259,100 @@ Return the default vector transport method for the [`Grassmann`](@ref) manifold,
239259which is `ParallelTransport``()`.
240260"""
241261default_vector_transport_method (:: Grassmann ) = ParallelTransport ()
262+
263+ function _grassmann_chart_rows (M:: Grassmann , i:: AbstractVector )
264+ n, k = get_parameter (M. size)
265+ rows = collect (i)
266+ if length (rows) != k || ! all (row -> 1 <= row <= n, rows) || length (unique (rows)) != k
267+ throw (ArgumentError (" A Grassmann chart index must contain $k distinct row indices in 1:$n ." ))
268+ end
269+ return rows
270+ end
271+
272+ function _grassmann_chart_complement (M:: Grassmann , i:: AbstractVector )
273+ n, _ = get_parameter (M. size)
274+ return setdiff (collect (1 : n), _grassmann_chart_rows (M, i))
275+ end
276+
277+ @doc raw """
278+ inner(M::Grassmann, A::GrassmannAtlas, i::AbstractVector, a, Xc, Yc)
279+
280+ Compute the Riemannian inner product of coordinate vectors `Xc` and `Yc` at
281+ coordinates `a` in the chart `i` of the standard [`GrassmannAtlas`](@ref).
282+ Writing `Z = reshape(a, n-k, k)`, `U = reshape(Xc, n-k, k)`,
283+ `V = reshape(Yc, n-k, k)`, and `G = I + ZᵀZ`, the formula is
284+
285+ ````math
286+ g_Z(U,V) = \o peratorname{tr}\l eft(G^{-1} U^\m athsf{T}
287+ \l eft(I - ZG^{-1}Z^\m athsf{T}\r ight)V\r ight).
288+ ````
289+ """
290+ function inner (M:: Grassmann{ℝ} , :: GrassmannAtlas , i:: AbstractVector , a, Xc, Yc)
291+ _grassmann_chart_rows (M, i)
292+ n, k = get_parameter (M. size)
293+ coordinate_dimension = k * (n - k)
294+ length (a) == coordinate_dimension || throw (DimensionMismatch (" Expected $coordinate_dimension chart coordinates." ))
295+ length (Xc) == coordinate_dimension || throw (DimensionMismatch (" Expected $coordinate_dimension vector coordinates." ))
296+ length (Yc) == coordinate_dimension || throw (DimensionMismatch (" Expected $coordinate_dimension vector coordinates." ))
297+ Z = reshape (a, n - k, k)
298+ U = reshape (Xc, n - k, k)
299+ V = reshape (Yc, n - k, k)
300+ G = I + transpose (Z) * Z
301+ return dot (U / G, V - Z * (G \ (transpose (Z) * V)))
302+ end
303+
304+ @doc raw """
305+ affine_connection!(M::Grassmann, Zc, A::GrassmannAtlas, i, a, Xc, Yc)
306+
307+ Store the Levi-Civita covariant derivative of `Yc` in direction `Xc` in
308+ `Zc`, using the standard [`GrassmannAtlas`](@ref). Writing
309+ `Z = reshape(a, n-k, k)`, `U = reshape(Xc, n-k, k)`,
310+ `V = reshape(Yc, n-k, k)`, and `G = I + ZᵀZ`, the coordinate expression is
311+
312+ ````math
313+ \n abla_U V = -UG^{-1}Z^\m athsf{T}V - VG^{-1}Z^\m athsf{T}U.
314+ ````
315+ """
316+ function affine_connection! (
317+ M:: Grassmann{ℝ} ,
318+ Zc,
319+ :: GrassmannAtlas ,
320+ i:: AbstractVector ,
321+ a,
322+ Xc,
323+ Yc,
324+ )
325+ _grassmann_chart_rows (M, i)
326+ n, k = get_parameter (M. size)
327+ coordinate_dimension = k * (n - k)
328+ length (a) == coordinate_dimension || throw (DimensionMismatch (" Expected $coordinate_dimension chart coordinates." ))
329+ length (Xc) == coordinate_dimension || throw (DimensionMismatch (" Expected $coordinate_dimension vector coordinates." ))
330+ length (Yc) == coordinate_dimension || throw (DimensionMismatch (" Expected $coordinate_dimension vector coordinates." ))
331+ length (Zc) == coordinate_dimension || throw (DimensionMismatch (" Expected $coordinate_dimension output coordinates." ))
332+ Z = reshape (a, n - k, k)
333+ U = reshape (Xc, n - k, k)
334+ V = reshape (Yc, n - k, k)
335+ G = I + transpose (Z) * Z
336+ Zc .= vec (- (U * (G \ (transpose (Z) * V)) + V * (G \ (transpose (Z) * U))))
337+ return Zc
338+ end
339+
340+ @doc raw """
341+ det_local_metric(M::Grassmann, A::GrassmannAtlas, i::AbstractVector, a)
342+
343+ Return the determinant of the local metric in the standard
344+ [`GrassmannAtlas`](@ref) at coordinates `a` in chart `i`.
345+ """
346+ function det_local_metric (M:: Grassmann{ℝ} , A:: GrassmannAtlas , i:: AbstractVector , a)
347+ return det (local_metric (M, A, i, a))
348+ end
349+
350+ @doc raw """
351+ inverse_chart_injectivity_radius(M::Grassmann, A::GrassmannAtlas, i)
352+
353+ Return the injectivity radius of an affine chart in the standard
354+ [`GrassmannAtlas`](@ref), which is infinite.
355+ """
356+ function inverse_chart_injectivity_radius (M:: Grassmann{ℝ} , :: GrassmannAtlas , i:: AbstractVector )
357+ return Inf
358+ end
0 commit comments