work with groups
- Introduction: group of rotations on a plane
- Relationship between groups, metrics and connections
- Literature
This is a short overview of group support in Manifolds.jl
and how to get started working with them.
Groups currently available in Manifolds.jl
are listed in group section.
You can read more about the theory of Lie groups for example in [Chi12]. An example application of Lie groups in robotics is described in [SDA21].
First, let’s load libraries we will use. RecursiveArrayTools.jl
is necessary because its ArrayPartition
is used as one of the possible representations of elements of product and semidirect product groups. StaticArrays.jl
can be used to speed up some operations.
using Manifolds, RecursiveArrayTools, StaticArrays
Introduction: group of rotations on a plane
Let’s first consider an example of the group of rotations of a plane, $\operatorname{SO}(2)$. They can be represented in several ways, for example as angles of rotation (which corresponds to RealCircleGroup
), unit complex numbers (CircleGroup
), or rotation matrices (SpecialOrthogonal
). Let’s consider the last representation since it is the most nontrivial one and can be more easily generalized to other groups.
The associated manifolds and groups are defined by:
G = SpecialOrthogonal(2)
M = base_manifold(G)
@assert M === Rotations(2)
This duality (Lie group and the underlying manifold being separate) is a common pattern in Manifolds.jl
. The group G
can be used for both Lie group-specific operations and metric-specific operation, while the manifold M
only allows using manifold and metric operations. This way groups can be specialized in ways not relevant to plain manifolds, and if someone doesn’t use the groups structure, they don’t have to consider it by just using the manifold.
Some basic definitions
# default basis
B = DefaultOrthogonalBasis()
# Identity rotation
p0 = @SMatrix [1.0 0; 0 1]
# Group identity element of a special type
IG = Identity(G)
Identity(MultiplicationOperation)
Let’s say we want to define a manifold point p_i
some rotation θ from the identity_element
reference rotation p0
(another point on the manifold that we will use as reference)
# + radians rotation from x-axis on plane to point i
xθi = π/6
0.5235987755982988
From Coordinates
To get our first Lie algebra element we can use the hat
function which is commonly used in robotics, or equivalently a more generalized get_vector
, function:
X_ = hat(G, IG, xθi) # specific definition to Lie algebras
xXi = get_vector(G, p0, xθi, B) # generalized definition beyond Lie algebras
println(xXi)
@assert isapprox(X_, xXi)
[0.0 -0.5235987755982988; 0.5235987755982988 0.0]
Note that hat
here assumes a default (orthogonal) basis for the more general get_vector
.
Note
In this case, the same would work given the base manifold
Rotations(2)
:_X_ = hat(M, p0, xθi) # Lie groups definition _X = get_vector(M, p0, xθi, B) # generalized definition @assert _X_ == xXi; @assert _X == xXi
One more caveat here is that for the Rotation matrices, the tangent vectors are always stored as elements from the Lie algebra.
Now, we can transform this algebra element to a point on the manifold using the exponential map exp
:
xRi = exp(G, p0, xXi)
# similarly for known underlying manifold
xRi_ = exp(M, p0, xXi)
@assert isapprox(xRi, xRi_)
To Coordinates
The logarithmic map transforms elements of the group back to its Lie algebra:
xXi_ = log(G, p0, xRi)
xXi__ = log(M, p0, xRi)
@assert xXi ≈ xXi__
Similarly, the coordinate values can be extracted from the algebra elements using vee
, or using the more generalized get_coordinates
:
# extracting coordinates using vee
xθi__ = vee(G, p0, xXi_)[1]
_xθi__ = vee(M, p0, xXi_)[1]
# OR, the preferred generalized get_coordinate function
xθi_ = get_coordinates(G, p0, xXi_, B)[1]
_xθi_ = get_coordinates(M, p0, xXi_, B)[1]
# confirm all versions are correct
@assert isapprox(xθi, xθi_); @assert isapprox(xθi, _xθi_)
@assert isapprox(xθi, xθi__); @assert isapprox(xθi, _xθi__)
Actions and Operations
With the basics in hand on how to move between the coordinate, algebra, and group representations, let’s briefly look at composition and application of points on the manifold. For example, a Rotations
manifold is the mathematical representation, but the points have an application purpose in retaining information regarding a specific rotation.
Points from a Lie group may have an associated action (i.e. a rotation) which we apply
. Consider rotating through θ = π/6
three vectors V
from their native domain Euclidean(2)
, from the reference point a
to a new point b
. Engineering disciplines sometimes refer to the action of a manifold point a
or b
as reference frames. More generally, by taking the tangent space at point p
, we are defining a local coordinate frame with basis B
, and should not be confused with “reference frame” a
or b
.
Keeping with our two-dimensional example above:
aV1 = [1; 0]
aV2 = [0; 1]
aV3 = [10; 10]
A_left = RotationAction(Euclidean(2), G)
bθa = π/6
bXa = get_vector(base_manifold(G), p0, bθa, B)
bRa = exp(G, p0, bXa)
for aV in [aV1; aV2; aV3]
bV = apply(A_left, bRa, aV)
# test we are getting the rotated vectors in Euclidean(2) as expected
@assert isapprox(bV[1], norm(aV) * cos(bθa))
@assert isapprox(bV[2], norm(aV) * sin(bθa))
end
Note
In general, actions are usually non-commutative and the user must therefore be aware whether they want to use
LeftAction
orRightAction
. In this case, the defaultLeftAction()
is used.
Finally, the actions (i.e. points from a manifold) can be compose
d together. Consider putting together two rotations aRb
and bRc
such that a single composite rotation aRc
is found. The next bit of code composes five rotations of π/4
increments:
A_left = RotationAction(M, G)
aRi = copy(p0)
iθi_ = π/4
x_θ = get_vector(M, p0, iθi_, B) #hat(Rn, R0, θ)
iRi_ = exp(M, p0, x_θ)
# do 5 times over:
# Ri_ = Ri*iRi_
for i in 1:5
aRi = compose(A_left, aRi, iRi_)
end
# drop back to a algebra, then coordinate
aXi = log(G, p0, aRi)
aθi = get_coordinates(G, p0, aXi, B)
# should wrap around to 3rd quadrant of xy-plane
@assert isapprox(-3π/4, aθi[1])
Warning
compose
orapply
must be done with group (not algebra) elements. This example shows how these two element types can easily be confused, since both the manifold group and algebra elements can have exactly the same data storage type – i.e. a 2x2 matrix.
As a last note, other rotation representations, including quaternions, Pauli matrices, etc., have similar features. A contrasting example in rotations, however, are Euler angles which can also store rotation information but quickly becomes problematic with familiar problems such as “gimbal-lock”.
Relationship between groups, metrics and connections
Group structure provides a canonical way to define 📖 exponential and logarithmic maps from the Lie algebra. They can be calculated in Manifolds.jl
using the exp_lie
and log_lie
functions. Such exponential and logarithmic maps can be extended invariantly to tangent spaces at any point of the Lie group. This extension is implemented using functions exp_inv
and log_inv
.
Finally, there are log
and exp
functions which are metric (or connection)-related functions in Manifolds.jl
. For groups which can be equipped with a bi-invariant metric, log
and log_inv
return the same result, similarly exp
and exp_inv
. However, only compact groups and their products with Euclidean spaces can have a bi-invariant metric (see for example Theorem 21.9 in [GQ20]). A prominent example of a Lie group without a bi-invariant metric is the special Euclidean group (in two or more dimensions). Then we have a choice between a metric but non-invariant exponential map (which is generally the default choice for exp
) or a non-metric, invariant exponential map (exp_inv
). Which one should be used depends on whether being metric or being invariant is more important in a particular application.
G = SpecialEuclidean(2)
p = ArrayPartition([1.0, -1.0], xRi)
X = ArrayPartition([2.0, -3.0], aXi)
q_m = exp(G, p, X)
println(q_m)
q_i = exp_inv(G, p, X)
println(q_i)
ArrayPartition{Float64, Tuple{Vector{Float64}, SMatrix{2, 2, Float64, 4}}}(([3.0, -4.0], [-0.25881904510252124 0.9659258262890682; -0.9659258262890682 -0.25881904510252124]))
ArrayPartition{Float64, Tuple{Vector{Float64}, MMatrix{2, 2, Float64, 4}}}(([0.8121200537878321, -3.8212723543456155], [-0.25881904510252124 0.9659258262890682; -0.9659258262890682 -0.25881904510252124]))
As we can see, the results differ. We can observe the invariance as follows:
p2 = ArrayPartition([2.0, -1.0], xRi)
q1_m = exp(G, translate(G, p2, p), translate_diff(G, p2, p, X))
q2_m = translate(G, p2, exp(G, p, X))
println(isapprox(q1_m, q2_m))
q1_i = exp_inv(G, translate(G, p2, p), translate_diff(G, p2, p, X))
q2_i = translate(G, p2, exp_inv(G, p, X))
println(isapprox(q1_i, q2_i))
false
true
Now, q1_m
and q2_m
are different due to non-invariance of the metric connection but q1_i
and q2_i
are equal due to invarianced of exp_inv
.
The following table outlines invariance of exp
and log
of various groups.
Group | Zero torsion connection | Invariant |
---|---|---|
ProductGroup | Product of connections in each submanifold | 🟡[1] |
SemidirectProductGroup | Same as underlying product | ❌ |
TranslationGroup | CartanSchoutenZero | ✅ |
CircleGroup | CartanSchoutenZero | ✅ |
GeneralLinearGroup | Metric connection from the left invariant metric induced from the standard basis on the Lie algebra | ❌ |
GeneralUnitaryMultiplicationGroup | CartanSchoutenZero (explicitly) | ✅ |
HeisenbergGroup | CartanSchoutenZero | ✅ |
SpecialLinearGroup | Same as GeneralLinear | ❌ |
Literature
- [Chi12]
- G. S. Chirikjian. Stochastic Models, Information Theory, and Lie Groups, Volume 2. 1 Edition, Vol. 2 of Applied and Numerical Harmonic Analysis (Birkhäuser Boston, MA, 2012).
- [GQ20]
- J. Gallier and J. Quaintance. Differential Geometry and Lie Groups: A Computational Perspective. Vol. 12 of Geometry and Computing (Springer International Publishing, Cham, 2020).
- [SDA21]
- J. Solà, J. Deray and D. Atchuthan. A micro Lie theory for state estimation in robotics (Dec 2021), arXiv:1812.01537 [cs.RO], arXiv: 1812.01537.
- 1Yes if all component connections are invariant separately, otherwise no