diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..d89910bc --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,2 @@ +ignore: + - "src/documentation_glossary.jl" # exclude this since it is just use to create the docs and code cov goes bogus on this. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 594fc01f..19bbd185 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,13 +1,13 @@ name: CI on: push: - branches: [master] + branches: [main] tags: [v*] pull_request: jobs: test: - name: Julia ${{ matrix.julia-version }} - ${{ matrix.group }} - ${{ matrix.os }} + name: Julia ${{ matrix.julia-version }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: diff --git a/.github/workflows/documenter.yml b/.github/workflows/documenter.yml index 5f3076f9..89e32675 100644 --- a/.github/workflows/documenter.yml +++ b/.github/workflows/documenter.yml @@ -1,7 +1,7 @@ name: Documenter on: push: - branches: [master] + branches: [main] tags: [v*] pull_request: diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 410d633c..9a2fb80f 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -1,7 +1,7 @@ name: Format on: push: - branches: [master] + branches: [main] tags: [v*] pull_request: diff --git a/.vale.ini b/.vale.ini index 4b5150cc..d5954533 100644 --- a/.vale.ini +++ b/.vale.ini @@ -14,6 +14,5 @@ BasedOnStyles = Vale, Google Google.Will = false ; given format and really with intend a _will_ Google.Headings = false ; some might jeally ahabe [] in their headers - [*.jl] BasedOnStyles = Vale, Google, write-good diff --git a/NEWS.md b/NEWS.md index 172971f9..9a91f7fe 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/). @@ -13,10 +13,11 @@ Everything denoted by “formerly” refers to the previous name in [`Manifolds. * `LieAlgebra` * `LieGroup` (formerly `GroupManifold`) as well as the concrete groups - * `AdditiveGroup` (formerly `TranslationGroup`) + * `TranslationGroup` + * `GeneralLinearGroup` (formerly `GeneralLinear`) * `AbstractGroupOperation` as well as its concrete subtypes * `AdditionGroupOperation` (formerly `AdditionOperation`) -* `AbstractGroupActionType` with its 2 specific (new) abstract +* `AbstractGroupActionType` with its 2 specific (new) abstract subtypes * `AbstractLeftGroupActionType` * `AbstractRightGroupActionType` * For the group operation actions there are now @@ -24,23 +25,27 @@ Everything denoted by “formerly” refers to the previous name in [`Manifolds. * `RightGroupOperation` (formerly `RightBackwardAction`) * `InverseLeftGroupOperation` (formerly `RightForwardAction`) * `InverseRightGroupOperation` (formerly `LeftBackwardAction`) +* `LieAlgebraOrthogonalBasis` (replaces `VeeOrthogonalBasis`, which is still available in `ManifoldsBase.jl`) +* `Identity` * `apply`and `apply!` * `base_manifold` to access the manifold within a Lie group * `compose` and `compose!` * `conjugate` and `conjugate!` * `diff_apply`, `diff_apply!`, `diff_group_apply`, and `diff_group_apply!` (formerly `apply_diff_[group][!]`) +* `diff_conjugate` and `diff_conjugate!` * `diff_left_compose`, `diff_left_compose!`, `diff_right_compose`, `diff_right_compose!` (formerly `translate_diff` with different sides) * `exp(G::LieGroup, g, X)` and `exp!(G::LieGroup, h, g, X)` (formerly `exp_inv` and `exp_inv!`) * `exp(G::LieGroup, ::Identity, X)` and `exp!(G::LieGroup, h, ::Identity, X)` (formerly `exp_lie` and `exp_lie!`) -* `Identity` -* `idenity_element` and `identity_element!` +* `hat` and `hat!`, with slightly different signatures, since the base point is omitted. +* `identity_element` and `identity_element!` * `inv` and `inv!` (`inv(::AbstractGroupAction)` was formerly `switch_direction`) * `inv_left_compose`, `inv_left_compose!` and `inv_right_compose`, `inv_right_compose!` (these functions correspond to `inverse_translate` with corresponding direction and side) * `is_identity` -* `Lie_bracket` and `Lie_bracket!` (formerly `lie_bracket`) +* `lie_bracket` and `lie_bracket!` * `log(G::LieGroup, g, h)` and `log!(G::LieGroup, X, g, h)` (formerly `log_inv` and `log_inv!`) * `log(G::LieGroup, ::Identity, g)` and `log!(G::LieGroup, X, ::Identity, g)` (formerly `log_lie` and `log_lie!`) * `switch` (formerly `switch_side`) +* `vee` and `vee!`, with slightly different signatures, since the base point is omitted. Compared to `Manifolds.jl` * all `translate` functions are not implemented here, since you can just use `compose`. The differentials are implemented as listed above with respect to both left and right argument of compose diff --git a/Project.toml b/Project.toml index d929ecfa..95840f20 100644 --- a/Project.toml +++ b/Project.toml @@ -1,18 +1,20 @@ name = "LieGroups" uuid = "6774de46-80ba-43f8-ba42-e41071ccfc5f" -authors = ["Seth Axen ", "Mateusz Baran ", "Ronny Bergmann ", "Yueh-Hua Tu", "Olivier Verdier " ] +authors = ["Seth Axen ", "Mateusz Baran ", "Ronny Bergmann ", "Yueh-Hua Tu", "Olivier Verdier "] version = "0.1.0" [deps] LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Manifolds = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e" ManifoldsBase = "3362f125-f0bb-47a3-aa74-596ffd7ef2fb" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] Aqua = "0.8" LinearAlgebra = "1.6" -Manifolds = "0.10" -ManifoldsBase = "0.15.16" +Manifolds = "0.10.5" +ManifoldsBase = "0.15.20" +Random = "1.6" Test = "1.6" julia = "1.6" diff --git a/Readme.md b/Readme.md index 7788bd19..1e489cb8 100644 --- a/Readme.md +++ b/Readme.md @@ -5,11 +5,16 @@ -[![](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliamanifolds.github.io/Manifolds.jl/latest/) +[![](https://img.shields.io/badge/docs-dev-blue.svg)](https://juliamanifolds.github.io/LieGroups.jl/dev/) [![Code Style: Blue](https://img.shields.io/badge/code%20style-blue-4495d1.svg)](https://github.com/invenia/BlueStyle) [![CI](https://github.com/JuliaManifolds/LieGroups.jl/actions/workflows/ci.yml/badge.svg)](https://github.com/JuliaManifolds/LieGroups.jl/actions?query=workflow%3ACI+branch%3Amain) [![codecov.io](http://codecov.io/github/JuliaManifolds/LieGroups.jl/coverage.svg?branch=main)](https://codecov.io/gh/JuliaManifolds/LieGroups.jl/) This is a package to rework the Lie group features of [`Manifolds.jl`](https://juliamanifolds.github.io/Manifolds.jl/stable/) in a unified way into a separate package. +> [!NOTE] +> Since this is a rework of the features from [`Manifolds.jl`](https://juliamanifolds.github.io/Manifolds.jl/stable/), both `LieGroups.jl` and `Manifolds.jl` 0.10 export a few types of same name, for example `Identity`. +While `LieGroups.jl` depends on `Manifolds.jl`, it is not recommended to load both into the same namespace, that is, doing `using Manifolds.jl, LieGroups.jl`, since then these conflicts might lead to unforeseen errors, where you would need to specify the namespace to resolve this ambiguity. +> See [transition from Manifolds.jl](https://juliamanifolds.github.io/LieGroups.jl/stable/tutorials/transition-from-manifoldsjl.html) for a comprehensive list. + This especially also includes a few different choices in default behaviour that is different from the [`Manifolds.jl`](https://juliamanifolds.github.io/Manifolds.jl/stable/) one. For purely manifold-based operations, any Lie group still is “build upon” a Riemannian manifold. diff --git a/docs/make.jl b/docs/make.jl index 8c4510e6..fdaf427d 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -99,7 +99,11 @@ for (md_file, doc_file) in [("CONTRIBUTING.md", "contributing.md"), ("NEWS.md", end ## Build tutorials menu -tutorials_menu = "How to..." => ["Get started with Lie Groups" => "index.md"] +tutorials_menu = + "How to..." => [ + "🚀 Get Started with LieGroups.jl" => "tutorials/getstarted.md", + "Transition from `GroupManifolds`" => "tutorials/transition.md", + ] # (e) finally make docs bib = CitationBibliography(joinpath(@__DIR__, "src", "references.bib"); style=:alpha) links = InterLinks( @@ -118,16 +122,17 @@ makedocs(; "Home" => "index.md", "About" => "about.md", (tutorials_in_menu ? [tutorials_menu] : [])..., + "Lie groups" => [ + "List of Lie groups" => "groups/index.md", + "General Linear" => "groups/general_linear.md", + "Translation group" => "groups/translation.md", + ], "Interfaces" => [ "Lie group" => "interface/group.md", "Lie algebra" => "interface/algebra.md", "Group operation" => "interface/operations.md", "Group action" => "interface/actions.md", ], - "Lie groups" => [ - "List of Lie Groups" => "groups/index.md", - "Translation group" => "groups/translation.md", - ], "Contributing to LieGroups.jl" => "contributing.md", "Notation" => "notation.md", "Changelog" => "news.md", diff --git a/docs/src/groups/general_linear.md b/docs/src/groups/general_linear.md new file mode 100644 index 00000000..7df8d952 --- /dev/null +++ b/docs/src/groups/general_linear.md @@ -0,0 +1,7 @@ +# The general linear group + +```@autodocs +Modules = [LieGroups] +Pages = ["groups/general_linear_group.jl"] +Order = [:type, :function] +``` \ No newline at end of file diff --git a/docs/src/groups/index.md b/docs/src/groups/index.md index 270641c3..090c9357 100644 --- a/docs/src/groups/index.md +++ b/docs/src/groups/index.md @@ -4,4 +4,5 @@ | Group | Manifold | ``∘`` | Comment | |:------|:---------|:---------:|:------| +| [`GeneralLinearGroup`](@ref) | [`InvertibleMatrices`](@extref `Manifolds.InvertibleMatrices`) | [`*`](@ref MatrixMultiplicationGroupOperation) | | | [`TranslationGroup`](@ref) | [`Euclidean`](@extref `Manifolds.Euclidean`) | [`+`](@ref AdditionGroupOperation) | | \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md index cb52ac36..75bb460f 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,3 +1,11 @@ # LieGroups.jl -Welcome to the Documentation of `LieGroups.jl`. \ No newline at end of file +Welcome to the Documentation of `LieGroups.jl`. + +```@meta +CurrentModule = LieGroups +``` + +```@docs +LieGroups.LieGroups +``` \ No newline at end of file diff --git a/docs/src/interface/algebra.md b/docs/src/interface/algebra.md index 90fe5789..b42da685 100644 --- a/docs/src/interface/algebra.md +++ b/docs/src/interface/algebra.md @@ -2,6 +2,7 @@ ```@docs LieAlgebra +LieAlgebraOrthogonalBasis ``` ## Functions on Lie algebras diff --git a/docs/src/interface/operations.md b/docs/src/interface/operations.md index 2959544f..421fe013 100644 --- a/docs/src/interface/operations.md +++ b/docs/src/interface/operations.md @@ -17,6 +17,21 @@ The following sections collect these. ```@autodocs Modules = [LieGroups] -Pages = ["addition.jl"] +Pages = ["addition_operation.jl"] Order = [:type, :function] +``` + +## [Multiplication group operation](@id multiplication-operation-sec) + +```@autodocs +Modules = [LieGroups] +Pages = ["multiplication_operation.jl"] +Order = [:type, :function] +``` + +## Literature + +```@bibliography +Pages = ["operations.md"] +Canonical=false ``` \ No newline at end of file diff --git a/docs/src/notation.md b/docs/src/notation.md index bf0abb6f..e261c27c 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -3,7 +3,7 @@ In this package,the notation introduced in [Manifolds.jl Notation](https://juliamanifolds.github.io/Manifolds.jl/latest/misc/notation.html) is used with the following additional parts. | Symbol | Description | Also used | Comment | -|:--:|:--------------- |:--:|:-- | +|:----:|:--------------- |:----:|:--- | | ``∘`` | a group operation | | | | ``c_g:\mathcal G → \mathcal G`` | the conjugation map (with `g`) | | | | ``\mathrm{e}`` | identity element of a group | | | diff --git a/docs/src/references.bib b/docs/src/references.bib index d30095da..690ff696 100644 --- a/docs/src/references.bib +++ b/docs/src/references.bib @@ -3,6 +3,23 @@ # # A +# +# +# G +@inproceedings{Giles:2008, + ADDRESS = {Berlin, Heidelberg}, + SERIES = {Lecture {Notes} in {Computational} {Science} and {Engineering}}, + TITLE = {Collected {Matrix} {Derivative} {Results} for {Forward} and {Reverse} {Mode} {Algorithmic} {Differentiation}}, + ISBN = {978-3-540-68942-3}, + DOI = {10.1007/978-3-540-68942-3_4}, + BOOKTITLE = {Advances in {Automatic} {Differentiation}}, + PUBLISHER = {Springer}, + AUTHOR = {Giles, Mike B.}, + EDITOR = {Bischof, Christian H. and Bücker, H. Martin and Hovland, Paul and Naumann, Uwe and Utke, Jean}, + YEAR = {2008}, + PAGES = {35--44} +} + # # # H diff --git a/docs/src/tutorials/getstarted.md b/docs/src/tutorials/getstarted.md new file mode 100644 index 00000000..1622d4c6 --- /dev/null +++ b/docs/src/tutorials/getstarted.md @@ -0,0 +1 @@ +# 🚀 Get Started with LieGroups.jl \ No newline at end of file diff --git a/docs/src/tutorials/transition.md b/docs/src/tutorials/transition.md new file mode 100644 index 00000000..71728583 --- /dev/null +++ b/docs/src/tutorials/transition.md @@ -0,0 +1,52 @@ +# Transition from `GroupManifolds` in `Manifolds.jl` + +One predecessor of `LieGroups.jl` are the [`GroupManifold`](@extref `Manifolds.GroupManifold`)s in `Manifolds.jl`. +While this package provides the same features, one reason for a new package is, +that a “restart” offers the opportunity to put the main focus for the functions in this package +really on Lie groups. + +This tutorial provides an overview of the necessary changes to your code if you based it on the predecessor. + +## Table of function names and its successors + +The following table lists all functions related to `GroupManifolds` and their new names +or replacements here in `LieGroups.jl`. In this code `G` always refers to the `GroupManifold` +in the first column and the [`LieGroup`](@ref) in the second. +Lie group elements (points) are always `g,h`, +Lie algebra elements (vectors) always `X, Y`. + +New functions and types in this package are only mentioned, if they are worth a comment and if something changed. + +The list is alphabetical, but first lists types, then functions + +| `Manifolds.jl` | `LieGroups.jl` | Comment | +|:---------- |:---------- |:-------------- | +| `AdditionOperation` | [`AdditionGroupOperation`](@ref) | | +| `LeftForwardAction` | [`LeftGroupOperation`](@ref) +| `RightBackwardAction` | [`RightGroupOperation`](@ref) | | +| `LeftBackwardAction` | [`InverseRightGroupOperation`](@ref) | note that this is now also aa [`AbstractLeftGroupActionType`](@ref) | +| | [`LieAlgebra`](@ref)`(G)` | new alias to emphasize its manifold- and vector structure as well as for a few dispatch methods. | +| `GroupManifold(M, op)` | [`LieGroup`](@ref)`(M, op)` | | +| `RightForwardAction` | [`InverseLeftGroupOperation`](@ref) | note that this is an [`AbstractRightGroupActionType`](@ref) | +| `adjoint` | [`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}`` | +| `exp(G, g, X)` | `exp(`[`base_manifold`](@ref base_manifold(G::LieGroup))`(G), g, X)` | the previous defaults whenever not agreeing with the invariant one can now be accessed on the internal manifold | +| `exp_inv(G, g, X)` | [`exp`](@ref exp(G::LieGroup, g, X, t::Number))`(G, g, X)` | the exponential map invariant to the group operation is the default on Lie groups here | +| `exp_lie(G, X)` | [`exp`](@ref exp(G::LieGroup, e::Identity, X, t::Number))`(G, `[`Identity`](@ref)`(G), X)` | the (matrix) exponential is now the one at the [`Identity`](@ref)`(G)`, since there it agrees with the invariant one | +| `inverse_translate(G, g, h, c)` | [`inv_left_compose`](@ref)`(G, g, h)`, [`inv_right_compose`](@ref)`(G, g, h)` | compute ``g^{-1}∘h`` and ``g∘h^{-1}``, resp. | +| `inverse_tranlsate_diff(G, g, h, X, LeftForwardAction())` | - | discontinued, use `diff_left_compose(G, inv(G,g), h)` | +| `inverse_tranlsate_diff(G, g, h, X, RightBackwardAction())` | - | discontinued, use `diff_left_compose(G, h, inv(G,g))` | +| `log(G, g, h)` | `log(`[`base_manifold`](@ref base_manifold(G::LieGroup))`(G), g, h)` | you can now access the previous defaults on the internal manifold whenever they do not agree with the invariant one | +| `log_inv(G, g, h)` | [`log`](@ref log(G::LieGroup, g, h))`(G, g, h)` | the logarithmic map invariant to the group operation is the default on Lie groups here | +| `log_lie(G, g)` | [`log`](@ref log(G::LieGroup, e::Identity, g))`(G, `[`Identity`](@ref)`(G), g)` | the (matrix) logarithm is now the one at the identity, since there it agrees with the invariant one | +| `switch_direction(A)` | [`inv`](@ref inv(::AbstractGroupAction))`(A)` | switches from an action to its inverse action (formerly the direction forward/backward, sometimes even left/right, do not confuse with the side left/right). | +| `switch_side(A)` | [`switch`](@ref switch(::AbstractGroupAction))`(A)` | switches from a left action to its corresponding right action. | +| `translate(G, g, h)` | [`compose`](@ref)`(G, g, h)` | unified to `compose` | +| `translate_diff(G, g, X, c)` | [`diff_left_compose`](@ref)`(G, g, h, X)`, [`diff_right_compose`](@ref)`(G, g, h, X)` | for compose ``g∘h`` the functions now specify whether the derivative is taken w.r.t. to the left (`g`) or right (`h`) argument | +|`VeeOrthogonalBasis` | [`LieAlgebraOrthogonalBasis`](@ref) | | + +# Notable changes + +* The [`GeneralLinearGroup`](@ref) (formerly `GeneralLinear`) switched to using its Lie algebra to represent tangent vectors. diff --git a/src/LieGroups.jl b/src/LieGroups.jl index 1e45a8a7..1b7d8ee8 100644 --- a/src/LieGroups.jl +++ b/src/LieGroups.jl @@ -1,20 +1,32 @@ +@doc raw""" +LieGroups.jl: Lie groups and Lie algebras in Julia. + +The package is named after the norwegian mathematician [Sophus Lie](https://en.wikipedia.org/wiki/Sophus_Lie). + +* 📚 Documentation: [manoptjl.org](https://juliamanifolds.github.io/LieGroups.jl/dev/) +* 📦 Repository: [github.com/JuliaManifolds/LieGroups.jl](https://github.com/JuliaManifolds/LieGroups.jl) +* 💬 Discussions: [github.com/JuliaManifolds/LieGroups.jl/discussions](https://github.com/JuliaManifolds/LieGroups.jl/discussions) +* 🎯 Issues: [github.com/JuliaManifolds/LieGroups.jl/issues](https://github.com/JuliaManifolds/LieGroups.jl/issues) +""" module LieGroups -using ManifoldsBase, Manifolds, LinearAlgebra +using LinearAlgebra, ManifoldsBase, Manifolds, Random # # # = Compatibility (and a bit of type piracy for now) # The following imports are necessary to use Manifolds.jl 0.10 with Lie groups # The line is removed when the Groups are removed from possibly 0.11 -import Manifolds: apply, apply!, identity_element, is_identity, compose +import Manifolds: + apply, apply!, compose, identity_element, is_identity, hat, hat!, vee, vee! # Both define the following structs, so these for now lead to asking for explicit prefixes # Manifolds: Identity, TranslationGroup include("documentation_glossary.jl") include("interface.jl") include("Lie_algebra/Lie_algebra_interface.jl") # Generic Operations -include("group_operations/addition.jl") +include("group_operations/addition_operation.jl") +include("group_operations/multiplication_operation.jl") # Actions include("group_actions/group_action_interface.jl") @@ -22,41 +34,56 @@ include("group_actions/group_operation_action.jl") # Lie groups include("groups/translation_group.jl") +include("groups/general_linear_group.jl") export LieGroup, LieAlgebra - +export LieAlgebraOrthogonalBasis +# +# +# Group Operations export AbstractGroupOperation, Identity export AdditionGroupOperation +export AbstractMultiplicationGroupOperation +export MatrixMultiplicationGroupOperation +# +# +# Group Actions export AbstractGroupActionType, AbstractGroupAction export AbstractLeftGroupActionType, AbstractRightGroupActionType export LeftGroupOperation, RightGroupOperation export InverseLeftGroupOperation, InverseRightGroupOperation export GroupOperationAction -export TranslationGroup +# +# +# Specific groups +export TranslationGroup, GeneralLinearGroup export adjoint, adjoint!, apply, apply! export base_lie_group, base_manifold export compose, compose! -export diff_apply, +export det, + diff_apply, diff_apply!, diff_group_apply, diff_group_apply!, diff_left_compose, diff_left_compose!, diff_right_compose, - diff_right_compose!, - inv_left_compose, - inv_left_compose!, - inv_right_compose, - inv_right_compose! + diff_right_compose! +export get_coordinates, get_coordinates!, get_vector, get_vector! +export hat, hat! +export inv, inv!, inv_left_compose, inv_left_compose!, inv_right_compose, inv_right_compose! export isapprox, is_point, is_vector export conjugate, conjugate!, diff_conjugate, diff_conjugate! export exp, exp! export identity_element, identity_element!, is_identity, inv, inv!, diff_inv, diff_inv! export lie_bracket, lie_bracket!, log, log! +export manifold_dimension export norm +export rand, rand! export switch +export vee, vee! export zero_vector, zero_vector! end # module LieGroups diff --git a/src/Lie_algebra/Lie_algebra_interface.jl b/src/Lie_algebra/Lie_algebra_interface.jl index 13be4e4b..b24fd8b0 100644 --- a/src/Lie_algebra/Lie_algebra_interface.jl +++ b/src/Lie_algebra/Lie_algebra_interface.jl @@ -26,6 +26,41 @@ function LieAlgebra(G::LieGroup{𝔽,O}) where {𝔽,O<:AbstractGroupOperation} return LieAlgebra{𝔽,O,typeof(G)}(G, Identity(G), ManifoldsBase.TangentSpaceType()) end +function ManifoldsBase.get_coordinates(𝔤::LieAlgebra, X, B::ManifoldsBase.AbstractBasis) + G = 𝔤.manifold + return get_coordinates(base_manifold(G), identity_element(G), X, B) +end +function ManifoldsBase.get_coordinates!(𝔤::LieAlgebra, c, X, B::ManifoldsBase.AbstractBasis) + G = 𝔤.manifold + get_coordinates!(base_manifold(G), c, identity_element(G), X, B) + return c +end + +function ManifoldsBase.get_vector(𝔤::LieAlgebra, c, B::ManifoldsBase.AbstractBasis) + G = 𝔤.manifold + return get_vector(base_manifold(G), identity_element(G), c, B) +end +function ManifoldsBase.get_vector!(𝔤::LieAlgebra, X, c, B::ManifoldsBase.AbstractBasis) + G = 𝔤.manifold + get_vector!(base_manifold(G), X, identity_element(G), c, B) + return X +end + +""" + is_point(𝔤::LieAlgebra, X; kwargs...) + +Check whether `X` is a valid point on the Lie Algebra `𝔤`. +This falls back to checking whether `X` is a valid point on the tangent space +at the [`identity_element`](@ref)`(G)` on `G.manifold` on the [`LieGroup`](@ref) +of `G` +""" +function ManifoldsBase.is_point(𝔤::LieAlgebra, X; kwargs...) + # the manifold stored in the Fiber / Lie algebra is the Lie group G + G = 𝔤.manifold + e = identity_element(G) + return ManifoldsBase.is_vector(G.manifold, e, X; kwargs...) +end + _doc_lie_bracket = """ lie_bracket!(𝔤::LieAlgebra, X, Y) lie_bracket!(𝔤::LieAlgebra, Z, X, Y) @@ -48,31 +83,33 @@ function lie_bracket! end @doc "$(_doc_lie_bracket)" lie_bracket!(𝔤::LieAlgebra, Z, X, Y) -""" - is_point(𝔤::LieAlgebra, X; kwargs...) - -Check whether `X` is a valid point on the Lie Algebra `𝔤`. -This falls back to checking whether `X` is a valid point on the tangent space -at the [`identity_element`](@ref)`(G)` on `G.manifold` on the [`LieGroup`](@ref) -of `G` -""" -function ManifoldsBase.is_point(𝔤::LieAlgebra, X; kwargs...) - # the manifold stored in the Fiber / Lie algebra is the Lie group G - G = 𝔤.manifold - e = identity_element(G) - return ManifoldsBase.is_vector(G.manifold, e, X; kwargs...) -end - -# Move this line already to ManifoldsBase? On Fibers of course. -LinearAlgebra.norm(𝔤::LieAlgebra, X) = LinearAlgebra.norm(𝔤.manifold, 𝔤.point, X) -# Non-mutating case with single number -> avoid ambiguity -LinearAlgebra.norm(𝔤::LieAlgebra, X::Real) = LinearAlgebra.norm(𝔤.manifold, 𝔤.point, X) function LinearAlgebra.norm( G::LieGroup{𝔽,O}, ::Identity{O}, X ) where {𝔽,O<:AbstractGroupOperation} return LinearAlgebra.norm(G, identity_element(G), X) end +_doc_rand_algebra = """ + rand(::LieGroup; vector_at=nothing, σ=1.0, kwargs...) + rand(::LieAlgebra; σ=1.0, kwargs...) + rand!(::LieGroup, gX; vector_at=nothing, kwargs...) + rand!(::LieAlgebra, X; σ=1.0, kwargs...) + +Compute a random point or tangent vector on a Lie group. + +For points this just means to generate a random point on the +underlying manifold itself. + +For tangent vectors, an element in the Lie Algebra is generated, +see also [`rand(::LieAlgebra; kwargs...)`](@ref) +""" + +@doc "$(_doc_rand_algebra)" +Random.rand(::LieAlgebra; kwargs...) + +@doc "$(_doc_rand_algebra)" +Random.rand!(::LieAlgebra, X; kwargs...) + function Base.show(io::IO, 𝔤::LieAlgebra) return print(io, "LieAlgebra( $(𝔤.manifold) )") end diff --git a/src/documentation_glossary.jl b/src/documentation_glossary.jl index ff940bd4..7ee687a4 100644 --- a/src/documentation_glossary.jl +++ b/src/documentation_glossary.jl @@ -51,6 +51,7 @@ end # LaTeX # Define LaTeX shortcuts _tex(args...; kwargs...) = glossary(:LaTeX, args...; kwargs...) +define!(:LaTeX, :big, raw"\big") define!(:LaTeX, :bigl, raw"\bigl") define!(:LaTeX, :bigr, raw"\bigr") define!(:LaTeX, :Big, raw"\Big") @@ -59,11 +60,32 @@ define!(:LaTeX, :Bigr, raw"\Bigr") define!(:LaTeX, :def, raw"\coloneqq") define!(:LaTeX, :Cal, (letter) -> raw"\mathcal " * "$letter") define!(:LaTeX, :exp, raw"\exp") +define!(:LaTeX, :frac, (a, b) -> raw"\frac" * "{$a}{$b}") define!(:LaTeX, :Frak, (letter) -> raw"\mathfrak " * "$letter") +define!(:LaTeX, :l, "") # lazy fallback for sets +define!(:LaTeX, :r, "") # lazy fallback for sets define!(:LaTeX, :log, raw"\log") define!(:LaTeX, :qquad, raw"\qquad") define!(:LaTeX, :quad, raw"\quad") -define!(:LaTeX, :rm, (letter) -> raw"\mathrm " * "$letter") +define!(:LaTeX, :rm, (letter) -> raw"\mathrm" * "{$letter}") +define!( + :LaTeX, + :Set, + (content, size="") -> + _tex(Symbol("$(size)l")) * + raw"\{ " * + "$(content)" * + _tex(Symbol("$(size)r")) * + raw" \}", +) +define!( + :LaTeX, + :SetDef, + (elem, cond, size="") -> + _tex(:Set, elem * raw"\ " * _tex(Symbol("$(size)")) * raw"|\ " * "$(cond)", size), +) +define!(:LaTeX, :sum, raw"\sum") +define!(:LaTeX, :transp, raw"\mathrm{T}") # # --- # Mathematics and semantic symbols diff --git a/src/group_operations/addition.jl b/src/group_operations/addition_operation.jl similarity index 97% rename from src/group_operations/addition.jl rename to src/group_operations/addition_operation.jl index 07db44c7..b3f42a1a 100644 --- a/src/group_operations/addition.jl +++ b/src/group_operations/addition_operation.jl @@ -125,7 +125,7 @@ Base.exp( @doc "$(_doc_exp_add)" function ManifoldsBase.exp!( - G::LieGroup{𝔽,AdditionGroupOperation}, + ::LieGroup{𝔽,AdditionGroupOperation}, g, ::Identity{AdditionGroupOperation}, X, @@ -167,6 +167,12 @@ function inv!(::LieGroup{𝔽,AdditionGroupOperation}, h, g) where {𝔽} h .= (-1) .* g return h end +# Resolve ambiguity +function inv!( + G::LieGroup{𝔽,AdditionGroupOperation}, q, ::Identity{AdditionGroupOperation} +) where {𝔽} + return identity_element!(G, q) +end _doc_lie_bracket_add = """ lie_bracket!(𝔤::LieAlgebra{𝔽,AdditionGroupOperation}, X, Y) diff --git a/src/group_operations/multiplication_operation.jl b/src/group_operations/multiplication_operation.jl new file mode 100644 index 00000000..285a0af6 --- /dev/null +++ b/src/group_operations/multiplication_operation.jl @@ -0,0 +1,347 @@ +""" + AbstractMultiplicationGroupOperation <: AbstractGroupOperation + +A group operation that is realised introducing defaults that fall back +to `*` being overloaded, for example +`_compose(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, a, b) = a * b` +""" +abstract type AbstractMultiplicationGroupOperation <: AbstractGroupOperation end + +""" + AbstractMultiplicationGroupOperation <: AbstractMultiplicationGroupOperation + +A group operation that is realised by a matrix multiplication. +""" +struct MatrixMultiplicationGroupOperation <: AbstractMultiplicationGroupOperation end + +Base.:*(::Identity{MatrixMultiplicationGroupOperation}, p::Union{AbstractMatrix,Number}) = p +function Base.:*( + p::Union{AbstractMatrix,Number}, ::Identity{MatrixMultiplicationGroupOperation} +) + return p +end +function Base.:*( + e::Identity{<:AbstractMultiplicationGroupOperation}, + ::Identity{<:AbstractMultiplicationGroupOperation}, +) + return e +end +function Base.:*( + e::Identity{<:AbstractMultiplicationGroupOperation}, ::Identity{AdditionGroupOperation} +) + return e +end +function Base.:*( + ::Identity{AdditionGroupOperation}, e::Identity{<:AbstractMultiplicationGroupOperation} +) + return e +end + +Base.:/(p, ::Identity{<:AbstractMultiplicationGroupOperation}) = p +Base.:/(::Identity{<:AbstractMultiplicationGroupOperation}, p) = inv(p) +function Base.:/( + e::Identity{<:AbstractMultiplicationGroupOperation}, + ::Identity{<:AbstractMultiplicationGroupOperation}, +) + return e +end + +Base.:\(p, ::Identity{<:AbstractMultiplicationGroupOperation}) = inv(p) +Base.:\(::Identity{<:AbstractMultiplicationGroupOperation}, p) = p +function Base.:\( + e::Identity{<:AbstractMultiplicationGroupOperation}, + ::Identity{<:AbstractMultiplicationGroupOperation}, +) + return e +end + +_doc_compose_mult = """ + compose(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, g, h) + compose!(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, k, g, h) + +Compute the group operation composition of `g` and `h` with respect to +an [`AbstractMultiplicationGroupOperation`](@ref) on `G`, which falls back to calling +`g*h`, where `*` is assumed to be overloaded accordingly. + +This can be computed in-place of `k`. +""" + +@doc "$(_doc_compose_mult)" +compose(::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, g, h) where {𝔽} + +@doc "$(_doc_compose_mult)" +compose!(::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, k, g, h) where {𝔽} + +function _compose!(::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, k, g, h) where {𝔽} + # perform the multiplication “safe”, that is, even when providing + # one of the inputs `g,h`` and as output `k` + (k === g || k === h) ? copyto!(k, g * h) : mul!(k, g, h) + return k +end + +Base.inv(e::Identity{<:AbstractMultiplicationGroupOperation}) = e + +LinearAlgebra.det(::Identity{<:AbstractMultiplicationGroupOperation}) = true + +_doc_diff_conjugate_add = """ + diff_conjugate(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, g, h, X) + diff_conjugate!(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, Y, g, h, X) + +Compute the differential of the conjutage ``c_g(h) = g$(_math(:∘))h$(_math(:∘))g^{-1} = ghg^{-1}``, +which simplifies for an [`AbstractMultiplicationGroupOperation`](@ref) to ``D(c_g(h))[X] = gXg^{-1}``. +""" + +@doc "$(_doc_diff_conjugate_add)" +diff_conjugate(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, g, h, X) where {𝔽} + +@doc "$(_doc_diff_conjugate_add)" +function diff_conjugate!( + G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, Y, g, h, X +) where {𝔽} + inv_right_compose!(G, Y, X, g) # Y = Xg^{-1} + compose!(G, Y, g, Y) # Y = gY + return Y +end + +_doc_diff_inv_mult = """ + diff_inv(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, g, X) + diff_inv!(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, Y, g, X) + +Compute the value of differential ``Dι_{$(_math(:G))}(g)[X]`` of matrix inversion ``ι_{$(_math(:G))}(g) := g^{-1}`` at ``X ∈ 𝔤`` +in the [`LieAlgebra`](@ref) ``𝔤`` of the [`LieGroup`](@ref) `G`. + +The formula is given by + +```math +Dι_{$(_math(:G))}(g)[X] = -g^{$(_tex(:transp))}Xg^{-1}, +``` + +which stems from using the differential of the inverse from [Giles:2008](@cite) given by +``D(g^{-1})[X] = -g^{-1}Xg^{-1}`` composed with the push forward of the left composition +``Dλ_$(_math(:e))(g)[X] = gX`` mapping from the Liea algebra into the tangent space at ``g``, +and its adjoint ``D^*λ_$(_math(:e))(g)[X] = g^{$(_tex(:transp))}X``. +Then we get ``g^{$(_tex(:transp))}(g^{-1}(gX)g^{-1})`` which simplifies to ``-g^{$(_tex(:transp))}Xg^{-1}`` from above. +""" + +@doc "$(_doc_diff_inv_mult)" +diff_inv(::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, g, X) where {𝔽} + +function diff_inv( + ::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, + p::AbstractArray{<:Number,0}, + X::AbstractArray{<:Number,0}, +) where {𝔽} + p_inv = inv(p[]) + return -(p[] * X * p_inv) +end + +@doc "$(_doc_diff_inv_mult)" +function diff_inv!(::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, Y, p, X) where {𝔽} + p_inv = inv(p) + Z = X * p_inv + mul!(Y, p', Z) + Y .*= -1 + return Y +end + +_doc_diff_left_compose_mult = """ + diff_left_compose(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, g, h, X) + diff_left_compose!(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, Y, g, h, X) + +Compute the differential of the left group multiplication ``λ_g(h) = g$(_math(:∘))h``, +which simplifies for an [`AbstractMultiplicationGroupOperation`](@ref) to ``Dλ_g(h)[X] = gX``. +""" + +@doc "$(_doc_diff_left_compose_mult)" +diff_left_compose(::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, g, h, X) where {𝔽} + +@doc "$(_doc_diff_left_compose_mult)" +function diff_left_compose!( + G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, Y, g, h, X +) where {𝔽} + return copyto!(LieAlgebra(G), Y, g * X) +end + +_doc_diff_right_compose_mult = """ + diff_right_compose(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, h, g, X) + diff_right_compose!(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, Y, h, g, X) + +Compute the differential of the right group multiplication ``ρ_g(h) = h$(_math(:∘))g``, +which simplifies for an [`AbstractMultiplicationGroupOperation`](@ref) to ``Dρ_g(h)[X] = Xg``. +""" + +@doc "$(_doc_diff_right_compose_mult)" +diff_right_compose(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, h, g, X) where {𝔽} + +@doc "$(_doc_diff_right_compose_mult)" +function diff_right_compose!( + G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, Y, g, h, X +) where {𝔽} + return copyto!(LieAlgebra(G), Y, X * g) +end + +_doc_exp_mult = """ + exp(G::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, e::Identity{MatrixMultiplicationGroupOperation}, X, t::Number=1) + exp!(G::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, g, e::Identity{MatrixMultiplicationGroupOperation}, X, t::Number=1) + +Compute the Lie group exponential on a [`LieGroup`](@ref) with a [`MatrixMultiplicationGroupOperation`](@ref), +which simplifies to the [matrix exponential](https://en.wikipedia.org/wiki/Matrix_exponential). + +This can be computed in-place of `g`. +""" + +@doc "$(_doc_exp_mult)" +Base.exp( + ::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, + ::Identity{MatrixMultiplicationGroupOperation}, + X, + t::Number=1, +) where {𝔽} = exp(t * X) + +@doc "$(_doc_exp_mult)" +function ManifoldsBase.exp!( + ::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, + g, + ::Identity{MatrixMultiplicationGroupOperation}, + X, + t::Number=1, +) where {𝔽} + copyto!(g, exp(t .* X)) + return g +end + +_doc_identity_element_mult = """ + identity_element(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}) + identity_element!(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, e) + +Return the a point representation of the [`Identity`](@ref), +which for an [`AbstractMultiplicationGroupOperation`](@ref) is the one-element or identity array. +""" + +@doc "$(_doc_identity_element_mult)" +identity_element(::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}) where {𝔽} + +@doc "$(_doc_identity_element_mult)" +identity_element!(::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, e) where {𝔽} +function identity_element!( + ::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, e::AbstractMatrix +) where {𝔽} + return copyto!(e, LinearAlgebra.I) +end + +_doc_inv_mult = """ + inv(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperationroupOperation}, g) + inv!(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, h, g) + +Compute the inverse group element ``g^{-1}``, which for an [`AbstractMultiplicationGroupOperation`](@ref) +simplifies to the multiplicative inverse ``g^{-1}``. This can be done in-place of `h`. +""" + +@doc "$(_doc_inv_mult)" +Base.inv(G::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, g) where {𝔽} + +@doc "$(_doc_inv_mult)" +function inv!(::LieGroup{𝔽,<:AbstractMultiplicationGroupOperation}, h, g) where {𝔽} + copyto!(h, inv(g)) + return h +end +function inv!( + G::LieGroup{𝔽,O}, q, ::Identity{O} +) where {𝔽,O<:AbstractMultiplicationGroupOperation} + return identity_element!(G, q) +end + +# Compute g^{-1}h more efficient than inverting g +function inv_left_compose!( + ::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, k, g, h +) where {𝔽} + return copyto!(k, g \ h) +end +# Compute g∘h^{-1} more efficient than inverting h +function inv_right_compose!( + ::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, k, g, h +) where {𝔽} + return copyto!(k, g / h) +end + +_doc_lie_bracket_mult = """ + lie_bracket(::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, X, Y) + lie_bracket!(::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, Z, X, Y) + +Compute the Lie bracket ``[⋅,⋅]: $(_math(:𝔤))×$(_math(:𝔤)) → $(_math(:𝔤))``, +which for the for the [`MatrixMultiplicationGroupOperation`](@ref) yields the +commutator bracket + +```math +[X, Y] = XY-YX +``` + +The computation can be done in-place of `Z`. +""" + +@doc "$(_doc_lie_bracket_mult)" +lie_bracket(::LieAlgebra{𝔽,MatrixMultiplicationGroupOperation}, X, Y) where {𝔽} + +@doc "$(_doc_lie_bracket_mult)" +function lie_bracket!(::LieAlgebra{𝔽,MatrixMultiplicationGroupOperation}, Z, X, Y) where {𝔽} + mul!(Z, X, Y) + mul!(Z, Y, X, -1, true) + return Z +end + +_doc_log_mult = """ + log(G::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, e::Identity{MatrixMultiplicationGroupOperation}, g) + log!(G::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, X, e::Identity{MatrixMultiplicationGroupOperation}, g) + +Compute the Lie group logarithm on a [`LieGroup`](@ref) with a [`MatrixMultiplicationGroupOperation`](@ref), +which simplifies to the [matrix logarithm](https://en.wikipedia.org/wiki/Logarithm_of_a_matrix). + +This can be computed in-place of `X`. +""" + +@doc "$(_doc_log_mult)" +Base.log( + ::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, + ::Identity{MatrixMultiplicationGroupOperation}, + g, +) where {𝔽} = log(g) +function Base.log( + G::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, + e::Identity{MatrixMultiplicationGroupOperation}, + ::Identity{MatrixMultiplicationGroupOperation}, +) where {𝔽} + return zero_vector(G, e) +end + +@doc "$(_doc_log_mult)" +function ManifoldsBase.log!( + ::LieGroup{𝔽,MatrixMultiplicationGroupOperation}, + X, + ::Identity{MatrixMultiplicationGroupOperation}, + g, +) where {𝔽} + copyto!(X, log(g)) + return X +end + +LinearAlgebra.mul!(q, ::Identity{<:AbstractMultiplicationGroupOperation}, p) = copyto!(q, p) +function LinearAlgebra.mul!( + q::AbstractMatrix, p::AbstractMatrix, ::Identity{MatrixMultiplicationGroupOperation} +) + return copyto!(q, p) +end +function LinearAlgebra.mul!( + q::Union{AbstractMatrix}, + ::Identity{<:AbstractMultiplicationGroupOperation}, + ::Identity{<:AbstractMultiplicationGroupOperation}, +) + return copyto!(q, I) +end +function LinearAlgebra.mul!( + q::Identity{<:AbstractMultiplicationGroupOperation}, + ::Identity{<:AbstractMultiplicationGroupOperation}, + ::Identity{<:AbstractMultiplicationGroupOperation}, +) + return q +end +Base.one(e::Identity{<:AbstractMultiplicationGroupOperation}) = e diff --git a/src/groups/general_linear_group.jl b/src/groups/general_linear_group.jl new file mode 100644 index 00000000..655faf13 --- /dev/null +++ b/src/groups/general_linear_group.jl @@ -0,0 +1,57 @@ + +@doc """ + GeneralLinearGroup{𝔽,T} + +The general linear group ``$(_tex(:rm,"GL"))(n)`` is the set of all invertible matrices + +```math +$(_tex(:rm,"GL"))(n) = $(_tex(:SetDef, "g ∈ 𝔽^{n×n}", "$(_tex(:rm,"det"))(p) ≠ 0", "big")), +$(_tex(:qquad)) 𝔽 ∈ $(_tex(:Set, "ℝ, ℂ")), +``` +equipped with the [`MatrixMultiplicationGroupOperation`](@ref) as the group operation. + +The set of invertible matrices is a Riemannian manifold, since it inherits its structure from +the embedding as an open subset of the space of matrices ``ℝ^{n×n}``. + +# Constructor + + GeneralLinearGroup(n::Int; field=ℝ, kwargs...) + +Generate the general linear group group on ``𝔽^{n×n}``. +All keyword arguments in `kwargs...` are passed on to [`InvertibleMatrices`](@extref `Manifolds.InvertibleMatrices`). +""" +const GeneralLinearGroup{𝔽,T} = LieGroup{ + 𝔽,MatrixMultiplicationGroupOperation,Manifolds.InvertibleMatrices{𝔽,T} +} + +function GeneralLinearGroup(n::Int; field=ManifoldsBase.ℝ, kwargs...) + Im = Manifolds.InvertibleMatrices(n, field; kwargs...) + return GeneralLinearGroup{typeof(Im).parameters...}( + Im, MatrixMultiplicationGroupOperation() + ) +end + +_doc_exp_GLn = """ + exp(::GeneralLinearGroup, ::Identity{MatrixMultiplicationGroupOperation}, X) + exp!(::GeneralLinearGroup, g, ::Identity{MatrixMultiplicationGroupOperation}, X) + +Compute the Lie group exponential on the [`GeneralLinearGroup`](@ref), which is given by the +[matrix exponential](https://en.wikipedia.org/wiki/Matrix_exponential) + +```math +$(_tex(:exp)) X = $(_tex(:sum))_{k=0}^{∞} $(_tex(:frac, "1", "k!"))X^k +``` + +see also [HilgertNeeb:2012; Example 9.2.3 (b)](@cite) +""" + +@doc "$(_doc_exp_GLn)" +exp(::GeneralLinearGroup, ::Identity{MatrixMultiplicationGroupOperation}, X) + +@doc "$(_doc_exp_GLn)" +exp!(::GeneralLinearGroup, g, ::Identity{MatrixMultiplicationGroupOperation}, X) + +function Base.show(io::IO, G::GeneralLinearGroup{𝔽}) where {𝔽} + n = Manifolds.get_parameter(G.manifold.size)[1] + return print(io, "GeneralLinearGroup($n; field=$(𝔽))") +end diff --git a/src/interface.jl b/src/interface.jl index 2a93fcd4..45f79fe8 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -10,6 +10,25 @@ on elements of a Lie group ``$(_math(:G))``. """ abstract type AbstractGroupOperation end +""" + LieAlgebraOrthogonalBasis{𝔽} <: ManifoldsBase.AbstractOrthogonalBasis{𝔽,ManifoldsBase.TangentSpaceType} + +Specify an orthogonal basis for a Lie algebra. +This is used as the default within [`hat`](@ref) and [`vee`](@ref). + +If not specifically overwritten/implemented for a Lie group, the [`DefaultOrthogonalBasis`](@extref `ManifoldsBase.DefaultOrthogonalBasis`) +at the [`identity_element`](@ref) on the [`base_manifold](@ref base_manifold(::LieGroup)) acts as a fallback. + +!!! note + In order to implement the corresponding [`get_coordinates`](@ref) and [`get_vector`](@ref) functions, + define `get_coordiinates_lie(::LieGroup, p, X, N)` and `get_vector_lie(::LieGroup, p, X, N)`, resp. +""" +struct LieAlgebraOrthogonalBasis{𝔽} <: + ManifoldsBase.AbstractOrthogonalBasis{𝔽,ManifoldsBase.TangentSpaceType} end +function LieAlgebraOrthogonalBasis(𝔽::ManifoldsBase.AbstractNumbers=ℝ) + return LieAlgebraOrthogonalBasis{𝔽}() +end + """ LieGroup{𝔽, O<:AbstractGroupOperation, M<:AbstractManifold{𝔽}} @@ -68,6 +87,27 @@ Identity(::Type{O}) where {O<:AbstractGroupOperation} = Identity{O}() # # --- Functions --- +# Internal pass through for coordinates and vectors + +@inline function ManifoldsBase._get_coordinates( + G::LieGroup, p, X, B::LieAlgebraOrthogonalBasis +) + return get_coordinates_lie(G, p, X, number_system(B)) +end +@inline function ManifoldsBase._get_coordinates!( + G::LieGroup, Y, p, X, B::LieAlgebraOrthogonalBasis +) + return get_coordinates_lie!(G, Y, p, X, number_system(B)) +end +@inline function ManifoldsBase._get_vector(G::LieGroup, p, c, B::LieAlgebraOrthogonalBasis) + return get_vector_lie(G, p, c, number_system(B)) +end +@inline function ManifoldsBase._get_vector!( + G::LieGroup, Y, p, c, B::LieAlgebraOrthogonalBasis +) + return get_vector_lie!(G, Y, p, c, number_system(B)) +end + _doc_adjoint = """ adjoint(G::LieGroup, g X) adjoint!(G::LieGroup, Y, g, X) @@ -136,6 +176,8 @@ function ManifoldsBase.check_point( ) end +ManifoldsBase.check_size(G::LieGroup, ::Identity) = nothing + # compose g ∘ h _doc_compose = """ compose(G::LieGroup, g, h) @@ -379,6 +421,116 @@ function ManifoldsBase.exp!(G::LieGroup, h, e::Identity, X, t::Number=1) throw(MethodError(exp!, (typeof(G), typeof(h), typeof(e), typeof(X), typeof(t)))) end +_doc_get_coordinates = """ + get_coordinates(G::LieGroup, g, X, B::AbstractBasis) + get_coordinates(𝔤::LieAlgebra, X, B::AbstractBasis) + get_coordinates!(G::LieGroup, c, g, X, B::AbstractBasis) + get_coordinates!(𝔤::LieAlgebra, c, X, B::AbstractBasis) + +Return the vector of coordinates to the decomposition of `X` with respect to an [`AbstractBasis`](@extref `ManifoldsBase.AbstractBasis`) +of the [`LieAlgebra`](@ref) `𝔤`. +Since all tangent vectors are assumed to be represented in the Lie algebra, +both signatures are equivalent. +The operation can be performed in-place of `c`. + +By default this function requires [`identity_element`](@ref)`(G)` and calls +the corresponding [`get_coordinates`](@extref `ManifoldsBase.get_coordinates-Tuple{AbstractManifold, Any, Any, ManifoldsBase.AbstractBasis}`) function +of the Riemannian manifold the Lie group is build on. + +The inverse operation is [`get_vector`](@ref). + +See also [`vee`](@ref). +""" + +@doc "$(_doc_get_coordinates)" +ManifoldsBase.get_coordinates(G::LieGroup, g, X, B::ManifoldsBase.AbstractBasis) + +@doc "$(_doc_exp_id)" +ManifoldsBase.get_coordinates!(G::LieGroup, c, g, X, B::ManifoldsBase.AbstractBasis) + +function get_coordinates_lie(G::LieGroup, g, X, N) + return get_coordinates( + base_manifold(G), identity_element(G), X, ManifoldsBase.DefaultOrthogonalBasis(N) + ) +end +function get_coordinates_lie!(G::LieGroup, Y, g, X, N) + return get_coordinates!( + base_manifold(G), Y, identity_element(G), X, ManifoldsBase.DefaultOrthogonalBasis(N) + ) +end + +_doc_get_vector = """ + get_vector(G::LieGroup, g, c, B::AbstractBasis) + get_vector(𝔤::LieAlgebra, c, B::AbstractBasis) + get_vector!(G::LieGroup, X, g, c, B::AbstractBasis) + get_vector!(𝔤::LieAlgebra, X, c, B::AbstractBasis) + +Return the vector corresponding to a set of coefficients in an [`AbstractBasis`](@extref `ManifoldsBase.AbstractBasis`) +of the [`LieAlgebra`](@ref) `𝔤`. +Since all tangent vectors are assumed to be represented in the Lie algebra, +both signatures are equivalend. +The operation can be performed in-place of a tangent vector `X`. + +By default this function requires [`identity_element`](@ref)`(G)` and calls +the corresponding [`get_vector`](@extref ManifoldsBase.get_vector-Tuple{AbstractManifold, Any, Any, ManifoldsBase.AbstractBasis}) function +of the Riemannian manifold the Lie group is build on. + +The inverse operation is [`get_coordinates`](@ref). + +See also [`hat`](@ref) +""" + +@doc "$(_doc_get_vector)" +ManifoldsBase.get_vector(G::LieGroup, g, c, B::ManifoldsBase.AbstractBasis) + +@doc "$(_doc_exp_id)" +ManifoldsBase.get_vector!(G::LieGroup, X, g, c, B::ManifoldsBase.AbstractBasis) + +@inline function get_vector_lie(G::LieGroup, g, c, N) + return get_vector( + base_manifold(G), identity_element(G), c, ManifoldsBase.DefaultOrthogonalBasis(N) + ) +end +@inline function get_vector_lie!(G::LieGroup, Y, g, c, N) + return get_vector!( + base_manifold(G), Y, identity_element(G), c, ManifoldsBase.DefaultOrthogonalBasis(N) + ) +end + +_doc_hat = """ + hat(G::LieGroup, c) + hat!(G::LieGroup, X, c) + +Compute the hat map ``(⋅)^̂ `` that maps a vector of coordinates ``c_i`` +with respect to a certain basis to a tangent vector in the Lie algebra + +```math +X = $(_tex(:sum))_{i∈$(_tex(:Cal,"I"))} c_iB_i, +``` + +where ``$(_tex(:Set, "B_i"))_{i∈$(_tex(:Cal,"I"))}`` is a basis of the Lie algebra +and ``$(_tex(:Cal,"I"))`` a corresponding index set, which is usually ``$(_tex(:Cal,"I"))=$(_tex(:Set,raw"1,\ldots,n"))``. + +The computation can be performed in-place of `X`. +The inverse of `hat` is [`vee`](@ref). + +Technically, `hat` is a specific case of [`get_vector`](@ref) and is implemented using the +[`LieAlgebraOrthogonalBasis`](@ref) +""" + +# function hat end +@doc "$(_doc_hat)" +function hat(G::LieGroup{𝔽}, c) where {𝔽} + return get_vector_lie(G, Identity(G), c, 𝔽) +end + +# function hat! end +@doc "$(_doc_hat)" +function hat!(G::LieGroup{𝔽}, X, c) where {𝔽} + get_vector_lie!(G, X, Identity(G), c, 𝔽) + return X +end + _doc_identity_element = """ identity_element(G::LieGroup) identity_element!(G::LieGroup, e) @@ -421,6 +573,10 @@ function inv! end @doc "$_doc_inv" inv!(G::LieGroup, h, g) +function Base.inv(::LieGroup{𝔽,O}, e::Identity{O}) where {𝔽,O<:AbstractGroupOperation} + return e +end + _doc_inv_left_compose = """ inv_left_compose(G::LieGroup, g, h) inv_left_compose!(G::LieGroup, k, g, h) @@ -502,7 +658,7 @@ end Check whether `g` is a valid point on the Lie Group `G`. This falls back to checking whether `g` is a valid point on `G.manifold`, unless `g` is an [`Identity`](@ref). Then, it is checked whether it is the -idenity element corresponding to `G`. +identity element corresponding to `G`. """ ManifoldsBase.is_point(G::LieGroup, g; kwargs...) @@ -617,7 +773,41 @@ function ManifoldsBase.log!(G::LieGroup, X, e::Identity, g) throw(MethodError(ManifoldsBase.log!, (typeof(G), typeof(X), typeof(e), typeof(g)))) end -LinearAlgebra.norm(G::LieGroup, g, X) = norm(G.manifold, g, X) +ManifoldsBase.manifold_dimension(G::LieGroup) = manifold_dimension(G.manifold) + +ManifoldsBase.norm(G::LieGroup, g, X) = norm(G.manifold, g, X) + +_doc_rand = """ + rand(::LieGroup; vector_at=nothing, σ::Real=1.0, kwargs...) + rand(::LieAlgebra; σ::Real=1.0, kwargs...) + rand!(::LieGroup, gX; vector_at=nothing, σ::Real=1.0, kwargs...) + rand!(::LieAlgebra, X; σ::Real=1.0, kwargs...) + +Compute a random point or tangent vector on a Lie group. + +For points this just means to generate a random point on the +underlying manifold itself. + +For tangent vectors, an element in the Lie Algebra is generated, +see also [`rand(::LieAlgebra; kwargs...)`](@ref) +""" + +@doc "$(_doc_rand)" +Random.rand(::LieGroup; kwargs...) + +@doc "$(_doc_rand)" +function Random.rand!(G::LieGroup, pX; kwargs...) + return rand!(Random.default_rng(), G, pX; kwargs...) +end + +function Random.rand!(rng::AbstractRNG, G::LieGroup, pX; vector_at=nothing, kwargs...) + M = base_manifold(G) + if vector_at === nothing # for points -> pass to manifold + rand!(rng, M, pX, kwargs...) + else # for tangent vectors -> materialize identity, pass to tangent space there. + rand!(rng, M, pX; vector_at=identity_element(G), kwargs...) + end +end function ManifoldsBase.representation_size(G::LieGroup) return representation_size(G.manifold) @@ -627,6 +817,41 @@ function Base.show(io::IO, G::LieGroup) return print(io, "LieGroup($(G.manifold), $(G.op))") end +_doc_vee = """ + vee(G::LieGroup, X) + vee!(G::LieGroup, c, X) + +Compute the vee map ``(⋅)^∨`` that maps a tangent vector `X` from the [`LieAlgebra`](@ref) +to its coordinates with respect to the [`LieAlgebraOrthogonalBasis`](@ref) basis in the Lie algebra + +```math +X = $(_tex(:sum))_{i∈$(_tex(:Cal,"I"))} c_iB_i, +``` + +where ``$(_tex(:Set, "B_i"))_{i∈$(_tex(:Cal,"I"))}`` is a basis of the Lie algebra +and ``$(_tex(:Cal,"I"))`` a corresponding index set, which is usually ``$(_tex(:Cal,"I"))=$(_tex(:Set,raw"1,\ldots,n"))``. + +The computation can be performed in-place of `c`. +The inverse of `vee` is [`hat`](@ref). + +Technically, `vee` is a specific case of [`get_coordinates`](@ref) and is implemented using +the [`LieAlgebraOrthogonalBasis`](@ref) + +""" + +# function vee end +@doc "$(_doc_vee)" +function vee(G::LieGroup{𝔽}, X) where {𝔽} + return get_coordinates_lie(G, Identity(G), X, 𝔽) +end + +# function vee! end +@doc "$(_doc_vee)" +function vee!(G::LieGroup{𝔽}, c, X) where {𝔽} + get_coordinates_lie!(G, c, Identity(G), X, 𝔽) + return c +end + function ManifoldsBase.zero_vector( G::LieGroup{𝔽,O}, ::Identity{O} ) where {𝔽,O<:AbstractGroupOperation} diff --git a/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl index 2344b11e..8982496e 100644 --- a/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl +++ b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl @@ -13,7 +13,7 @@ The following functions are expected to be available, since their defaults just """ module LieGroupsTestSuite using LieGroups -using Test +using Test, Random # # @@ -436,9 +436,73 @@ end # # -# --- I +# --- H """ - test_inv_compose(G::LieGroup, g, h, X) + test_hat_vee(G::LieGroup, g, X; kwargs...) + +Test `hat` and `vee` for given Lie group element `g` and a Lie Algebra vector `X`. + +# Keyword arguments + +* `expected_value=missing`: the expected value of `vee(G,X)` +* `test_mutating::Bool=true`: test the mutating functions +* `test_vee::Bool=true`: test the vee function +* `test_hat::Bool=true`: test the hat function +""" +function test_hat_vee( + G::LieGroup, + g, + X; + test_mutating::Bool=true, + test_vee::Bool=true, + test_hat::Bool=true, + expected_value=missing, +) + @testset "hat & vee" begin + 𝔤 = LieAlgebra(G) + if test_hat + c = ismissing(expected_value) ? zeros(manifold_dimension(G)) : expected_value + Y1 = hat(G, c) + @test is_vector(G, g, Y1) + ismissing(expected_value) && @test norm(G, g, Y1) ≈ 0 + !ismissing(expected_value) && @test isapprox(𝔤, X, Y1) + if test_mutating + Y2 = zero_vector(𝔤) + hat!(G, Y2, c) + @test isapprox(𝔤, Y1, Y2) + end + end + if test_vee + c1 = vee(G, X) + if test_mutating + c2 = zero(c1) + vee!(G, c2, X) + @test c1 ≈ c2 + end + if !ismissing(expected_value) + @test c1 ≈ expected_value + end + end + if test_hat && test_vee + Y1 = hat(G, vee(G, X)) + @test isapprox(𝔤, X, Y1) + if test_mutating + Y2 = zero_vector(𝔤) + c = zeros(manifold_dimension(G)) + vee!(G, c, X) + hat!(G, Y2, c) + @test isapprox(𝔤, Y1, Y2) + end + end + end + return nothing +end + +# +# +# --- `I` +""" + test_inv_compose(G::LieGroup, g, h, X; kwargs...) Test the special functions combining inv and compose, `inv_left_compose` and `inv_right_compose`. For these tests both `compose` and `inv` are required. @@ -496,6 +560,43 @@ function test_inv_compose( return nothing end +""" + test_inv(G::LieGroup, g) + +Test the inverse function, both the allocating and the in-place variant, +and that the double inverse is the identity. + +# Keyword arguments + +* `test_mutating::Bool=true`: test the mutating functions +* `test_identity::Bool=true`: test that `inv(e) == e` +""" +function test_inv(G::LieGroup, g; test_mutating::Bool=true, test_identity::Bool=true) + @testset "inv" begin + k1 = inv(G, g) + @test is_point(G, k1) + g1 = inv(G, k1) + @test isapprox(G, g, g1) + if test_mutating + k2 = copy(G, g) + inv!(G, k2, g) + @test isapprox(G, k1, k2) + # continue in-place + inv!(G, k2, k2) + @test isapprox(G, k2, g) + end + if test_identity + e = Identity(G) + @test inv(G, e) === e + if test_mutating + e2 = copy(G, g) + inv!(G, e2, e) + @test is_identity(G, e2) + end + end + end + return nothing +end # # # --- L @@ -524,6 +625,60 @@ function test_lie_bracket(G::LieGroup, X, Y; expected=missing, test_mutating::Bo end end +# +# +# --- R +""" + test_rand(G::LieGroup) + +Test the random function, both the allocating and the in-place variant, +as well as the variant with an `rng`, if one is provided. + +both the random point and the random tangent vector variants are tested. + +# Keyword arguments + +* `test_mutating::Bool=true`: test the mutating functions +* `rng=missing`: test with a specific random number generator +""" +function test_rand( + G::LieGroup, g; test_mutating::Bool=true, rng::Union{Missing,AbstractRNG}=missing +) + @testset "rand" begin + g1 = rand(G) + @test is_point(G, g1) + if test_mutating + g2 = copy(G, g) + rand!(G, g2) + @test is_point(G, g2) + end + X1 = rand(G; vector_at=g1) + @test is_vector(G, g1, X1) + if test_mutating + X2 = zero_vector(LieAlgebra(G), g1) + rand!(G, X2; vector_at=g1) + @test is_vector(G, g1, X2) + end + if !ismissing(rng) + g1 = rand(rng, G) + @test is_point(G, g1) + if test_mutating + g2 = copy(G, g) + rand!(rng, G, g2) + @test is_point(G, g2) + end + X1 = rand(rng, G; vector_at=g1) + @test is_vector(G, g1, X1) + if test_mutating + X2 = zero_vector(LieAlgebra(G), g1) + rand!(rng, G, X2; vector_at=g1) + @test is_vector(G, g1, X2) + end + end + end + return nothing +end + # # # --- S @@ -533,7 +688,7 @@ end Test that show methods work as expected. For now this (only) checks that `"\$G"` yields the `repr_string`. -requires `show` (or `repr`) to be implemented. +Requires `show` (or `repr`) to be implemented. """ function test_show(G::Union{AbstractGroupAction,LieGroup}, repr_string::AbstractString) @testset "repr(G, g, h)" begin @@ -559,6 +714,7 @@ Possible properties are * `:Vectors` is a vector of at least 3 elements from the Lie algebra `𝔤` og `G` * `:Mutating` is a boolean (`true` by default) whether to test the mutating variants of functions or not. * `:Name` is a name of the test. If not provided, defaults to `"\$G"` +* `:Rng` is a random number generator, if provided, the random functions are tested with this generator as well Possible `expectations` are @@ -571,6 +727,7 @@ Possible `expectations` are * `:inv_left_compose` for the result of `inv_left_right_compose` with respect to the first two points * `:inv_right_compose` for the result of `inv_right_compose` with respect to the first two points * `:repr` is a sting one gets from `repr(G)` +* `:vee` for the result of `vee(G, X)` where `X` is the first of the vectors """ function test_lie_group(G::LieGroup, properties::Dict, expectations::Dict=Dict()) a_tol = get(expectations, :atol, 1e-8) @@ -643,6 +800,22 @@ function test_lie_group(G::LieGroup, properties::Dict, expectations::Dict=Dict() ) end + # + # + # --- H + if any(in.([hat, vee], Ref(functions))) + v = get(expectations, :vee, missing) + test_hat_vee( + G, + points[1], + vectors[1]; + expected_value=v, + test_hat=(hat in functions), + test_mutating=mutating, + test_vee=(vee in functions), + ) + end + # # # --- `I` @@ -660,7 +833,9 @@ function test_lie_group(G::LieGroup, properties::Dict, expectations::Dict=Dict() test_right=(inv_right_compose in functions), ) end - # + if (inv in functions) + test_inv(G, points[1]; test_mutating=mutating) + end # # # --- L if (lie_bracket in functions) @@ -668,6 +843,14 @@ function test_lie_group(G::LieGroup, properties::Dict, expectations::Dict=Dict() test_lie_bracket(G, vectors[1], vectors[2]; expected=v, test_mutating=mutating) end + # + # + # --- R + if (rand in functions) + v = get(properties, :Rng, missing) + test_rand(G, points[1]; rng=v, test_mutating=mutating) + end + # # # --- S diff --git a/test/groups/test_general_linear_group.jl b/test/groups/test_general_linear_group.jl new file mode 100644 index 00000000..490440f7 --- /dev/null +++ b/test/groups/test_general_linear_group.jl @@ -0,0 +1,73 @@ +using LieGroups, Random, Test + +using ManifoldsBase: ℂ + +s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +@testset "GL(2)" begin + G = GeneralLinearGroup(2) + g1, g2, g3 = [2.0 0.0; 0.0 1.0], [1.0 0.5; 0.5 1.0], [1.0 2.0; 3.0 4.0] + X1, X2, X3 = [-1.0 0.0; 0.0 0.0], [0.0 0.5; 0.5 0.0], [1.0 2.0; 3.0 4.0] + properties = Dict( + :Name => "The general linear group", + :Points => [g1, g2, g3], + :Vectors => [X1, X2, X3], + :Rng => Random.MersenneTwister(), + :Functions => [ + compose, + conjugate, + diff_conjugate, + diff_inv, + diff_left_compose, + diff_right_compose, + exp, + # hat, # requires a fix in Manifolds.jl to have an ONB on invertible matrices + inv, + inv_left_compose, + inv_right_compose, + is_identity, + lie_bracket, + log, + #rand, # requires a fix in Manifolds.jl to have rand on invertible matrices + show, + #vee, # requires a fix in Manifolds.jl to have an ONB on invertible matrices + ], + ) + expectations = Dict( + :repr => "GeneralLinearGroup(2; field=ℝ)", :lie_bracket => X1 * X2 - X2 * X1 + ) + test_lie_group(G, properties, expectations) + + @test is_point(G, Identity(G); error=:error) + @test_throws DomainError is_point(G, Identity(AdditionGroupOperation()); error=:error) +end + +@testset "GL(1, 𝔽) special cases" begin + @testset "real" begin + G = GeneralLinearGroup(1) + e = Identity(G) + p = 3.0 * ones(1, 1) + X = 1.0 * ones(1, 1) + @test exp(G, p, X) ≈ p * exp(X)' * exp(X - X') + q = exp(G, p, X) + Y = log(G, p, q) + @test Y ≈ X + @test exp(G, e, X) ≈ exp(X) + @test log(G, e, exp(X)) ≈ X + log(G, e, Identity(G)) == zeros(1, 1) # Matrix to matrix + end + @testset "complex" begin + G = GeneralLinearGroup(1; field=ℂ) + e = Identity(G) + p = (1 + im) * ones(1, 1) + X = (1 - im) * ones(1, 1) + @test exp(G, p, X) ≈ p * exp(X)' * exp(X - X') + q = exp(G, p, X) + Y = log(G, p, q) + @test Y ≈ X + @test exp(G, e, X) ≈ exp(X) + @test log(G, e, exp(X)) ≈ X + end +end diff --git a/test/groups/test_translation_group.jl b/test/groups/test_translation_group.jl index a3499185..9ff55e41 100644 --- a/test/groups/test_translation_group.jl +++ b/test/groups/test_translation_group.jl @@ -1,4 +1,4 @@ -using LieGroups, Test +using LieGroups, Random, Test s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") !(s in LOAD_PATH) && (push!(LOAD_PATH, s)) @@ -6,13 +6,13 @@ using LieGroupsTestSuite begin G = TranslationGroup(3) - # Later maybe via auto-discover? g1, g2, g3 = [1.0, 0.0, 0.0], [0.0, 3.0, 0.0], [1.1, 1.2, 3.3] X1, X2, X3 = [0.0, 1.0, 0.0], [2.0, 0.0, 0.0], [0.1, 0.2, 0.3] properties = Dict( :Name => "The Translation group", :Points => [g1, g2, g3], :Vectors => [X1, X2, X3], + :Rng => Random.MersenneTwister(), :Functions => [ adjoint, compose, @@ -21,6 +21,7 @@ begin diff_left_compose, diff_right_compose, exp, + hat, identity_element, inv, inv_left_compose, @@ -28,7 +29,9 @@ begin is_identity, lie_bracket, log, + rand, show, + vee, ], ) expectations = Dict( diff --git a/test/operations/test_multiplication_operation.jl b/test/operations/test_multiplication_operation.jl new file mode 100644 index 00000000..846cb87b --- /dev/null +++ b/test/operations/test_multiplication_operation.jl @@ -0,0 +1,50 @@ +using LieGroups, LinearAlgebra, Test + +s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +@testset "Multiplication Operation" begin + @testset "Base.:* and Base.:* with the Identity" begin + # Generic & Number + e = Identity(MatrixMultiplicationGroupOperation()) + @test (e * e) === e + g = 2 + @test (g * e) == g + @test (e * g) == g + @test (g / e) == g + @test (e \ g) == g + @test (e / g) == 1 / g + @test (g \ e) == 1 / g + @test (e / e) === e + @test (e \ e) === e + @test inv(e) === e + @test det(e) + ea = Identity(AdditionGroupOperation) + @test ea * e === e + @test e * ea === e + M = LieGroupsTestSuite.DummyManifold() + G = LieGroup(M, MatrixMultiplicationGroupOperation()) + # Zero-dimensional array + g2 = fill(2.0, ()) + X2 = fill(1.0, ()) + @test diff_inv(G, g2, X2) == fill(-1.0, ()) + # Array + g3 = [2.0 0.0; 1.0 2.0] + @test inv(G, e) === e + h3 = zero(g3) + X3 = [1.0 0.0; 0.0 2.0] + @test diff_conjugate(G, g3, g3, X3) == g3 * X3 * inv(g3) + # inplace-multiplication with e + h3 = zero(g3) + mul!(h3, e, g3) + @test h3 == g3 + h3 = zero(g3) + mul!(h3, g3, e) + @test h3 == g3 + mul!(h3, e, e) + @test h3 == I + @test mul!(e, e, e) === e + @test one(e) === e + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 21773cae..bd027224 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,12 +15,14 @@ end end @testset "Generic Group Operations" begin include_test("operations/test_addidion_operation.jl") + include_test("operations/test_multiplication_operation.jl") end @testset "Generic Group Actions" begin include_test("actions/test_action_interface.jl") include_test("actions/test_operation_action.jl") end @testset "Lie Groups" begin + include_test("groups/test_general_linear_group.jl") include_test("groups/test_translation_group.jl") end include("test_aqua.jl") diff --git a/test/test_aqua.jl b/test/test_aqua.jl index cb44ecec..9cce68d1 100644 --- a/test/test_aqua.jl +++ b/test/test_aqua.jl @@ -1,8 +1,18 @@ -using Aqua, LieGroups, Test +using Aqua, LieGroups, Test, LinearAlgebra @testset "Aqua.jl" begin - Aqua.test_all(LieGroups; ambiguities=(broken=false, exclude=[ - Base.:+, #temporary ambiguities between Manifolds.Identity and LieGroups.Identity - Base.:-, #temporary ambiguities between Manifolds.Identity and LieGroups.Identity - ])) + Aqua.test_all( + LieGroups; + ambiguities=( + broken=false, + exclude=[ + Base.:+, #temporary ambiguities between Manifolds.Identity and LieGroups.Identity + Base.:-, #temporary ambiguities between Manifolds.Identity and LieGroups.Identity + Base.:*, #temporary ambiguities between Manifolds.Identity and LieGroups.Identity + Base.:/, #temporary ambiguities between Manifolds.Identity and LieGroups.Identity + Base.:\, #temporary ambiguities between Manifolds.Identity and LieGroups.Identity + LinearAlgebra.mul!, #temporary ambiguities between Manifolds.Identity and LieGroups.Identity + ], + ), + ) end diff --git a/test/test_interface.jl b/test/test_interface.jl index 442389d2..bfcf128e 100644 --- a/test/test_interface.jl +++ b/test/test_interface.jl @@ -1,4 +1,4 @@ -using LieGroups, Test +using LieGroups, Test, ManifoldsBase s = joinpath(@__DIR__, "LieGroupsTestSuite.jl") !(s in LOAD_PATH) && (push!(LOAD_PATH, s)) @@ -25,9 +25,70 @@ using LieGroupsTestSuite @test is_point(G, e) @test !is_point(G, Identity(op2)) @test_throws DomainError is_point(G, Identity(op2); error=:error) - # Exp log Method Error fallbacks that avoid the stack overflow - g = :none - X = :nonetoo - @test_throws MethodError exp!(G, g, e, X) - @test_throws MethodError log!(G, X, e, g) + @testset "Methoderrors for the non-implemented mutating variants" begin + g = :none + h = :alsonone + X = :nonetoo + begin # locally define identity element + LieGroups.identity_element(::typeof(G)) = :id + LieGroups.exp!(::typeof(G), h, ::typeof(e), X, t::Number=1) = :id + @test exp(G, e, X) === :id + # delete both methods again + Base.delete_method(which(identity_element, (typeof(G),))) + Base.delete_method(which(exp!, typeof.([G, h, e, X, 1]))) + # + # same for log + ManifoldsBase.allocate_result(::typeof(G), ::typeof(log), g) = :g + LieGroups.log!(::typeof(G), X, ::Identity, g) = :g + @test log(G, e, g) === :g + # delete both methods again + Base.delete_method(which(ManifoldsBase.allocate_result, typeof.([G, log, g]))) + Base.delete_method(which(log!, typeof.([G, X, e, g]))) + end + # so they arae undefined here again but we checked the exp fallback + @test_throws MethodError exp!(G, g, e, X) + @test_throws MethodError log!(G, X, e, g) + end + @testset "Generic get_coordinates/get_vector passthrough on 𝔤" begin + M = ManifoldsBase.DefaultManifold(2) + op = AdditionGroupOperation() + G = LieGroup(M, op) + B2 = LieAlgebraOrthogonalBasis() + B = DefaultOrthonormalBasis() + p = [1.0, 2.0] + q = [0.0, 0.0] + # coordinates and vector on 𝔤 are here the same as the ones on M at 0 + X = [1.0, 0.0] + @test get_coordinates(G, q, X, B2) == get_coordinates(M, q, X, B) + Y = copy(X) + @test get_coordinates!(G, q, Y, X, B2) == get_coordinates!(M, Y, q, X, B) + @test X == Y + c = [0.0, 1.0] + @test get_vector(G, q, c, B2) == get_vector(M, q, c, B) + d = copy(c) + @test get_vector!(G, q, d, c, B2) == get_vector!(M, d, q, c, B) + @test c == d + end +end +@testset "Generic Lie Algebra Interface functions" begin + @testset "Generic get_coordinates/get_vector passthrough on 𝔤" begin + M = ManifoldsBase.DefaultManifold(2) + op = AdditionGroupOperation() + G = LieGroup(M, op) + 𝔤 = LieAlgebra(G) + B = DefaultOrthonormalBasis() + p = [1.0, 2.0] + q = [0.0, 0.0] + # coordinates and vector on 𝔤 are here the same as the ones on M at 0 + X = [1.0, 0.0] + @test get_coordinates(𝔤, X, B) == get_coordinates(M, q, X, B) + Y = copy(X) + @test get_coordinates!(𝔤, Y, X, B) == get_coordinates!(M, Y, q, X, B) + @test X == Y + c = [0.0, 1.0] + @test get_vector(𝔤, c, B) == get_vector(M, q, c, B) + d = copy(c) + @test get_vector!(𝔤, d, c, B) == get_vector!(M, d, q, c, B) + @test c == d + end end