diff --git a/NEWS.md b/NEWS.md index 863c172f4..6da59935d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,7 +5,7 @@ All notable Changes to the Julia package `LieGroups.jl` will be documented in th The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.1.0] – unreleased +## [0.1.0] unreleased Everything denoted by “formerly” refers to the previous name in [`Manifolds.jl`](https://juliamanifolds.github.io/Manifolds.jl/stable/). diff --git a/docs/CondaPkg.toml b/docs/CondaPkg.toml new file mode 100644 index 000000000..8adcf64fb --- /dev/null +++ b/docs/CondaPkg.toml @@ -0,0 +1,3 @@ +[deps] +jupyter = "" +python = "3.11" \ No newline at end of file diff --git a/docs/make.jl b/docs/make.jl index 88e13d95c..17072cde8 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -93,6 +93,9 @@ if run_quarto || run_on_CI run(`quarto render $(tutorials_folder)`) return nothing end +else + # fallback to at least create empty files for tutorials that are directly linked from the docs + touch(joinpath(@__DIR__, "src/tutorials/getstarted.md")) end # (d) load necessary packages for the docs diff --git a/docs/src/index.md b/docs/src/index.md index 75bb460f0..a465fecfb 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -8,4 +8,47 @@ CurrentModule = LieGroups ```@docs LieGroups.LieGroups -``` \ No newline at end of file +``` + +The implemented [Lie groups](https://en.wikipedia.org/wiki/Lie_group) are implemented using the interface for manifolds in [`ManifoldsBase.jl`](@extref ManifoldsBase :doc:`index`) together with additional interfaces [for Lie groups](interface/group.md) and [Lie algebras](interface/algebra.md) as well as internally using the manifolds implemented in [`Manifolds.jl`](@extref Manifolds :doc:`index`). + +For more general information about the history of and contributions to the package see the [About](about.md) page. + +## Getting started + +To install the package just type + +```julia +using Pkg; Pkg.add("LieGroups") +``` + +Then you can directly start, for example consider the [`SpecialEuclideanGroup`](@ref) +``\mathrm{SE}(3)`` representing all orientations and places an object can take in ``ℝ^3``. +These are characterised by a ``3×3`` rotation matrix together with a point the object is at. +For example. +having such a point, we can use the Lie group logarithmic function [`log(G::SpecialEuclideanGroup, g)`](@ref) +and the Lie group exponential function [`exp(G::SpecialEuclideanGroup, X)`](@ref) +to create an orientation “half the way” from the origin pose. + +The default representation is in [homogeneous coordinates]() + +```@example start +using LieGroups +SE3 = SpecialEuclideanGroup(3) +g = 1/sqrt(2) .* [1.0 -1.0 0.0 0.0; 1.0 1.0 0.0 3.0*sqrt(2); 0.0 0.0 sqrt(2) 0.0; 0.0 0.0 0.0 sqrt(2)] +``` + +Then half that pose is + +```@example start +h = exp(SE3, 0.5 .* log(SE3, g)) +``` + +To check, just “perform that movement” twice with the group operation +[`compose`](@ref) of `h` with itself to get `g` back + +```@example start +compose(SE3, h, h) +``` + +for more details see the [get started](tutorials/getstarted.md) tutorial. \ No newline at end of file diff --git a/docs/src/references.bib b/docs/src/references.bib index dfee30241..9013e35ff 100644 --- a/docs/src/references.bib +++ b/docs/src/references.bib @@ -2,6 +2,7 @@ # # # A +# ------------------------------------------------------------------------------------------ @article{AndricaRohan:2013, AUTHOR = {Andrica, D. and Rohan, R.-A.}, ISSUE = {2}, @@ -28,9 +29,7 @@ @book{BinzPods:2008 # # # G -# -# G -# ---------------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------------------ @article{GallierXu:2002, AUTHOR = {Gallier, J and Xu, D}, ISSUE = {4}, @@ -58,6 +57,7 @@ @inproceedings{Giles:2008 # # # H +# ------------------------------------------------------------------------------------------ @book{HilgertNeeb:2012, AUTHOR = {Hilgert, Joachim and Neeb, Karl-Hermann}, DOI = {10.1007/978-0-387-84794-8}, @@ -67,6 +67,22 @@ @book{HilgertNeeb:2012 YEAR = {2012} } +# +# +# L +# ------------------------------------------------------------------------------------------ +@article{LatifiToomanian:2013, + AUTHOR = {Latifi, Dariush and Toomanian, Megerdich}, + DOI = {10.1186/2251-7456-7-37}, + ISSN = {2251-7456}, + JOURNAL = {Mathematical Sciences}, + MONTH = aug, + NUMBER = {1}, + PAGES = {37}, + TITLE = {On the existence of bi-invariant {Finsler} metrics on {Lie} groups}, + VOLUME = {7}, + YEAR = {2013} +} # # # S diff --git a/docs/src/tutorials/transition.md b/docs/src/tutorials/transition.md index 972d481ca..2f0e4de73 100644 --- a/docs/src/tutorials/transition.md +++ b/docs/src/tutorials/transition.md @@ -31,7 +31,7 @@ The list is alphabetical, but first lists types, then functions | `SemidirectProductGroup(G, H, a)` | [`LeftSemidirectProductLieGroup`](@ref)`(G, H, a)` | While this staid the same, there is now also the [`default_left_action`](@ref)`(G,H)`. When this agrees with `a` you can use the short hand `G⋉H` to generate this semidirect product. Analogously there now also exists the [`RightSemidirectProductLieGroup`](@ref)`(G,H)` with[`default_left_action`](@ref)`(G,H)` that allows for the short cut `G⋊H` | | `RightBackwardAction` | [`RightGroupOperationAction`](@ref) | | | `RightForwardAction` | [`InverseLeftGroupOperationAction`](@ref) | note that this is an [`AbstractRightGroupActionType`](@ref) | -| `adjoint` | [`adjoint`](@ref) | now implemented with a default, when you provide [`diff_conjugate!`](@ref). +| `adjoint_action` | [`adjoint`](@ref) | now implemented with a default, when you provide [`diff_conjugate!`](@ref). | `apply_diff` | [`diff_apply`](@ref) | modifiers (diff) come first, consistent with [`ManifoldsDiff.jl`](https://juliamanifolds.github.io/ManifoldDiff.jl/stable/) | | `apply_diff_group` | [`diff_group_apply`](@ref) | modifiers (diff/group) come first, consistent with [`ManifoldsDiff.jl`](https://juliamanifolds.github.io/ManifoldDiff.jl/stable/) | | | [`conjugate`](@ref), [`diff_conjugate`](@ref) | a new function to model ``c_g: \mathcal G → \mathcal G`` given by ``c_g(h) = g∘h∘g^{-1}`` | diff --git a/src/interface.jl b/src/interface.jl index cd5108743..1a16c2d0c 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -394,7 +394,7 @@ _doc_exponential = """ exp(G::AbstractLieGroup, X::T) exp!(G::AbstractLieGroup, g, X) - Compute the (Lie group) exponential function +Compute the (Lie group) exponential function ```math $(_tex(:exp))_{$(_math(:G))}: $(_math(:𝔤)) → $(_math(:G)),$(_tex(:qquad)) $(_tex(:exp))_{$(_math(:G))}(X) = γ_X(1), diff --git a/tutorials/Project.toml b/tutorials/Project.toml index 076550451..c8a021509 100644 --- a/tutorials/Project.toml +++ b/tutorials/Project.toml @@ -1,3 +1,13 @@ [deps] IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" LieGroups = "6774de46-80ba-43f8-ba42-e41071ccfc5f" +Manifolds = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e" +RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" +Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc" + +[compat] +IJulia = "1.26" +LieGroups = "0.0.3" +Manifolds = "0.10.14" +RecursiveArrayTools = "3.31" +Rotations = "1.7" diff --git a/tutorials/getstarted.qmd b/tutorials/getstarted.qmd index 4ec4eb8a3..de2aecbcf 100644 --- a/tutorials/getstarted.qmd +++ b/tutorials/getstarted.qmd @@ -1,3 +1,324 @@ # 🚀 Get Started with LieGroups.jl -In this tutorial, we aim to present how to get started with `LieGroups.jl`. \ No newline at end of file +## Introduction + +This tutorial introduces both a few basics of [Lie groups](https://en.wikipedia.org/wiki/Lie_group) as well as how to use [LieGroups.jl](https://juliamanifolds.github.io/LieGroups.jl/stable/). The Lie groups we consider are the rotations in the plane, +or [special orthogonal group](../groups/special_orthogonal_group.md) as well as rigid body motions, +or in other words the [special euclidean group](../groups/special_euclidean_group.md). + +In a nutshell a Lie group $𝒢$ is a [manifold](https://en.wikipedia.org/wiki/Manifold) $ℳ$ that also is a group with a group operation. + +A manifold can informally be described as a set that “locally looks like a Euclidean space” +and has some way to measure angles at every point. +Formally, this yields an inner product that depends (smoothly) on the point one is at. +For Lie groups this will be a bit easier. + +A [group](https://en.wikipedia.org/wiki/Group_(mathematics)) means that on the set there also +exists an operation, within this package usually denoted by $∘: 𝒢 × 𝒢 → 𝒢$, +`LieGroups.jl` uses the manifolds defined in [`Manifolds.jl`](https://juliamanifolds.github.io/Manifolds.jl/stable/). For more details on that, see also the [introductory tutorial](https://juliamanifolds.github.io/Manifolds.jl/stable/tutorials/getstarted/) there. +A Lie group is usually written as a tuple $(ℳ,∘)$ with the manifold and the group operation thereon. + +For more theoretical background, see for example [HilgertNeeb:2012](@cite), especially their Chapter 9. + +## Rotations on the plane $ℝ^2$ and in $ℝ^3$. + +This first part considers rotations in the plane. These can be represented by rotation matrices +```math +R_α = \begin{pmatrix} \cos(α)& -\sin(α)\\ \sin(α) & \cos(α) \end{pmatrix},\qquad α ∈ ℝ, +``` + +where we already represent all possible rotations when restricting $α$ to $[0,2π]$. +Adding two matrices $R_α + R_β$ does not yield a valid rotation matrix again, but we +can have these matrices in mind as being “locally like the real line”, since changing the rotation angle +slightly yields rotation matrices that are “close by” in the sense that they perform nearly the same rotation +when applied to a point $x ∈ ℝ^2$ by computing $R_αx$. +Matrix multiplication $R_β = R_{α_1} R_{α_2}$ does yield a new rotation and also fulfills all other +properties to yield a group operation. +Using trigonometric identities we observe that $R_β = R_{α_1+α_2}$. +Since the angle of rotation is $2π$-periodic, we also see here, that e.g. for small angles around $0$, +this behaves like the real line, but globally it differs, since $R_{α} = R_{α+2π}$ + + + +The set of rotation matrices is hence a Lie group and called the [special orthogonal group](../groups/special_orthogonal_group.md) $\mathrm{SO}(2)$. In `LieGroups.jl` we initialise this as + +```{julia} +using LieGroups, LinearAlgebra, RecursiveArrayTools, Rotations +SO2 = SpecialOrthogonalGroup(2) +``` + +Elements $g, h ∈ \mathrm{SO}(2)$ we generate as + +```{julia} +g = [1.0 0.0; 0.0 1.0] +h = 1/sqrt(2) .* [1.0 -1.0; 1.0 1.0] +``` + +A first thing to do is to use [`is_point`](@ref) to verify they are valid. + +```{julia} +(is_point(SO2, g), is_point(SO2, h)) +``` + +The already mentioned group operation as the matrix multiplication, there is also a generic +function available, [`compose`](@ref) as well as its in-place variant [`compose!`](@ref). +The following two yield the same matrix + +```{julia} +[ compose(SO2, g, h), g*h ] +``` + +Furthermore a lot of of functions are “passed down” tp the manifold, which is stored within +the [`LieGroup`](@ref). For example the dimension of the manifold, or the number of degrees of freedom +can be accessed via the ``[`manifold_dimension`](@extref `ManifoldsBase.manifold_dimension-Tuple{AbstractManifold}`)``{=commonmark} + +```{julia} +manifold_dimension(SO2) +``` + +## The Lie algebra, and the Lie group exponential function. + +For the following investigations, we consider the special orthogonal group $\mathrm{SO}(3)$, +that is rotations in a 3-dimensional space. +Besides one special rotation in the following code for completeness, a prominent, at first glace +maybe a bit “dull” point on this Lie group is the identity matrix `e`. + +```{julia} +SO3 = SpecialOrthogonalGroup(3) +g = RotZ(π/3)*RotY(π/4) +e = Matrix{Float64}(I,3,3) +g +``` +The element `g` can be seen as a rotation by $π/3$ in the x-y-plane combined with a rotation by $π/4$ in the x-z plane. + +The tangent space $T_e𝒢$ plays a special role and is called the [`LieAlgebra`](@ref) $𝔤$. + +Similar to the ``[Riemannian exponential map](@extref `Base.exp-Tuple{AbstractManifold, Any, Any}`)``{=commonmark} +The exponential function $\exp_𝒢: 𝔤 → 𝒢$ maps Lie algebra tangent vectors $X ∈ T_e𝒢$ +to a point on the Lie group. This is implemented in [`exp(G::LieGroup, X)`](@ref). Its inverse +is the Lie group logarithmic function [`log(G::LieGroup, g)`](@ref). + +```{julia} +X = log(SO3,g) +is_point(LieAlgebra(SO3), X) +``` + +```{julia} +exp(SO3,X) +``` + +The term “exponential” has at least three different meanings throughout Lie groups and Riemannian manifolds. +To be precise, we call the just introduced one “exponential function”, since it often agrees with the [matrix exponential](https://en.wikipedia.org/wiki/Matrix_exponential). + +Taking a closed look at `X` we see + +```{julia} +X +``` + +an example that all elements from the Lie algebra are skew symmetric matrices. +This allows for one way to see that we have three degrees of freedom, cf + +```{julia} +manifold_dimension(SO3) +``` + +As the Lie algebra was introduced via a tangent space, we also know that it is a vector space. +To “turn” the `X` into a vector with three elements, we need a basis of the tangent space +to decompose `X` into and “read off” its components. +While in general this can be done using the [bases of a tangent space](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases/) from [`ManifoldsBase.jl`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/), +a very specific one is [`vee(::LieAlgebra, X)`](@ref) + +```{julia} +c = vee(LieAlgebra(SO3), X) +``` + +and its inverse, [`hat(G::LieAlgebra, c)`](@ref) + +```{julia} +X_ = hat(LieAlgebra(SO3),c) +``` + +## Representing tangent vectors + +Consider the function of left group composition $λ_g(h) = g∘h$. +Its ``[differential](@ref diff_left_compose(G::AbstractLieGroup, g, h, X))``{=commonmark} (or push forward) $Dλ_g(e)$ maps from $T_e𝒢$ to $T_g𝒢$ +and is a linear bijection between both spaces. +Its adjoint differential (or pullback) $D^*λ_g(e)$ maps back from $T_g𝒢$ to $T_e𝒢$. + +This allows to introduce an inner product on all tangent spaces, that smoothly varies with +the base point, we obtain the left-invariant metric + +```math +⟨X,Y⟩_g = ⟨D^*λ_g(e)[X],D^*λ_g(e)⟩_e, +``` + +since it is invariant if you use the differential of the left composition to identify tangent vectors. +We can even “spare” the pullbacks in this definition, when representing tangent vector alsways by their correspnding representants in the Lie algebra. +This is the default in `LieGroups.jl`. + +Alternatively one can start with the right composition and its ``[differential](@ref diff_right_compose(G::AbstractLieGroup, h, g, X))``{=commonmark} to arrive an right-invariant vector fields and a right-invariant metric. + +The left-invariant metric is in general not right-invariant, except for compact Lie groups and their direct products with translations, see for example [LatifiToomanian:2013](@cite) for an even more general proof. + +With respect to this metric on the manifold the exponential and logarithmic maps are given by + +```math +\exp_g(X) = g ∘ \exp_𝒢(X) +\quad\text{with its inverse}\quad +\log_g(h) = \log_𝒢(g^{-1}∘h) +``` + +With respect to these geodesics and the representation in the Lie algebra, parallel transport +simplifies to the identity. To still access the Riemannian exponential map with respect to the +metric (compatible to the Levi-Civita connection) on the underlying Riemannian manifold, use +`exp(base_manifold(G), g, X)`. + +As an example, we compute + +```{julia} +h = exp(SO3, g, X) +``` +and its inverse is returning `X` as well. +```{julia} +log(SO3, g, h) +``` + +## The Special Euclidean group $\mathrm{SE}(3)$ of rigid body motions + +There are two further ingredients this tutorial needs to get towards rigid body motions. + +### Group actions + +For a group like $\mathrm{SO}(3)$ an action describes how points $g ∈ 𝒢$ can act on +points from a Riemannian manifold $p ∈ ℳ$ – in short an action of a certain [`AbstractGroupActionType`](@ref) +”combines“ $p$ and $g$ into a new element $q = σ_g(p)$. +In the example of $𝒢=\mathrm{SO}(3)$ and $ℳ=ℝ^3$ the action is (just) the application of the +rotation or the matrix-vector multiplication $σ_g(p)=gp$. +A special case is, when $ℳ$ itself is also a Lie group. +In the example this is the case, since together with vector addition $p+q$ we get the [`TranslationGroup`](@ref) $T(3) = (ℝ^3, +)$ +Depending on how the concatenation of two types, the [`AbstractLeftGroupActionType`](@ref) like the one here +and a [`AbstractRightGroupActionType`](@ref). + +```{julia} +T3 = TranslationGroup(3) +``` +### Products and semidirect products of Lie groups + +Similar to [product manifolds](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/metamanifolds/#ProductManifold) +(direct) [product Lie groups](../groups/product_group.md) are formed the same way. For two Lie groups +$𝒢 = 𝒢_1 × 𝒢_2$ is the product of the two manifolds together with the component wise application of the group operations: +the first group operation (of $𝒢_1$) to the first component and that of $𝒢_2$ to the second. + +Instead of the component wise or “not interacting” variant of the (direct) product Lie groups, +for the [semidirect product Lie groups](../groups/semidirect_product_group.md) $𝒢 = 𝒢_1 ⋉ 𝒢_2$ +we also require an action of how $𝒢_1$ acts on $𝒢_2$. +Semidirect here means that the first component of $𝒢$ is the same as for the direct product, +but before applying the second group operation on $𝒢_2$ one of the elements is “acted upon” +from an element of $𝒢_1$. The group operation reads for $(g_1,g_2), (h_1,h_2) ∈ 𝒢$ + +```math +(g_1,g_2) ∘ (h_1,h_2) := (g_1 ⋆ h_1, σ_{h_1}(g_2) ⋄ h_2). +``` + +### Rigid body motions + +We obtain the [special Euclidean group](../groups/special_euclidean_group.md) +$\mathrm{SE}(3) = \mathrm{SO}(3) ⋉ T(3)$ where the group action is the one discussed as an example before. + +```{julia} +SE3 = SpecialEuclideanGroup(3) +``` + +which we could also generate with `SO3` and `T3` from before as + +```{julia} +SO3 ⋉ T3 +``` + +This call employs the [`default_left_action`](@ref)`(SO3,T3)` to determine the action +for the semidirect product Lie group. This is defined whenever for two Lie groups, their (left) +action is clear, because there exists a reasonable default. Otherwise the full form + +```{julia} +LeftSemidirectProductLieGroup(SO3, T3, LeftGroupOperationAction()) +``` + +is necessary. Here, the first `Left` for the semidirect product refers to the fact that +the left group acts on the right one before the right group operation is performed. +[`LeftGroupOperationAction`](@ref) refers to that the group operation – left multiplication with a matrix – is applied here. + +For this case, any point $\mathrm{SE}(3)$ is a tuple of a rotation matrix and a vector. +We model this using [RecursiveArrayTools.jl](https://docs.sciml.ai/RecursiveArrayTools/stable/), +namely its `ArrayPartition`. +For example with the rotation matrix `g` from before we have + +```{julia} +g1 = ArrayPartition(g, [1.0, 0.0, 2.0]) +``` + +and for a pure translation we can reuse `e` as in + +```{julia} +h1 = ArrayPartition(e, [0.0, 3.0, 0.0]) +``` + +and we can summarize a few operations from before: both are valid points on `SE3` + +```{julia} +(is_point(SE3, g1), is_point(SE3, h1)) +``` + +we can perform the group operation + +```{julia} +gh1 = compose(SE3, g1, h1) +``` + +apply the Lie group exponential + +```{julia} +Y = log(SE3, gh1) +``` + +and look at the manifold dimension, it is the sum of dimensions of its components + +```{julia} +manifold_dimension(SE3) +``` + +The coordinates of `Y` are + +```{julia} +vee(LieAlgebra(SE3), Y) +``` + +which actually is component wise again, the first 3 values refer to the rotation part, +the second three to the translation part. + +## Technical details + +This tutorial is cached. It was last run on the following package versions. + +```{julia} +#| code-fold: true +#| echo: false +using Pkg +Pkg.status() +``` +```{julia} +#| code-fold: true +#| echo: false +#| output: asis +using Dates +println("This tutorial was last rendered $(Dates.format(now(), "U d, Y, H:M:S"))."); +``` + +## Literature + +````{=commonmark} +```@bibliography +Pages = ["getstarted.md"] +Canonical=false +``` +```` \ No newline at end of file