diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0c498a..594fc01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - julia-version: ["1.6", "1.10", "~1.11.0-0"] + julia-version: ["1.6", "1.10", "1.11"] os: [ubuntu-latest, macOS-latest] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/documenter.yml b/.github/workflows/documenter.yml index 46b2e4e..5f3076f 100644 --- a/.github/workflows/documenter.yml +++ b/.github/workflows/documenter.yml @@ -17,7 +17,7 @@ jobs: version: "1.4.551" - uses: julia-actions/setup-julia@latest with: - version: "1.10" + version: "1.11" - name: Julia Cache uses: julia-actions/cache@v2 - name: Cache Quarto diff --git a/.gitignore b/.gitignore index a21ad5b..f3b7d6b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ temp tutorials/*.ipynb tutorials/*_files/* tutorials/_freeze +.vscode/settings.json diff --git a/.vale.ini b/.vale.ini new file mode 100644 index 0000000..4b5150c --- /dev/null +++ b/.vale.ini @@ -0,0 +1,19 @@ +StylesPath = docs/styles +MinAlertLevel = warning +Vocab = LieGroups + +MinAlertLevel = suggestion + +Packages = Google, write-good + +[*.md] +BasedOnStyles = Vale, Google, write-good + +[NEWS.md, CONTRIBUTING.md] +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/.zenodo.json b/.zenodo.json index 853dcff..e1b600b 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,17 +1,36 @@ { + "creators": [ + { + "affiliation": "University of California, San Francisco", + "name": "Axen, Seth", + "orcid": "0000-0003-3933-8247" + }, + { + "affiliation": "AGH University of Science and Technology", + "name": "Baran, Mateusz", + "orcid": "0000-0001-9667-5579" + }, { "affiliation": "NTNU Trondheim", "name": "Bergmann, Ronny", "orcid": "0000-0001-8342-7218" + }, + { + "name": "Tu, Yueh-Hua" + }, + { + "affiliation": "KTH Royal Institute of Technology", + "name": "Verdier, Olivier", + "orcid": "0000-0003-3699-6244" } ], - "description": "LieGroups.jl provides a library of Lie groups.", + "description": "LieGroups.jl is a library of Lie groups, Lie algebras", "keywords": [ - "Riemannian manifolds", - "Lie groups", - "Julia", - "Programming" + "Lie group", + "Lie algebra", + "manifold", + "Julia" ], "license": "MIT", "title": "LieGroups.jl", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5bba87..527fbe7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,53 +1,51 @@ # Contributing to `LieGroups.jl` First, thanks for taking the time to contribute. -Any contribution is appreciated and welcome. +We appreciate and welcome any contribution. The following is a set of guidelines to [`LieGroups.jl`](https://juliamanifolds.github.io/LieGroups.jl/). #### Table of contents -- [Contributing to `LieGroups.jl`](#Contributing-to-manoptjl) +- [Contributing to `LieGroups.jl`](#Contributing-to-liegroupsjl) - [Table of Contents](#Table-of-Contents) - - [I just have a question](#I-just-have-a-question) - - [How can I file an issue?](#How-can-I-file-an-issue) - - [How can I contribute?](#How-can-I-contribute) + - [How to just ask a question](#How-to-ask-a-question) + - [How to file an issue](#How-to-file-an-issue) + - [How to contribute](#How-to-contribute) - [Code style](#Code-style) -## I just have a question +## How to just ask a question -The developer can most easily be reached in the Julia Slack channel [#manifolds](https://julialang.slack.com/archives/CP4QF0K5Z). +You can most easily reach the developers in the Julia Slack channel [#manifolds](https://julialang.slack.com/archives/CP4QF0K5Z). You can apply for the Julia Slack workspace [here](https://julialang.org/slack/) if you haven't joined yet. You can also ask your question on [discourse.julialang.org](https://discourse.julialang.org). -## How can I file an issue? +## How to file an issue If you found a bug or want to propose a feature, please open an issue in within the [GitHub repository](https://github.com/JuliaManifolds/LieGroups.jl/issues). -## How can I contribute? +## How to contribute -Currently we are still consolidating most details and defaults, but feel free to contribute to these discussions within this repository. +Currently most details are still work-in-progress. +Feel free to contribute ideas, features you would like to see, Lie groups you want to have or would like to contribute, or any other idea for `LieGroups.jl`. For these, use either the [discussions](https://github.com/JuliaManifolds/LieGroups.jl/discussions) or [issues](https://github.com/JuliaManifolds/LieGroups.jl/issues) in the [GitHub repository](https://github.com/JuliaManifolds/LieGroups.jl) ## Code style -Try to follow the [documentation guidelines](https://docs.julialang.org/en/v1/manual/documentation/) from the Julia documentation as well as [Blue Style](https://github.com/invenia/BlueStyle). -Run [`JuliaFormatter.jl`](https://github.com/domluna/JuliaFormatter.jl) on the repository in the way set in the `.JuliaFormatter.toml` file, which enforces a number of conventions consistent with the Blue Style. +Please follow the [documentation guidelines](https://docs.julialang.org/en/v1/manual/documentation/) from the Julia documentation as well as [Blue Style](https://github.com/invenia/BlueStyle). +Run [`JuliaFormatter.jl`](https://github.com/domluna/JuliaFormatter.jl) on the repository running `using JuliaFormatter; format(".")` on the main folder of the project. Please follow a few internal conventions: -- Any implemented function should be accompanied by its mathematical formulae if a closed form exists. -- A Lie group, a Lie group action or a Lie algebra should be implemented in it own file +- Include the mathematical formulae for any implemented function if a closed form exists. +- Define a Lie group, a Lie group action, or a Lie algebra in its own file. Include all related functions in the same file - an alphabetical order of functions in every file is preferable. - The preceding implies that the mutating variant of a function follows the non-mutating variant. -- usually, both the allocating and the mutating variants of a function should be documented. For avoiding duplication, one doc string attached to both is preferrable +- Document both the allocating and the mutating variants of a function. To avoid duplication, attach one doc string defined before both functions and attach it to both. - There should be no dangling `=` signs. -- Always add a newline between things of different types (struct/method/const). -- Always add a newline between methods for different functions (including allocating/mutating variants). +- Add a newline between things of different types (struct/method/const). +- Add a newline between methods for different functions (including allocating/mutating variants). - Prefer to have no newline between methods for the same function; when reasonable, merge the documentation strings. - All `import`/`using`/`include` should be in the main module file. -- `import` is discouraged and the explicit full name, like `Base.exp` should be used when implementing functions - -Concerning documentation - +- Avoid using `import` and use the explicit full name, like `Base.exp`, when implementing functions, that extend functions of other packages. - if possible provide both mathematical formulae and literature references using [DocumenterCitations.jl](https://juliadocs.org/DocumenterCitations.jl/stable/) and BibTeX where possible - Always document all input variables and keyword arguments \ No newline at end of file diff --git a/NEWS.md b/NEWS.md index f4a7f5b..172971f 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/). @@ -15,7 +15,7 @@ Everything denoted by “formerly” refers to the previous name in [`Manifolds. * `LieGroup` (formerly `GroupManifold`) as well as the concrete groups * `AdditiveGroup` (formerly `TranslationGroup`) * `AbstractGroupOperation` as well as its concrete subtypes - * `AdditiveGroupOperation` + * `AdditionGroupOperation` (formerly `AdditionOperation`) * `AbstractGroupActionType` with its 2 specific (new) abstract * `AbstractLeftGroupActionType` * `AbstractRightGroupActionType` @@ -24,8 +24,6 @@ Everything denoted by “formerly” refers to the previous name in [`Manifolds. * `RightGroupOperation` (formerly `RightBackwardAction`) * `InverseLeftGroupOperation` (formerly `RightForwardAction`) * `InverseRightGroupOperation` (formerly `LeftBackwardAction`) -* `LieGroups.AbstractGroupOperation` as well as its concrete subtypes - * `AdditiveGroupOperation` * `apply`and `apply!` * `base_manifold` to access the manifold within a Lie group * `compose` and `compose!` diff --git a/Project.toml b/Project.toml index f76dd3f..d929ecf 100644 --- a/Project.toml +++ b/Project.toml @@ -4,16 +4,22 @@ authors = ["Seth Axen ", "Mateusz Baran "index.md", "About" => "about.md", (tutorials_in_menu ? [tutorials_menu] : [])..., - "An Interface for Lie Groups" => "interface.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", - "Additive group" => "groups/additive.md", + "Translation group" => "groups/translation.md", ], "Contributing to LieGroups.jl" => "contributing.md", "Notation" => "notation.md", diff --git a/docs/src/about.md b/docs/src/about.md index cd19752..2ee6090 100644 --- a/docs/src/about.md +++ b/docs/src/about.md @@ -1 +1,4 @@ -# About LieGroups.jl \ No newline at end of file +# About LieGroups.jl + +`LieGroups.jl` is a “spin-off” from [`Manifolds.jl](https://juliamanifolds.github.io/Manifolds.jl/stable/), where `GroupManifolds` where implemented around 2021. +Around the same time, [Yueh-Hua Tu](https://github.com/yuehhua) started a package `LieGroups.jl`, which was continued than here with a full rewrite to use the manifolds from `Manifolds.jl`. diff --git a/docs/src/groups/additive.md b/docs/src/groups/additive.md deleted file mode 100644 index b0e1b9d..0000000 --- a/docs/src/groups/additive.md +++ /dev/null @@ -1,13 +0,0 @@ -# Additive Group - -```@docs -AdditiveGroup -``` - -## Functions - -```@autodocs -Modules = [LieGroups] -Pages = ["groups/additive.jl"] -Order = [:function] -``` diff --git a/docs/src/groups/index.md b/docs/src/groups/index.md index 858ebd3..270641c 100644 --- a/docs/src/groups/index.md +++ b/docs/src/groups/index.md @@ -1,5 +1,7 @@ -# Alphabetical List of Lie groups +# An overview of Lie groups + +# Alphabetical list of Lie groups | Group | Manifold | ``∘`` | Comment | |:------|:---------|:---------:|:------| -| [`AdditiveGroup`](@ref) | [`Euclidean`](@extref `Manifolds.Euclidean`) | [`+`](@ref AdditiveGroupOperation) | | \ No newline at end of file +| [`TranslationGroup`](@ref) | [`Euclidean`](@extref `Manifolds.Euclidean`) | [`+`](@ref AdditionGroupOperation) | | \ No newline at end of file diff --git a/docs/src/groups/translation.md b/docs/src/groups/translation.md new file mode 100644 index 0000000..1c20efd --- /dev/null +++ b/docs/src/groups/translation.md @@ -0,0 +1,7 @@ +# The Lie group of translations on a Euclidean space + +```@docs +TranslationGroup +``` + +For this Lie group, all implementations are already covered by the defaults in [the generic addition operation](@ref addition-operation-sec). diff --git a/docs/src/interface.md b/docs/src/interface.md deleted file mode 100644 index 9208170..0000000 --- a/docs/src/interface.md +++ /dev/null @@ -1,105 +0,0 @@ -# The interface for Lie Groups - -```@docs -LieGroup -LieAlgebra -``` - -## Group Operations - -```@docs -AbstractGroupOperation -Identity -``` - -## Properties - -```@docs -LieGroups.AbstractInvarianceTrait -LieGroups.HasBiinvariantMetric -LieGroups.HasLeftInvariantMetric -LieGroups.HasRightInvariantMetric -``` - -## Functions on Lie Groups - -```@docs -adjoint -adjoint! -base_manifold -compose -compose! -diff_left_compose -diff_left_compose! -diff_right_compose -diff_right_compose! -inv_left_compose -inv_left_compose! -inv_right_compose -inv_right_compose! -conjugate -conjugate! -diff_conjugate -diff_conjugate! -exp -exp! -identity_element -identity_element! -is_identity -inv(::LieGroup, ::Any) -inv! -diff_inv -diff_inv! -log -log! -``` - -## Actions on Lie groups - -```@autodocs -Modules = [LieGroups] -Pages = ["group_action_interface.jl"] -Order = [:type] -``` - -### Functions for Lie group actions - -```@autodocs -Modules = [LieGroups] -Pages = ["group_action_interface.jl"] -Order = [:function] -``` - -### Specific Lie group actions - -```@autodocs -Modules = [LieGroups] -Pages = ["group_operation_action.jl"] -Order = [:type, :function] -``` - -## Functions on Lie Algebras - -```@docs -lie_bracket -lie_bracket! -``` - -## Specific Group Operations - -For some generic group operations, an easy generic implementation can be provided. This section lists these specific cases and what is implemented for these. - -### Additive Group operation - -```@docs -AdditiveGroupOperation -``` - -### Multiplicative Group operation - -## Literature - -```@bibliography -Pages = ["interface.md"] -Canonical=false -``` diff --git a/docs/src/interface/actions.md b/docs/src/interface/actions.md new file mode 100644 index 0000000..32057b3 --- /dev/null +++ b/docs/src/interface/actions.md @@ -0,0 +1,30 @@ +# An Interface for Lie group actions + +```@autodocs +Modules = [LieGroups] +Pages = ["group_action_interface.jl"] +Order = [:type] +``` + +## Functions for Lie group actions + +```@autodocs +Modules = [LieGroups] +Pages = ["group_action_interface.jl"] +Order = [:function] +``` + +# Generic Lie group actions + +```@autodocs +Modules = [LieGroups] +Pages = ["group_operation_action.jl"] +Order = [:type, :function] +``` + +## Literature + +```@bibliography +Pages = ["actions.md"] +Canonical=false +``` \ No newline at end of file diff --git a/docs/src/interface/algebra.md b/docs/src/interface/algebra.md new file mode 100644 index 0000000..90fe578 --- /dev/null +++ b/docs/src/interface/algebra.md @@ -0,0 +1,20 @@ +# An interface for Lie algebras + +```@docs +LieAlgebra +``` + +## Functions on Lie algebras + +```@autodocs +Modules = [LieGroups] +Pages = ["src/Lie_algebra/Lie_algebra_interface.jl"] +Order = [:function] +``` + +## Literature + +```@bibliography +Pages = ["group.md"] +Canonical=false +``` \ No newline at end of file diff --git a/docs/src/interface/group.md b/docs/src/interface/group.md new file mode 100644 index 0000000..aa0cabb --- /dev/null +++ b/docs/src/interface/group.md @@ -0,0 +1,20 @@ +# An interface for Lie groups + +```@docs +LieGroup +``` + +## Functions on Lie groups + +```@autodocs +Modules = [LieGroups] +Pages = ["src/interface.jl"] +Order = [:function] +``` + +## Literature + +```@bibliography +Pages = ["group.md"] +Canonical=false +``` \ No newline at end of file diff --git a/docs/src/interface/operations.md b/docs/src/interface/operations.md new file mode 100644 index 0000000..2959544 --- /dev/null +++ b/docs/src/interface/operations.md @@ -0,0 +1,22 @@ + +# An interface for Lie group operations + +```@docs +AbstractGroupOperation +Identity +``` + +You can combine some specific group operations with one of several manifolds to form a Lie group. +You can still define the corresponding functions generically for all groups with this group operation regardless of the manifold. +The following sections collect these. + + +* an [`AdditionGroupOperation`](@ref) + +## [Additive group operation](@id addition-operation-sec) + +```@autodocs +Modules = [LieGroups] +Pages = ["addition.jl"] +Order = [:type, :function] +``` \ No newline at end of file diff --git a/docs/src/notation.md b/docs/src/notation.md index 4c33df2..bf0abb6 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -1,20 +1,16 @@ -# Notation on LIe Groups +# Notation on Lie groups -For most of the notation used throughout this package, we follow [HilgertNeeb:2012](@cite) +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:\mathcal G → \mathcal G, c_g`` | the conjugation map (with `g`) | | -| ``\mathrm{e}`` | identity element of a group | | -| ``g, h, k`` | elements on a (Lie) group. Sometimes called points. | ``g_1, g_2, ...`` | -| ``\mathfrak g`` | a Lie algebra | | -| ``\mathcal{G}`` | a (Lie) group | | -| ``λ_g: \mathcal G → \mathcal G`` | the left multiplication map ``λ_g(h) = g∘h`` | | -| ``\mathcal M`` | a manifold | ``\mathcal M_1, \mathcal M_2,\ldots,\mathcal N`` | | -| ``\operatorname{Exp}`` | the matrix exponential | | -| ``\operatorname{Log}`` | the matrix logarithm | | -| ``ρ_g: \mathcal G → \mathcal G`` | the right multiplication map ``ρ_g(h) = h∘g`` | | -| ``σ: \mathcal G × \mathcal M`` | a left group action | we use ``σ_g(p)`` if the group element is fixed | -| ``τ: \mathcal M × \mathcal G`` | a right group action | ``σ_\mathrm{R}`` | we use ``τ_g(p)`` if the group element is fixed | -| ``T_p \mathcal M`` | the tangent space at ``p`` | | | +| ``∘`` | a group operation | | | +| ``c_g:\mathcal G → \mathcal G`` | the conjugation map (with `g`) | | | +| ``\mathrm{e}`` | identity element of a group | | | +| ``g, h, k`` | elements on a (Lie) group. Sometimes called points. | ``g_1, g_2, ...`` | | +| ``\mathfrak g`` | a Lie algebra | | | +| ``\mathcal{G}`` | a (Lie) group | | | +| ``λ_g: \mathcal G → \mathcal G`` | the left group operation map ``λ_g(h) = g∘h`` | | | + ``ρ_g: \mathcal G → \mathcal G`` | the right group operation map ``ρ_g(h) = h∘g`` | | | +| ``σ: \mathcal G × \mathcal M`` | a left group action | | ``σ_g(p)`` to emphasize a fixed group element | +| ``τ: \mathcal M × \mathcal G`` | a right group action | ``σ_\mathrm{R}`` | ``τ_g(p)`` to emphasize a fixed group element | diff --git a/docs/src/references.md b/docs/src/references.md index 1041f81..c25523f 100644 --- a/docs/src/references.md +++ b/docs/src/references.md @@ -1,7 +1,7 @@ # Literature This is all literature mentioned / referenced in the `LieGroups.jl` documentation. -Usually you find a small reference section at the end of every documentation page +You can find a small reference section at the end of every documentation page that contains the corresponding references as well. ```@bibliography diff --git a/docs/styles/Google/AMPM.yml b/docs/styles/Google/AMPM.yml new file mode 100644 index 0000000..fbdc6e4 --- /dev/null +++ b/docs/styles/Google/AMPM.yml @@ -0,0 +1,9 @@ +extends: existence +message: "Use 'AM' or 'PM' (preceded by a space)." +link: 'https://developers.google.com/style/word-list' +level: error +nonword: true +tokens: + - '\d{1,2}[AP]M' + - '\d{1,2} ?[ap]m' + - '\d{1,2} ?[aApP]\.[mM]\.' diff --git a/docs/styles/Google/Acronyms.yml b/docs/styles/Google/Acronyms.yml new file mode 100644 index 0000000..f41af01 --- /dev/null +++ b/docs/styles/Google/Acronyms.yml @@ -0,0 +1,64 @@ +extends: conditional +message: "Spell out '%s', if it's unfamiliar to the audience." +link: 'https://developers.google.com/style/abbreviations' +level: suggestion +ignorecase: false +# Ensures that the existence of 'first' implies the existence of 'second'. +first: '\b([A-Z]{3,5})\b' +second: '(?:\b[A-Z][a-z]+ )+\(([A-Z]{3,5})\)' +# ... with the exception of these: +exceptions: + - API + - ASP + - CLI + - CPU + - CSS + - CSV + - DEBUG + - DOM + - DPI + - FAQ + - GCC + - GDB + - GET + - GPU + - GTK + - GUI + - HTML + - HTTP + - HTTPS + - IDE + - JAR + - JSON + - JSX + - LESS + - LLDB + - NET + - NOTE + - NVDA + - OSS + - PATH + - PDF + - PHP + - POST + - RAM + - REPL + - RSA + - SCM + - SCSS + - SDK + - SQL + - SSH + - SSL + - SVG + - TBD + - TCP + - TODO + - URI + - URL + - USB + - UTF + - XML + - XSS + - YAML + - ZIP diff --git a/docs/styles/Google/Colons.yml b/docs/styles/Google/Colons.yml new file mode 100644 index 0000000..99363fb --- /dev/null +++ b/docs/styles/Google/Colons.yml @@ -0,0 +1,8 @@ +extends: existence +message: "'%s' should be in lowercase." +link: 'https://developers.google.com/style/colons' +nonword: true +level: warning +scope: sentence +tokens: + - ':\s[A-Z]' diff --git a/docs/styles/Google/Contractions.yml b/docs/styles/Google/Contractions.yml new file mode 100644 index 0000000..4f6fd5d --- /dev/null +++ b/docs/styles/Google/Contractions.yml @@ -0,0 +1,30 @@ +extends: substitution +message: "Use '%s' instead of '%s'." +link: 'https://developers.google.com/style/contractions' +level: suggestion +ignorecase: true +action: + name: replace +swap: + are not: aren't + cannot: can't + could not: couldn't + did not: didn't + do not: don't + does not: doesn't + has not: hasn't + have not: haven't + how is: how's + is not: isn't + it is: it's + should not: shouldn't + that is: that's + they are: they're + was not: wasn't + we are: we're + we have: we've + were not: weren't + what is: what's + when is: when's + where is: where's + will not: won't diff --git a/docs/styles/Google/DateFormat.yml b/docs/styles/Google/DateFormat.yml new file mode 100644 index 0000000..e9d227f --- /dev/null +++ b/docs/styles/Google/DateFormat.yml @@ -0,0 +1,9 @@ +extends: existence +message: "Use 'July 31, 2016' format, not '%s'." +link: 'https://developers.google.com/style/dates-times' +ignorecase: true +level: error +nonword: true +tokens: + - '\d{1,2}(?:\.|/)\d{1,2}(?:\.|/)\d{4}' + - '\d{1,2} (?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)|May|Jun(?:e)|Jul(?:y)|Aug(?:ust)|Sep(?:tember)?|Oct(?:ober)|Nov(?:ember)?|Dec(?:ember)?) \d{4}' diff --git a/docs/styles/Google/Ellipses.yml b/docs/styles/Google/Ellipses.yml new file mode 100644 index 0000000..1e07051 --- /dev/null +++ b/docs/styles/Google/Ellipses.yml @@ -0,0 +1,9 @@ +extends: existence +message: "In general, don't use an ellipsis." +link: 'https://developers.google.com/style/ellipses' +nonword: true +level: warning +action: + name: remove +tokens: + - '\.\.\.' diff --git a/docs/styles/Google/EmDash.yml b/docs/styles/Google/EmDash.yml new file mode 100644 index 0000000..5a81fb0 --- /dev/null +++ b/docs/styles/Google/EmDash.yml @@ -0,0 +1,13 @@ +extends: existence +message: "Don't put a space before or after a dash." +link: "https://developers.google.com/style/dashes" +nonword: true +level: error +action: + name: edit + params: + - trim + - " " +tokens: + - '\s[—–]\s' + diff --git a/docs/styles/Google/Exclamation.yml b/docs/styles/Google/Exclamation.yml new file mode 100644 index 0000000..b4e4a1f --- /dev/null +++ b/docs/styles/Google/Exclamation.yml @@ -0,0 +1,12 @@ +extends: existence +message: "Don't use exclamation points in text." +link: "https://developers.google.com/style/exclamation-points" +nonword: true +level: error +action: + name: edit + params: + - trim_right + - "!" +tokens: + - '\w+!(?:\s|$)' diff --git a/docs/styles/Google/FirstPerson.yml b/docs/styles/Google/FirstPerson.yml new file mode 100644 index 0000000..0b7b882 --- /dev/null +++ b/docs/styles/Google/FirstPerson.yml @@ -0,0 +1,13 @@ +extends: existence +message: "Avoid first-person pronouns such as '%s'." +link: 'https://developers.google.com/style/pronouns#personal-pronouns' +ignorecase: true +level: warning +nonword: true +tokens: + - (?:^|\s)I\s + - (?:^|\s)I,\s + - \bI'm\b + - \bme\b + - \bmy\b + - \bmine\b diff --git a/docs/styles/Google/Gender.yml b/docs/styles/Google/Gender.yml new file mode 100644 index 0000000..c848618 --- /dev/null +++ b/docs/styles/Google/Gender.yml @@ -0,0 +1,9 @@ +extends: existence +message: "Don't use '%s' as a gender-neutral pronoun." +link: 'https://developers.google.com/style/pronouns#gender-neutral-pronouns' +level: error +ignorecase: true +tokens: + - he/she + - s/he + - \(s\)he diff --git a/docs/styles/Google/GenderBias.yml b/docs/styles/Google/GenderBias.yml new file mode 100644 index 0000000..36f5a3f --- /dev/null +++ b/docs/styles/Google/GenderBias.yml @@ -0,0 +1,43 @@ +extends: substitution +message: "Consider using '%s' instead of '%s'." +ignorecase: true +link: "https://developers.google.com/style/inclusive-documentation" +level: error +action: + name: replace +swap: + (?:alumna|alumnus): graduate + (?:alumnae|alumni): graduates + air(?:m[ae]n|wom[ae]n): pilot(s) + anchor(?:m[ae]n|wom[ae]n): anchor(s) + authoress: author + camera(?:m[ae]n|wom[ae]n): camera operator(s) + door(?:m[ae]|wom[ae]n): concierge(s) + draft(?:m[ae]n|wom[ae]n): drafter(s) + fire(?:m[ae]n|wom[ae]n): firefighter(s) + fisher(?:m[ae]n|wom[ae]n): fisher(s) + fresh(?:m[ae]n|wom[ae]n): first-year student(s) + garbage(?:m[ae]n|wom[ae]n): waste collector(s) + lady lawyer: lawyer + ladylike: courteous + mail(?:m[ae]n|wom[ae]n): mail carriers + man and wife: husband and wife + man enough: strong enough + mankind: human kind|humanity + manmade: manufactured + manpower: personnel + middle(?:m[ae]n|wom[ae]n): intermediary + news(?:m[ae]n|wom[ae]n): journalist(s) + ombuds(?:man|woman): ombuds + oneupmanship: upstaging + poetess: poet + police(?:m[ae]n|wom[ae]n): police officer(s) + repair(?:m[ae]n|wom[ae]n): technician(s) + sales(?:m[ae]n|wom[ae]n): salesperson or sales people + service(?:m[ae]n|wom[ae]n): soldier(s) + steward(?:ess)?: flight attendant + tribes(?:m[ae]n|wom[ae]n): tribe member(s) + waitress: waiter + woman doctor: doctor + woman scientist[s]?: scientist(s) + work(?:m[ae]n|wom[ae]n): worker(s) diff --git a/docs/styles/Google/HeadingPunctuation.yml b/docs/styles/Google/HeadingPunctuation.yml new file mode 100644 index 0000000..c172986 --- /dev/null +++ b/docs/styles/Google/HeadingPunctuation.yml @@ -0,0 +1,13 @@ +extends: existence +message: "Don't put a period at the end of a heading." +link: "https://developers.google.com/style/capitalization#capitalization-in-titles-and-headings" +nonword: true +level: warning +scope: heading +action: + name: edit + params: + - trim_right + - "." +tokens: + - '[a-z0-9][.]\s*$' diff --git a/docs/styles/Google/Headings.yml b/docs/styles/Google/Headings.yml new file mode 100644 index 0000000..c8d5be2 --- /dev/null +++ b/docs/styles/Google/Headings.yml @@ -0,0 +1,29 @@ +extends: capitalization +message: "'%s' should use sentence-style capitalization." +link: "https://developers.google.com/style/capitalization#capitalization-in-titles-and-headings" +level: warning +scope: heading +match: $sentence +indicators: + - ":" +exceptions: + - Azure + - CLI + - Cosmos + - Docker + - Emmet + - gRPC + - I + - Kubernetes + - Linux + - macOS + - Marketplace + - MongoDB + - REPL + - Studio + - TypeScript + - URLs + - Visual + - VS + - Windows + - JSON diff --git a/docs/styles/Google/Latin.yml b/docs/styles/Google/Latin.yml new file mode 100644 index 0000000..ca03b91 --- /dev/null +++ b/docs/styles/Google/Latin.yml @@ -0,0 +1,11 @@ +extends: substitution +message: "Use '%s' instead of '%s'." +link: 'https://developers.google.com/style/abbreviations' +ignorecase: true +level: error +nonword: true +action: + name: replace +swap: + '\b(?:eg|e\.g\.)(?=[\s,;])': for example + '\b(?:ie|i\.e\.)(?=[\s,;])': that is diff --git a/docs/styles/Google/LyHyphens.yml b/docs/styles/Google/LyHyphens.yml new file mode 100644 index 0000000..50dacb4 --- /dev/null +++ b/docs/styles/Google/LyHyphens.yml @@ -0,0 +1,14 @@ +extends: existence +message: "'%s' doesn't need a hyphen." +link: "https://developers.google.com/style/hyphens" +level: error +ignorecase: false +nonword: true +action: + name: edit + params: + - regex + - "-" + - " " +tokens: + - '\b[^\s-]+ly-\w+\b' diff --git a/docs/styles/Google/OptionalPlurals.yml b/docs/styles/Google/OptionalPlurals.yml new file mode 100644 index 0000000..4a8767d --- /dev/null +++ b/docs/styles/Google/OptionalPlurals.yml @@ -0,0 +1,12 @@ +extends: existence +message: "Don't use plurals in parentheses such as in '%s'." +link: "https://developers.google.com/style/plurals-parentheses" +level: error +nonword: true +action: + name: edit + params: + - trim_right + - "(s)" +tokens: + - '\b\w+\(s\)' diff --git a/docs/styles/Google/Ordinal.yml b/docs/styles/Google/Ordinal.yml new file mode 100644 index 0000000..d1ac7d2 --- /dev/null +++ b/docs/styles/Google/Ordinal.yml @@ -0,0 +1,7 @@ +extends: existence +message: "Spell out all ordinal numbers ('%s') in text." +link: 'https://developers.google.com/style/numbers' +level: error +nonword: true +tokens: + - \d+(?:st|nd|rd|th) diff --git a/docs/styles/Google/OxfordComma.yml b/docs/styles/Google/OxfordComma.yml new file mode 100644 index 0000000..b9ba21e --- /dev/null +++ b/docs/styles/Google/OxfordComma.yml @@ -0,0 +1,7 @@ +extends: existence +message: "Use the Oxford comma in '%s'." +link: 'https://developers.google.com/style/commas' +scope: sentence +level: warning +tokens: + - '(?:[^,]+,){1,}\s\w+\s(?:and|or)' diff --git a/docs/styles/Google/Parens.yml b/docs/styles/Google/Parens.yml new file mode 100644 index 0000000..3b8711d --- /dev/null +++ b/docs/styles/Google/Parens.yml @@ -0,0 +1,7 @@ +extends: existence +message: "Use parentheses judiciously." +link: 'https://developers.google.com/style/parentheses' +nonword: true +level: suggestion +tokens: + - '\(.+\)' diff --git a/docs/styles/Google/Passive.yml b/docs/styles/Google/Passive.yml new file mode 100644 index 0000000..3265890 --- /dev/null +++ b/docs/styles/Google/Passive.yml @@ -0,0 +1,184 @@ +extends: existence +link: 'https://developers.google.com/style/voice' +message: "In general, use active voice instead of passive voice ('%s')." +ignorecase: true +level: suggestion +raw: + - \b(am|are|were|being|is|been|was|be)\b\s* +tokens: + - '[\w]+ed' + - awoken + - beat + - become + - been + - begun + - bent + - beset + - bet + - bid + - bidden + - bitten + - bled + - blown + - born + - bought + - bound + - bred + - broadcast + - broken + - brought + - built + - burnt + - burst + - cast + - caught + - chosen + - clung + - come + - cost + - crept + - cut + - dealt + - dived + - done + - drawn + - dreamt + - driven + - drunk + - dug + - eaten + - fallen + - fed + - felt + - fit + - fled + - flown + - flung + - forbidden + - foregone + - forgiven + - forgotten + - forsaken + - fought + - found + - frozen + - given + - gone + - gotten + - ground + - grown + - heard + - held + - hidden + - hit + - hung + - hurt + - kept + - knelt + - knit + - known + - laid + - lain + - leapt + - learnt + - led + - left + - lent + - let + - lighted + - lost + - made + - meant + - met + - misspelt + - mistaken + - mown + - overcome + - overdone + - overtaken + - overthrown + - paid + - pled + - proven + - put + - quit + - read + - rid + - ridden + - risen + - run + - rung + - said + - sat + - sawn + - seen + - sent + - set + - sewn + - shaken + - shaven + - shed + - shod + - shone + - shorn + - shot + - shown + - shrunk + - shut + - slain + - slept + - slid + - slit + - slung + - smitten + - sold + - sought + - sown + - sped + - spent + - spilt + - spit + - split + - spoken + - spread + - sprung + - spun + - stolen + - stood + - stridden + - striven + - struck + - strung + - stuck + - stung + - stunk + - sung + - sunk + - swept + - swollen + - sworn + - swum + - swung + - taken + - taught + - thought + - thrived + - thrown + - thrust + - told + - torn + - trodden + - understood + - upheld + - upset + - wed + - wept + - withheld + - withstood + - woken + - won + - worn + - wound + - woven + - written + - wrung diff --git a/docs/styles/Google/Periods.yml b/docs/styles/Google/Periods.yml new file mode 100644 index 0000000..d24a6a6 --- /dev/null +++ b/docs/styles/Google/Periods.yml @@ -0,0 +1,7 @@ +extends: existence +message: "Don't use periods with acronyms or initialisms such as '%s'." +link: 'https://developers.google.com/style/abbreviations' +level: error +nonword: true +tokens: + - '\b(?:[A-Z]\.){3,}' diff --git a/docs/styles/Google/Quotes.yml b/docs/styles/Google/Quotes.yml new file mode 100644 index 0000000..3cb6f1a --- /dev/null +++ b/docs/styles/Google/Quotes.yml @@ -0,0 +1,7 @@ +extends: existence +message: "Commas and periods go inside quotation marks." +link: 'https://developers.google.com/style/quotation-marks' +level: error +nonword: true +tokens: + - '"[^"]+"[.,?]' diff --git a/docs/styles/Google/Ranges.yml b/docs/styles/Google/Ranges.yml new file mode 100644 index 0000000..3ec045e --- /dev/null +++ b/docs/styles/Google/Ranges.yml @@ -0,0 +1,7 @@ +extends: existence +message: "Don't add words such as 'from' or 'between' to describe a range of numbers." +link: 'https://developers.google.com/style/hyphens' +nonword: true +level: warning +tokens: + - '(?:from|between)\s\d+\s?-\s?\d+' diff --git a/docs/styles/Google/Semicolons.yml b/docs/styles/Google/Semicolons.yml new file mode 100644 index 0000000..bb8b85b --- /dev/null +++ b/docs/styles/Google/Semicolons.yml @@ -0,0 +1,8 @@ +extends: existence +message: "Use semicolons judiciously." +link: 'https://developers.google.com/style/semicolons' +nonword: true +scope: sentence +level: suggestion +tokens: + - ';' diff --git a/docs/styles/Google/Slang.yml b/docs/styles/Google/Slang.yml new file mode 100644 index 0000000..63f4c24 --- /dev/null +++ b/docs/styles/Google/Slang.yml @@ -0,0 +1,11 @@ +extends: existence +message: "Don't use internet slang abbreviations such as '%s'." +link: 'https://developers.google.com/style/abbreviations' +ignorecase: true +level: error +tokens: + - 'tl;dr' + - ymmv + - rtfm + - imo + - fwiw diff --git a/docs/styles/Google/Spacing.yml b/docs/styles/Google/Spacing.yml new file mode 100644 index 0000000..66e45a6 --- /dev/null +++ b/docs/styles/Google/Spacing.yml @@ -0,0 +1,10 @@ +extends: existence +message: "'%s' should have one space." +link: 'https://developers.google.com/style/sentence-spacing' +level: error +nonword: true +action: + name: remove +tokens: + - '[a-z][.?!] {2,}[A-Z]' + - '[a-z][.?!][A-Z]' diff --git a/docs/styles/Google/Spelling.yml b/docs/styles/Google/Spelling.yml new file mode 100644 index 0000000..527ac07 --- /dev/null +++ b/docs/styles/Google/Spelling.yml @@ -0,0 +1,10 @@ +extends: existence +message: "In general, use American spelling instead of '%s'." +link: 'https://developers.google.com/style/spelling' +ignorecase: true +level: warning +tokens: + - '(?:\w+)nised?' + - 'colour' + - 'labour' + - 'centre' diff --git a/docs/styles/Google/Units.yml b/docs/styles/Google/Units.yml new file mode 100644 index 0000000..53522ab --- /dev/null +++ b/docs/styles/Google/Units.yml @@ -0,0 +1,8 @@ +extends: existence +message: "Put a nonbreaking space between the number and the unit in '%s'." +link: "https://developers.google.com/style/units-of-measure" +nonword: true +level: error +tokens: + - \b\d+(?:B|kB|MB|GB|TB) + - \b\d+(?:ns|ms|s|min|h|d) diff --git a/docs/styles/Google/We.yml b/docs/styles/Google/We.yml new file mode 100644 index 0000000..c7ac7d3 --- /dev/null +++ b/docs/styles/Google/We.yml @@ -0,0 +1,11 @@ +extends: existence +message: "Try to avoid using first-person plural like '%s'." +link: 'https://developers.google.com/style/pronouns#personal-pronouns' +level: warning +ignorecase: true +tokens: + - we + - we'(?:ve|re) + - ours? + - us + - let's diff --git a/docs/styles/Google/Will.yml b/docs/styles/Google/Will.yml new file mode 100644 index 0000000..128a918 --- /dev/null +++ b/docs/styles/Google/Will.yml @@ -0,0 +1,7 @@ +extends: existence +message: "Avoid using '%s'." +link: 'https://developers.google.com/style/tense' +ignorecase: true +level: warning +tokens: + - will diff --git a/docs/styles/Google/WordList.yml b/docs/styles/Google/WordList.yml new file mode 100644 index 0000000..b3c6a40 --- /dev/null +++ b/docs/styles/Google/WordList.yml @@ -0,0 +1,80 @@ +extends: substitution +message: "Use '%s' instead of '%s'." +link: "https://developers.google.com/style/word-list" +level: warning +ignorecase: false +action: + name: replace +swap: + "(?:API Console|dev|developer) key": API key + "(?:cell ?phone|smart ?phone)": phone|mobile phone + "(?:dev|developer|APIs) console": API console + "(?:e-mail|Email|E-mail)": email + "(?:file ?path|path ?name)": path + "(?:kill|terminate|abort)": stop|exit|cancel|end + "(?:OAuth ?2|Oauth)": OAuth 2.0 + "(?:ok|Okay)": OK|okay + "(?:WiFi|wifi)": Wi-Fi + '[\.]+apk': APK + '3\-D': 3D + 'Google (?:I\-O|IO)': Google I/O + "tap (?:&|and) hold": touch & hold + "un(?:check|select)": clear + above: preceding + account name: username + action bar: app bar + admin: administrator + Ajax: AJAX + a\.k\.a|aka: or|also known as + Android device: Android-powered device + android: Android + API explorer: APIs Explorer + application: app + approx\.: approximately + authN: authentication + authZ: authorization + autoupdate: automatically update + cellular data: mobile data + cellular network: mobile network + chapter: documents|pages|sections + check box: checkbox + CLI: command-line tool + click on: click|click in + Cloud: Google Cloud Platform|GCP + Container Engine: Kubernetes Engine + content type: media type + curated roles: predefined roles + data are: data is + Developers Console: Google API Console|API Console + disabled?: turn off|off + ephemeral IP address: ephemeral external IP address + fewer data: less data + file name: filename + firewalls: firewall rules + functionality: capability|feature + Google account: Google Account + Google accounts: Google Accounts + Googling: search with Google + grayed-out: unavailable + HTTPs: HTTPS + in order to: to + ingest: import|load + k8s: Kubernetes + long press: touch & hold + network IP address: internal IP address + omnibox: address bar + open-source: open source + overview screen: recents screen + regex: regular expression + SHA1: SHA-1|HAS-SHA1 + sign into: sign in to + sign-?on: single sign-on + static IP address: static external IP address + stylesheet: style sheet + synch: sync + tablename: table name + tablet: device + touch: tap + url: URL + vs\.: versus + World Wide Web: web diff --git a/docs/styles/Google/meta.json b/docs/styles/Google/meta.json new file mode 100644 index 0000000..a5da2a8 --- /dev/null +++ b/docs/styles/Google/meta.json @@ -0,0 +1,4 @@ +{ + "feed": "https://github.com/errata-ai/Google/releases.atom", + "vale_version": ">=1.0.0" +} diff --git a/docs/styles/Google/vocab.txt b/docs/styles/Google/vocab.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/styles/config/vocabularies/LieGroups/accept.txt b/docs/styles/config/vocabularies/LieGroups/accept.txt new file mode 100644 index 0000000..043f7a7 --- /dev/null +++ b/docs/styles/config/vocabularies/LieGroups/accept.txt @@ -0,0 +1,8 @@ +julia +Julia +LieGroups.jl +Lie algebra +Lie algebras +Lie group +Lie groups +Riemannian \ No newline at end of file diff --git a/docs/styles/write-good/Cliches.yml b/docs/styles/write-good/Cliches.yml new file mode 100644 index 0000000..c953143 --- /dev/null +++ b/docs/styles/write-good/Cliches.yml @@ -0,0 +1,702 @@ +extends: existence +message: "Try to avoid using clichés like '%s'." +ignorecase: true +level: warning +tokens: + - a chip off the old block + - a clean slate + - a dark and stormy night + - a far cry + - a fine kettle of fish + - a loose cannon + - a penny saved is a penny earned + - a tough row to hoe + - a word to the wise + - ace in the hole + - acid test + - add insult to injury + - against all odds + - air your dirty laundry + - all fun and games + - all in a day's work + - all talk, no action + - all thumbs + - all your eggs in one basket + - all's fair in love and war + - all's well that ends well + - almighty dollar + - American as apple pie + - an axe to grind + - another day, another dollar + - armed to the teeth + - as luck would have it + - as old as time + - as the crow flies + - at loose ends + - at my wits end + - avoid like the plague + - babe in the woods + - back against the wall + - back in the saddle + - back to square one + - back to the drawing board + - bad to the bone + - badge of honor + - bald faced liar + - ballpark figure + - banging your head against a brick wall + - baptism by fire + - barking up the wrong tree + - bat out of hell + - be all and end all + - beat a dead horse + - beat around the bush + - been there, done that + - beggars can't be choosers + - behind the eight ball + - bend over backwards + - benefit of the doubt + - bent out of shape + - best thing since sliced bread + - bet your bottom dollar + - better half + - better late than never + - better mousetrap + - better safe than sorry + - between a rock and a hard place + - beyond the pale + - bide your time + - big as life + - big cheese + - big fish in a small pond + - big man on campus + - bigger they are the harder they fall + - bird in the hand + - bird's eye view + - birds and the bees + - birds of a feather flock together + - bit the hand that feeds you + - bite the bullet + - bite the dust + - bitten off more than he can chew + - black as coal + - black as pitch + - black as the ace of spades + - blast from the past + - bleeding heart + - blessing in disguise + - blind ambition + - blind as a bat + - blind leading the blind + - blood is thicker than water + - blood sweat and tears + - blow off steam + - blow your own horn + - blushing bride + - boils down to + - bolt from the blue + - bone to pick + - bored stiff + - bored to tears + - bottomless pit + - boys will be boys + - bright and early + - brings home the bacon + - broad across the beam + - broken record + - brought back to reality + - bull by the horns + - bull in a china shop + - burn the midnight oil + - burning question + - burning the candle at both ends + - burst your bubble + - bury the hatchet + - busy as a bee + - by hook or by crook + - call a spade a spade + - called onto the carpet + - calm before the storm + - can of worms + - can't cut the mustard + - can't hold a candle to + - case of mistaken identity + - cat got your tongue + - cat's meow + - caught in the crossfire + - caught red-handed + - checkered past + - chomping at the bit + - cleanliness is next to godliness + - clear as a bell + - clear as mud + - close to the vest + - cock and bull story + - cold shoulder + - come hell or high water + - cool as a cucumber + - cool, calm, and collected + - cost a king's ransom + - count your blessings + - crack of dawn + - crash course + - creature comforts + - cross that bridge when you come to it + - crushing blow + - cry like a baby + - cry me a river + - cry over spilt milk + - crystal clear + - curiosity killed the cat + - cut and dried + - cut through the red tape + - cut to the chase + - cute as a bugs ear + - cute as a button + - cute as a puppy + - cuts to the quick + - dark before the dawn + - day in, day out + - dead as a doornail + - devil is in the details + - dime a dozen + - divide and conquer + - dog and pony show + - dog days + - dog eat dog + - dog tired + - don't burn your bridges + - don't count your chickens + - don't look a gift horse in the mouth + - don't rock the boat + - don't step on anyone's toes + - don't take any wooden nickels + - down and out + - down at the heels + - down in the dumps + - down the hatch + - down to earth + - draw the line + - dressed to kill + - dressed to the nines + - drives me up the wall + - dull as dishwater + - dyed in the wool + - eagle eye + - ear to the ground + - early bird catches the worm + - easier said than done + - easy as pie + - eat your heart out + - eat your words + - eleventh hour + - even the playing field + - every dog has its day + - every fiber of my being + - everything but the kitchen sink + - eye for an eye + - face the music + - facts of life + - fair weather friend + - fall by the wayside + - fan the flames + - feast or famine + - feather your nest + - feathered friends + - few and far between + - fifteen minutes of fame + - filthy vermin + - fine kettle of fish + - fish out of water + - fishing for a compliment + - fit as a fiddle + - fit the bill + - fit to be tied + - flash in the pan + - flat as a pancake + - flip your lid + - flog a dead horse + - fly by night + - fly the coop + - follow your heart + - for all intents and purposes + - for the birds + - for what it's worth + - force of nature + - force to be reckoned with + - forgive and forget + - fox in the henhouse + - free and easy + - free as a bird + - fresh as a daisy + - full steam ahead + - fun in the sun + - garbage in, garbage out + - gentle as a lamb + - get a kick out of + - get a leg up + - get down and dirty + - get the lead out + - get to the bottom of + - get your feet wet + - gets my goat + - gilding the lily + - give and take + - go against the grain + - go at it tooth and nail + - go for broke + - go him one better + - go the extra mile + - go with the flow + - goes without saying + - good as gold + - good deed for the day + - good things come to those who wait + - good time was had by all + - good times were had by all + - greased lightning + - greek to me + - green thumb + - green-eyed monster + - grist for the mill + - growing like a weed + - hair of the dog + - hand to mouth + - happy as a clam + - happy as a lark + - hasn't a clue + - have a nice day + - have high hopes + - have the last laugh + - haven't got a row to hoe + - head honcho + - head over heels + - hear a pin drop + - heard it through the grapevine + - heart's content + - heavy as lead + - hem and haw + - high and dry + - high and mighty + - high as a kite + - hit paydirt + - hold your head up high + - hold your horses + - hold your own + - hold your tongue + - honest as the day is long + - horns of a dilemma + - horse of a different color + - hot under the collar + - hour of need + - I beg to differ + - icing on the cake + - if the shoe fits + - if the shoe were on the other foot + - in a jam + - in a jiffy + - in a nutshell + - in a pig's eye + - in a pinch + - in a word + - in hot water + - in the gutter + - in the nick of time + - in the thick of it + - in your dreams + - it ain't over till the fat lady sings + - it goes without saying + - it takes all kinds + - it takes one to know one + - it's a small world + - it's only a matter of time + - ivory tower + - Jack of all trades + - jockey for position + - jog your memory + - joined at the hip + - judge a book by its cover + - jump down your throat + - jump in with both feet + - jump on the bandwagon + - jump the gun + - jump to conclusions + - just a hop, skip, and a jump + - just the ticket + - justice is blind + - keep a stiff upper lip + - keep an eye on + - keep it simple, stupid + - keep the home fires burning + - keep up with the Joneses + - keep your chin up + - keep your fingers crossed + - kick the bucket + - kick up your heels + - kick your feet up + - kid in a candy store + - kill two birds with one stone + - kiss of death + - knock it out of the park + - knock on wood + - knock your socks off + - know him from Adam + - know the ropes + - know the score + - knuckle down + - knuckle sandwich + - knuckle under + - labor of love + - ladder of success + - land on your feet + - lap of luxury + - last but not least + - last hurrah + - last-ditch effort + - law of the jungle + - law of the land + - lay down the law + - leaps and bounds + - let sleeping dogs lie + - let the cat out of the bag + - let the good times roll + - let your hair down + - let's talk turkey + - letter perfect + - lick your wounds + - lies like a rug + - life's a bitch + - life's a grind + - light at the end of the tunnel + - lighter than a feather + - lighter than air + - like clockwork + - like father like son + - like taking candy from a baby + - like there's no tomorrow + - lion's share + - live and learn + - live and let live + - long and short of it + - long lost love + - look before you leap + - look down your nose + - look what the cat dragged in + - looking a gift horse in the mouth + - looks like death warmed over + - loose cannon + - lose your head + - lose your temper + - loud as a horn + - lounge lizard + - loved and lost + - low man on the totem pole + - luck of the draw + - luck of the Irish + - make hay while the sun shines + - make money hand over fist + - make my day + - make the best of a bad situation + - make the best of it + - make your blood boil + - man of few words + - man's best friend + - mark my words + - meaningful dialogue + - missed the boat on that one + - moment in the sun + - moment of glory + - moment of truth + - money to burn + - more power to you + - more than one way to skin a cat + - movers and shakers + - moving experience + - naked as a jaybird + - naked truth + - neat as a pin + - needle in a haystack + - needless to say + - neither here nor there + - never look back + - never say never + - nip and tuck + - nip it in the bud + - no guts, no glory + - no love lost + - no pain, no gain + - no skin off my back + - no stone unturned + - no time like the present + - no use crying over spilled milk + - nose to the grindstone + - not a hope in hell + - not a minute's peace + - not in my backyard + - not playing with a full deck + - not the end of the world + - not written in stone + - nothing to sneeze at + - nothing ventured nothing gained + - now we're cooking + - off the top of my head + - off the wagon + - off the wall + - old hat + - older and wiser + - older than dirt + - older than Methuselah + - on a roll + - on cloud nine + - on pins and needles + - on the bandwagon + - on the money + - on the nose + - on the rocks + - on the spot + - on the tip of my tongue + - on the wagon + - on thin ice + - once bitten, twice shy + - one bad apple doesn't spoil the bushel + - one born every minute + - one brick short + - one foot in the grave + - one in a million + - one red cent + - only game in town + - open a can of worms + - open and shut case + - open the flood gates + - opportunity doesn't knock twice + - out of pocket + - out of sight, out of mind + - out of the frying pan into the fire + - out of the woods + - out on a limb + - over a barrel + - over the hump + - pain and suffering + - pain in the + - panic button + - par for the course + - part and parcel + - party pooper + - pass the buck + - patience is a virtue + - pay through the nose + - penny pincher + - perfect storm + - pig in a poke + - pile it on + - pillar of the community + - pin your hopes on + - pitter patter of little feet + - plain as day + - plain as the nose on your face + - play by the rules + - play your cards right + - playing the field + - playing with fire + - pleased as punch + - plenty of fish in the sea + - point with pride + - poor as a church mouse + - pot calling the kettle black + - pretty as a picture + - pull a fast one + - pull your punches + - pulling your leg + - pure as the driven snow + - put it in a nutshell + - put one over on you + - put the cart before the horse + - put the pedal to the metal + - put your best foot forward + - put your foot down + - quick as a bunny + - quick as a lick + - quick as a wink + - quick as lightning + - quiet as a dormouse + - rags to riches + - raining buckets + - raining cats and dogs + - rank and file + - rat race + - reap what you sow + - red as a beet + - red herring + - reinvent the wheel + - rich and famous + - rings a bell + - ripe old age + - ripped me off + - rise and shine + - road to hell is paved with good intentions + - rob Peter to pay Paul + - roll over in the grave + - rub the wrong way + - ruled the roost + - running in circles + - sad but true + - sadder but wiser + - salt of the earth + - scared stiff + - scared to death + - sealed with a kiss + - second to none + - see eye to eye + - seen the light + - seize the day + - set the record straight + - set the world on fire + - set your teeth on edge + - sharp as a tack + - shoot for the moon + - shoot the breeze + - shot in the dark + - shoulder to the wheel + - sick as a dog + - sigh of relief + - signed, sealed, and delivered + - sink or swim + - six of one, half a dozen of another + - skating on thin ice + - slept like a log + - slinging mud + - slippery as an eel + - slow as molasses + - smart as a whip + - smooth as a baby's bottom + - sneaking suspicion + - snug as a bug in a rug + - sow wild oats + - spare the rod, spoil the child + - speak of the devil + - spilled the beans + - spinning your wheels + - spitting image of + - spoke with relish + - spread like wildfire + - spring to life + - squeaky wheel gets the grease + - stands out like a sore thumb + - start from scratch + - stick in the mud + - still waters run deep + - stitch in time + - stop and smell the roses + - straight as an arrow + - straw that broke the camel's back + - strong as an ox + - stubborn as a mule + - stuff that dreams are made of + - stuffed shirt + - sweating blood + - sweating bullets + - take a load off + - take one for the team + - take the bait + - take the bull by the horns + - take the plunge + - takes one to know one + - takes two to tango + - the more the merrier + - the real deal + - the real McCoy + - the red carpet treatment + - the same old story + - there is no accounting for taste + - thick as a brick + - thick as thieves + - thin as a rail + - think outside of the box + - third time's the charm + - this day and age + - this hurts me worse than it hurts you + - this point in time + - three sheets to the wind + - through thick and thin + - throw in the towel + - tie one on + - tighter than a drum + - time and time again + - time is of the essence + - tip of the iceberg + - tired but happy + - to coin a phrase + - to each his own + - to make a long story short + - to the best of my knowledge + - toe the line + - tongue in cheek + - too good to be true + - too hot to handle + - too numerous to mention + - touch with a ten foot pole + - tough as nails + - trial and error + - trials and tribulations + - tried and true + - trip down memory lane + - twist of fate + - two cents worth + - two peas in a pod + - ugly as sin + - under the counter + - under the gun + - under the same roof + - under the weather + - until the cows come home + - unvarnished truth + - up the creek + - uphill battle + - upper crust + - upset the applecart + - vain attempt + - vain effort + - vanquish the enemy + - vested interest + - waiting for the other shoe to drop + - wakeup call + - warm welcome + - watch your p's and q's + - watch your tongue + - watching the clock + - water under the bridge + - weather the storm + - weed them out + - week of Sundays + - went belly up + - wet behind the ears + - what goes around comes around + - what you see is what you get + - when it rains, it pours + - when push comes to shove + - when the cat's away + - when the going gets tough, the tough get going + - white as a sheet + - whole ball of wax + - whole hog + - whole nine yards + - wild goose chase + - will wonders never cease? + - wisdom of the ages + - wise as an owl + - wolf at the door + - words fail me + - work like a dog + - world weary + - worst nightmare + - worth its weight in gold + - wrong side of the bed + - yanking your chain + - yappy as a dog + - years young + - you are what you eat + - you can run but you can't hide + - you only live once + - you're the boss + - young and foolish + - young and vibrant diff --git a/docs/styles/write-good/E-Prime.yml b/docs/styles/write-good/E-Prime.yml new file mode 100644 index 0000000..074a102 --- /dev/null +++ b/docs/styles/write-good/E-Prime.yml @@ -0,0 +1,32 @@ +extends: existence +message: "Try to avoid using '%s'." +ignorecase: true +level: suggestion +tokens: + - am + - are + - aren't + - be + - been + - being + - he's + - here's + - here's + - how's + - i'm + - is + - isn't + - it's + - she's + - that's + - there's + - they're + - was + - wasn't + - we're + - were + - weren't + - what's + - where's + - who's + - you're diff --git a/docs/styles/write-good/Illusions.yml b/docs/styles/write-good/Illusions.yml new file mode 100644 index 0000000..b4f1321 --- /dev/null +++ b/docs/styles/write-good/Illusions.yml @@ -0,0 +1,11 @@ +extends: repetition +message: "'%s' is repeated!" +level: warning +alpha: true +action: + name: edit + params: + - truncate + - " " +tokens: + - '[^\s]+' diff --git a/docs/styles/write-good/Passive.yml b/docs/styles/write-good/Passive.yml new file mode 100644 index 0000000..f472cb9 --- /dev/null +++ b/docs/styles/write-good/Passive.yml @@ -0,0 +1,183 @@ +extends: existence +message: "'%s' may be passive voice. Use active voice if you can." +ignorecase: true +level: warning +raw: + - \b(am|are|were|being|is|been|was|be)\b\s* +tokens: + - '[\w]+ed' + - awoken + - beat + - become + - been + - begun + - bent + - beset + - bet + - bid + - bidden + - bitten + - bled + - blown + - born + - bought + - bound + - bred + - broadcast + - broken + - brought + - built + - burnt + - burst + - cast + - caught + - chosen + - clung + - come + - cost + - crept + - cut + - dealt + - dived + - done + - drawn + - dreamt + - driven + - drunk + - dug + - eaten + - fallen + - fed + - felt + - fit + - fled + - flown + - flung + - forbidden + - foregone + - forgiven + - forgotten + - forsaken + - fought + - found + - frozen + - given + - gone + - gotten + - ground + - grown + - heard + - held + - hidden + - hit + - hung + - hurt + - kept + - knelt + - knit + - known + - laid + - lain + - leapt + - learnt + - led + - left + - lent + - let + - lighted + - lost + - made + - meant + - met + - misspelt + - mistaken + - mown + - overcome + - overdone + - overtaken + - overthrown + - paid + - pled + - proven + - put + - quit + - read + - rid + - ridden + - risen + - run + - rung + - said + - sat + - sawn + - seen + - sent + - set + - sewn + - shaken + - shaven + - shed + - shod + - shone + - shorn + - shot + - shown + - shrunk + - shut + - slain + - slept + - slid + - slit + - slung + - smitten + - sold + - sought + - sown + - sped + - spent + - spilt + - spit + - split + - spoken + - spread + - sprung + - spun + - stolen + - stood + - stridden + - striven + - struck + - strung + - stuck + - stung + - stunk + - sung + - sunk + - swept + - swollen + - sworn + - swum + - swung + - taken + - taught + - thought + - thrived + - thrown + - thrust + - told + - torn + - trodden + - understood + - upheld + - upset + - wed + - wept + - withheld + - withstood + - woken + - won + - worn + - wound + - woven + - written + - wrung diff --git a/docs/styles/write-good/README.md b/docs/styles/write-good/README.md new file mode 100644 index 0000000..3edcc9b --- /dev/null +++ b/docs/styles/write-good/README.md @@ -0,0 +1,27 @@ +Based on [write-good](https://github.com/btford/write-good). + +> Naive linter for English prose for developers who can't write good and wanna learn to do other stuff good too. + +``` +The MIT License (MIT) + +Copyright (c) 2014 Brian Ford + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` diff --git a/docs/styles/write-good/So.yml b/docs/styles/write-good/So.yml new file mode 100644 index 0000000..e57f099 --- /dev/null +++ b/docs/styles/write-good/So.yml @@ -0,0 +1,5 @@ +extends: existence +message: "Don't start a sentence with '%s'." +level: error +raw: + - '(?:[;-]\s)so[\s,]|\bSo[\s,]' diff --git a/docs/styles/write-good/ThereIs.yml b/docs/styles/write-good/ThereIs.yml new file mode 100644 index 0000000..8b82e8f --- /dev/null +++ b/docs/styles/write-good/ThereIs.yml @@ -0,0 +1,6 @@ +extends: existence +message: "Don't start a sentence with '%s'." +ignorecase: false +level: error +raw: + - '(?:[;-]\s)There\s(is|are)|\bThere\s(is|are)\b' diff --git a/docs/styles/write-good/TooWordy.yml b/docs/styles/write-good/TooWordy.yml new file mode 100644 index 0000000..275701b --- /dev/null +++ b/docs/styles/write-good/TooWordy.yml @@ -0,0 +1,221 @@ +extends: existence +message: "'%s' is too wordy." +ignorecase: true +level: warning +tokens: + - a number of + - abundance + - accede to + - accelerate + - accentuate + - accompany + - accomplish + - accorded + - accrue + - acquiesce + - acquire + - additional + - adjacent to + - adjustment + - admissible + - advantageous + - adversely impact + - advise + - aforementioned + - aggregate + - aircraft + - all of + - all things considered + - alleviate + - allocate + - along the lines of + - already existing + - alternatively + - amazing + - ameliorate + - anticipate + - apparent + - appreciable + - as a matter of fact + - as a means of + - as far as I'm concerned + - as of yet + - as to + - as yet + - ascertain + - assistance + - at the present time + - at this time + - attain + - attributable to + - authorize + - because of the fact that + - belated + - benefit from + - bestow + - by means of + - by virtue of + - by virtue of the fact that + - cease + - close proximity + - commence + - comply with + - concerning + - consequently + - consolidate + - constitutes + - demonstrate + - depart + - designate + - discontinue + - due to the fact that + - each and every + - economical + - eliminate + - elucidate + - employ + - endeavor + - enumerate + - equitable + - equivalent + - evaluate + - evidenced + - exclusively + - expedite + - expend + - expiration + - facilitate + - factual evidence + - feasible + - finalize + - first and foremost + - for all intents and purposes + - for the most part + - for the purpose of + - forfeit + - formulate + - have a tendency to + - honest truth + - however + - if and when + - impacted + - implement + - in a manner of speaking + - in a timely manner + - in a very real sense + - in accordance with + - in addition + - in all likelihood + - in an effort to + - in between + - in excess of + - in lieu of + - in light of the fact that + - in many cases + - in my opinion + - in order to + - in regard to + - in some instances + - in terms of + - in the case of + - in the event that + - in the final analysis + - in the nature of + - in the near future + - in the process of + - inception + - incumbent upon + - indicate + - indication + - initiate + - irregardless + - is applicable to + - is authorized to + - is responsible for + - it is + - it is essential + - it seems that + - it was + - magnitude + - maximum + - methodology + - minimize + - minimum + - modify + - monitor + - multiple + - necessitate + - nevertheless + - not certain + - not many + - not often + - not unless + - not unlike + - notwithstanding + - null and void + - numerous + - objective + - obligate + - obtain + - on the contrary + - on the other hand + - one particular + - optimum + - overall + - owing to the fact that + - participate + - particulars + - pass away + - pertaining to + - point in time + - portion + - possess + - preclude + - previously + - prior to + - prioritize + - procure + - proficiency + - provided that + - purchase + - put simply + - readily apparent + - refer back + - regarding + - relocate + - remainder + - remuneration + - requirement + - reside + - residence + - retain + - satisfy + - shall + - should you wish + - similar to + - solicit + - span across + - strategize + - subsequent + - substantial + - successfully complete + - sufficient + - terminate + - the month of + - the point I am trying to make + - therefore + - time period + - took advantage of + - transmit + - transpire + - type of + - until such time as + - utilization + - utilize + - validate + - various different + - what I mean to say is + - whether or not + - with respect to + - with the exception of + - witnessed diff --git a/docs/styles/write-good/Weasel.yml b/docs/styles/write-good/Weasel.yml new file mode 100644 index 0000000..d1d90a7 --- /dev/null +++ b/docs/styles/write-good/Weasel.yml @@ -0,0 +1,29 @@ +extends: existence +message: "'%s' is a weasel word!" +ignorecase: true +level: warning +tokens: + - clearly + - completely + - exceedingly + - excellent + - extremely + - fairly + - huge + - interestingly + - is a number + - largely + - mostly + - obviously + - quite + - relatively + - remarkably + - several + - significantly + - substantially + - surprisingly + - tiny + - usually + - various + - vast + - very diff --git a/docs/styles/write-good/meta.json b/docs/styles/write-good/meta.json new file mode 100644 index 0000000..a115d28 --- /dev/null +++ b/docs/styles/write-good/meta.json @@ -0,0 +1,4 @@ +{ + "feed": "https://github.com/errata-ai/write-good/releases.atom", + "vale_version": ">=1.0.0" +} diff --git a/src/LieGroups.jl b/src/LieGroups.jl index d8ef5dc..1e45a8a 100644 --- a/src/LieGroups.jl +++ b/src/LieGroups.jl @@ -1,23 +1,32 @@ module LieGroups -using ManifoldsBase, Manifolds - -# before removing them from Manifolds.jl – let's for consistency include them here but -# overwrite them for now. This makes moving away from that (in Manifolds.jl 0.11) here nonbreaking. -import Manifolds: apply, apply! - +using ManifoldsBase, Manifolds, LinearAlgebra + +# +# +# = 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 +# 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") + +# Actions include("group_actions/group_action_interface.jl") include("group_actions/group_operation_action.jl") -include("additive_group_operation.jl") -include("groups/additive.jl") +# Lie groups +include("groups/translation_group.jl") export LieGroup, LieAlgebra export AbstractGroupOperation, Identity -export AdditiveGroupOperation +export AdditionGroupOperation export AbstractGroupActionType, AbstractGroupAction export AbstractLeftGroupActionType, AbstractRightGroupActionType @@ -25,14 +34,15 @@ export LeftGroupOperation, RightGroupOperation export InverseLeftGroupOperation, InverseRightGroupOperation export GroupOperationAction -export AdditiveGroup +export TranslationGroup -export apply, apply!, diff_apply, diff_apply! -export base_manifold -export adjoint, - adjoint!, - compose, - compose!, +export adjoint, adjoint!, apply, apply! +export base_lie_group, base_manifold +export compose, compose! +export diff_apply, + diff_apply!, + diff_group_apply, + diff_group_apply!, diff_left_compose, diff_left_compose!, diff_right_compose, @@ -41,10 +51,12 @@ export adjoint, 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!, log, log! +export exp, exp! export identity_element, identity_element!, is_identity, inv, inv!, diff_inv, diff_inv! -export base_Lie_group, base_manifold -export lie_bracket, lie_bracket! -export inv +export lie_bracket, lie_bracket!, log, log! +export norm +export switch +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 new file mode 100644 index 0000000..13be4e4 --- /dev/null +++ b/src/Lie_algebra/Lie_algebra_interface.jl @@ -0,0 +1,86 @@ + +""" + LieAlgebra{𝔽, G} <: AbstractManifold{𝔽} + +Represent the Lie algebra ``$(_math(:𝔤))``, that is a ``𝔽`` vector space with an associated +[`lie_bracket`](@ref) ``[⋅,⋅]: $(_math(:𝔤))×$(_math(:𝔤)) → $(_math(:𝔤))`` which fulfills + +1. ``[X,X] = 0`` for all ``X ∈ $(_math(:𝔤))`` +2. The Jacobi identity ``[X, [Y,Z]] = [[X,Y],Z] = [Y, [X,Z]]`` holds for all ``X, Y, Z ∈ $(_math(:𝔤))``. + +The Lie algebras considered here are those related to a [`LieGroup`](@ref) ``$(_math(:G))``, +namely the tangent space ``T_{$(_math(:e))}$(_math(:G))`` at the [`Identity`](@ref), +this is internally just a `const` of the corresponding $(_link(:TangentSpace)). + +# Constructor + + LieAlgebra(G::LieGroup) + +Return the Lie Algebra belonging to the [`LieGroup`](@ref) `G`. +""" +const LieAlgebra{𝔽,O<:AbstractGroupOperation,G<:LieGroup{𝔽,O}} = ManifoldsBase.Fiber{ + 𝔽,ManifoldsBase.TangentSpaceType,G,Identity{O} +} + +function LieAlgebra(G::LieGroup{𝔽,O}) where {𝔽,O<:AbstractGroupOperation} + return LieAlgebra{𝔽,O,typeof(G)}(G, Identity(G), ManifoldsBase.TangentSpaceType()) +end + +_doc_lie_bracket = """ + lie_bracket!(𝔤::LieAlgebra, X, Y) + lie_bracket!(𝔤::LieAlgebra, Z, X, Y) + +Compute the Lie bracket ``[⋅,⋅]: $(_math(:𝔤))×$(_math(:𝔤)) → $(_math(:𝔤))`` which fulfills + +1. ``[X,X] = 0`` for all ``X ∈ $(_math(:𝔤))`` +2. The Jacobi identity ``[X, [Y,Z]] = [[X,Y],Z] = [Y, [X,Z]]`` holds for all ``X, Y, Z ∈ $(_math(:𝔤))``. + +The computation can be done in-place of `Z`. +""" +function lie_bracket end +@doc "$(_doc_lie_bracket)" +function lie_bracket(𝔤::LieAlgebra, X, Y) + Z = ManifoldsBase.allocate_result(𝔤, lie_bracket, X, Y) + return lie_bracket!(𝔤, Z, X, Y) +end + +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 + +function Base.show(io::IO, 𝔤::LieAlgebra) + return print(io, "LieAlgebra( $(𝔤.manifold) )") +end + +function ManifoldsBase.zero_vector(𝔤::LieAlgebra) + return ManifoldsBase.zero_vector(𝔤.manifold, identity_element(𝔤.manifold)) +end + +function ManifoldsBase.zero_vector!(𝔤::LieAlgebra, X) + return ManifoldsBase.zero_vector!(𝔤.manifold, X, identity_element(𝔤.manifold)) +end diff --git a/src/additive_group_operation.jl b/src/additive_group_operation.jl deleted file mode 100644 index 2f3b946..0000000 --- a/src/additive_group_operation.jl +++ /dev/null @@ -1,6 +0,0 @@ -""" - AdditiveGroupOperation <: AbstractGroupOperation - -A general group operation for groups that -""" -struct AdditiveGroupOperation <: AbstractGroupOperation end diff --git a/src/documentation_glossary.jl b/src/documentation_glossary.jl index c0e0c16..ff940bd 100644 --- a/src/documentation_glossary.jl +++ b/src/documentation_glossary.jl @@ -9,9 +9,9 @@ # * links # * notes # -# to keep naming, notation, and formatting in a unifed way +# to keep naming, notation, and formatting in a unified way -# In general every dictionary here can be either :Symbol-> String or :Symbol -> Dictionary enrties +# In general every dictionary here can be either `:Symbol-> String` or `:Symbol -> Dictionary enrties` _LIEGROUPS_DOC_TYPE = Dict{Symbol,Union{String,Dict,Function}} @@ -28,7 +28,7 @@ if that entrs is * a function, it is called with `args...` and `kwargs...` passed * a dictionary, then the arguments and keyword arguments are passed to this dictionary, assuming `args[1]` is a symbol """ -#do not document for now, until we have an internals section +#Do not attach the doc string here for now, since there is no internals section in the documentation yet glossary(s::Symbol, args...; kwargs...) = glossary(_manopt_glossary, s, args...; kwargs...) function glossary(g::_LIEGROUPS_DOC_TYPE, s::Symbol, args...; kwargs...) return glossary(g[s], args...; kwargs...) @@ -93,7 +93,7 @@ define!(:Math, :M, (; M="M") -> _math(:Manifold, :symbol; M=M)) # # --- # Links -# Collect short forms for links, especially Interdocs ones. +# Collect short forms for links, especially `DocumenterInterlinks` ones. _link(args...; kwargs...) = glossary(:Link, args...; kwargs...) define!( @@ -101,7 +101,17 @@ define!( :AbstractManifold, "[`AbstractManifold`](@extref `ManifoldsBase.AbstractManifold`)", ) +define!( + :Link, + :isapprox, + "[`isapprox`](@extref `Base.isapprox-Tuple{AbstractManifold, Any, Any, Any}`)", +) define!(:Link, :TangentSpace, "[`TangentSpace`](@extref `ManifoldsBase.TangentSpace`)") +define!( + :Link, + :zero_vector, + "[`zero_vector`](@extref `ManifoldsBase.zero_vector-Tuple{AbstractManifold, Any}`)", +) # # --- diff --git a/src/group_actions/group_action_interface.jl b/src/group_actions/group_action_interface.jl index 6522065..73d3eb7 100644 --- a/src/group_actions/group_action_interface.jl +++ b/src/group_actions/group_action_interface.jl @@ -99,16 +99,17 @@ abstract type AbstractGroupAction{ function base_lie_group end @doc """ - base_Lie_group(A::AbstractGroupAction) + base_lie_group(A::AbstractGroupAction) -Return the [`LieGroup`](@ref) of the [`AbstractGroupAction`](@ref). +Return the [`LieGroup`](@ref) of the [`AbstractGroupAction`](@ref) +specifying the action. """ base_lie_group(::AbstractGroupAction) @doc """ base_manifold(A::AbstractGroupAction) -Return the $(_link(:AbstractManifold)) of the [`AbstractGroupAction`](@ref). +Return the $(_link(:AbstractManifold)) the group action acts upon. """ ManifoldsBase.base_manifold(::AbstractGroupAction) @@ -125,15 +126,17 @@ where the kind of group action is indicated by the [`AbstractGroupActionType`](@ This can be perfomed in-place of `q`. """ -# function apply end # uncomment (remove this comment) when removing this function from Manifolds.jl +# function apply end +# un-comment the preceding line and remove this, once GroupManifolds no longer exists in Manifolds.jl @doc "$(_doc_apply)" function apply(A::AbstractGroupAction, g, p) - q = allocate_result(A, apply, g, p) + q = allocate_result(base_manifold(A), apply, g, p) apply!(A, q, g, p) return q end -# function apply! end # uncomment (remove this comment) when removing this function from Manifolds.jl +# Define `function apply! end` here as well +# un-comment (remove this comment) when removing this function from Manifolds.jl @doc "$(_doc_apply)" apply!(A::AbstractGroupAction, q, g, p) @@ -148,9 +151,9 @@ where for a left group action we have ``σ_g(p) = σ(g,p)``, for a right action function diff_apply end @doc "$(_doc_diff_apply)" function diff_apply(A::AbstractGroupAction, g, p, X) - Y = allocate_result(A, apply, p, g, X) + Y = allocate_result(base_manifold(A), apply_diff_group, p, g, X) diff_apply!(A, Y, g, p, X) - return q + return Y end function diff_apply! end @@ -161,7 +164,7 @@ _doc_diff_group_apply = """ diff_group_apply(A::AbstractGroupAction{T, L, M}, g, p, X) diff_group_apply!(A::AbstractGroupAction{T, L, M}, Y, g, p, X) -Compute the differential ``D_g σ_p(g): T_g$(_math(:G)) → T_{σ_p(g)}$(_math(:M))``, +Compute the differential ``D_g σ_g(p): $(_math(:𝔤)) → $(_math(:𝔤))``, where we use the short hand notation ``σ_p(g) = σ(g,p)`` for a left action, and for a right action ``σ_p(g) = σ(p, g)``. """ @@ -169,9 +172,9 @@ and for a right action ``σ_p(g) = σ(p, g)``. function diff_group_apply end @doc "$(_doc_diff_group_apply)" function diff_group_apply(A::AbstractGroupAction, g, p, X) - Y = allocate_result(A, apply, p, g, X) + Y = allocate_result(base_manifold(A), apply, g, p, X) diff_group_apply!(A, Y, g, p, X) - return q + return Y end function diff_group_apply! end diff --git a/src/group_actions/group_operation_action.jl b/src/group_actions/group_operation_action.jl index e5e403b..6796bb5 100644 --- a/src/group_actions/group_operation_action.jl +++ b/src/group_actions/group_operation_action.jl @@ -112,31 +112,67 @@ end _doc_diff_apply_groupop = """ -Compute the differential ``D_g σ_p(g): T_g$(_math(:G)) → T_{σ_p(g)}$(_math(:M))`` -of a group operation action, that is + diff_apply(A::GroupOperationAction, g, p, X) -* for the [`LeftGroupOperation`](@ref) this calls [`diff_left_compose`](@ref)`(G, g, h, X)` -* for the [`RightGroupOperation`](@ref) this calls [`diff_right_compose`](@ref)`(G, g, h, X)` -* for the [`InverseLeftGroupOperation`](@ref) this calls [`diff_left_compose`](@ref) with ``g^{-1}`` -* for the [`InverseRightGroupOperation`](@ref) this calls [`diff_right_compose`](@ref) with ``g^{-1}`` +For the group operation action ``σ_g(p)``, compute the differential +``D_p σ_g(p): T_p$(_math(:G)) → T_{σ_g(p)}$(_math(:G))``, that is + +* for the [`LeftGroupOperation`](@ref) this calls [`diff_right_compose`](@ref)`(G, g, p, X)`, since here ``σ_g(p) = g$(_math(:∘))p`` +* for the [`RightGroupOperation`](@ref) this calls [`diff_left_compose`](@ref)`(G, p, g, X)`, since here ``σ_g(p) = p$(_math(:∘))g`` +* for the [`InverseLeftGroupOperation`](@ref) this calls [`diff_right_compose`](@ref) with ``g^{-1}``, since here ``σ_g(p) = g^{-1}$(_math(:∘))p`` +* for the [`InverseRightGroupOperation`](@ref) this calls [`diff_left_compose`](@ref) with ``g^{-1}``, since here ``σ_g(p) = p$(_math(:∘))g^{-1}`` """ @doc "$(_doc_diff_apply_groupop)" -diff_apply(A::GroupOperationAction, g, h, X) +diff_apply(A::GroupOperationAction, g, p, X) @doc "$(_doc_diff_apply_groupop)" -diff_apply!(A::GroupOperationAction, Y, g, h, X) +diff_apply!(A::GroupOperationAction, Y, g, p, X) -function diff_apply!(A::GroupOperationAction{LeftGroupOperation}, Y, g, h, X) - return diff_left_compose!(A.group, k, h, g) +function diff_apply!(A::GroupOperationAction{LeftGroupOperation}, Y, g, p, X) + return diff_right_compose!(A.group, Y, g, p, X) end -function diff_apply!(A::GroupOperationAction{RightGroupOperation}, Y, g, h, X) - return diff_right_compose!(A.group, k, h, g) +function diff_apply!(A::GroupOperationAction{RightGroupOperation}, Y, g, p, X) + return diff_left_compose!(A.group, Y, p, g, X) end -function diff_apply!(A::GroupOperationAction{InverseLeftGroupOperation}, Y, g, h, X) - return diff_left_compose!(A.group, k, inv(A.group, g), h) +function diff_apply!(A::GroupOperationAction{InverseLeftGroupOperation}, Y, g, p, X) + return diff_right_compose!(A.group, Y, inv(A.group, g), p, X) end -function diff_apply!(A::GroupOperationAction{InverseRightGroupOperation}, Y, g, h, X) - return diff_right_compose!(A.group, k, inv(A.group, g), h) +function diff_apply!(A::GroupOperationAction{InverseRightGroupOperation}, Y, g, p, X) + return diff_left_compose!(A.group, Y, p, inv(A.group, g), X) +end + +_doc_diff_group_apply_groupop = """ + + diff_group_apply(A::GroupOperationAction, g, p, X) + +Compute the differential ``D_g σ_g(p): $(_math(:𝔤)) → $(_math(:𝔤))`` of a group operation action, +that is + +* for the [`LeftGroupOperation`](@ref) this calls [`diff_left_compose`](@ref)`(G, g, p, X)`, since here ``σ_g(p) = g$(_math(:∘))p`` +* for the [`RightGroupOperation`](@ref) this calls [`diff_right_compose`](@ref)`(G, p, g, X)`, since here ``σ_g(p) = p$(_math(:∘))g`` +* for the [`InverseLeftGroupOperation`](@ref) this calls [`diff_left_compose`](@ref) with ``g^{-1}``, since here ``σ_g(p) = g^{-1}$(_math(:∘))p`` together with [`diff_inv`](@ref) +* for the [`InverseRightGroupOperation`](@ref) this calls [`diff_right_compose`](@ref) with ``g^{-1}``, since here ``σ_g(p) = p$(_math(:∘))g^{-1}`` together with [`diff_inv`](@ref) +""" + +@doc "$(_doc_diff_apply_groupop)" +diff_group_apply(A::GroupOperationAction, g, p, X) + +@doc "$(_doc_diff_apply_groupop)" +diff_group_apply!(A::GroupOperationAction, Y, g, p, X) + +function diff_group_apply!(A::GroupOperationAction{LeftGroupOperation}, Y, g, p, X) + return diff_left_compose!(A.group, Y, g, p, X) +end +function diff_group_apply!(A::GroupOperationAction{RightGroupOperation}, Y, g, p, X) + return diff_right_compose!(A.group, Y, p, g, X) +end +function diff_group_apply!(A::GroupOperationAction{InverseLeftGroupOperation}, Y, g, p, X) + diff_inv!(A.group, Y, g, X) + return diff_left_compose!(A.group, Y, inv(A.group, g), p, Y) +end +function diff_group_apply!(A::GroupOperationAction{InverseRightGroupOperation}, Y, g, p, X) + diff_inv!(A.group, Y, g, X) + return diff_right_compose!(A.group, Y, p, inv(A.group, g), Y) end """ @@ -172,6 +208,10 @@ Return the inverse of the [`InverseRightGroupOperation`](@ref), that is the [`Ri """ Base.inv(::InverseRightGroupOperation) = RightGroupOperation() +function Base.show(io::IO, A::GroupOperationAction) + return print(io, "GroupOperationAction($(A.type), $(A.group))") +end + """ switch(::LeftGroupOperation) diff --git a/src/group_operations/addition.jl b/src/group_operations/addition.jl new file mode 100644 index 0000000..07db44c --- /dev/null +++ b/src/group_operations/addition.jl @@ -0,0 +1,209 @@ +""" + AdditionGroupOperation <: AbstractGroupOperation + +A group operation that is realised introducing defaults that fall back +to `+` and `-` being overloaded, for example +`_compose(G::LieGroup{𝔽,AdditionGroupOperation}, a, b) = a + b` +""" +struct AdditionGroupOperation <: AbstractGroupOperation end + +# +# +# Handle interactions of `+` and `-` with the identity element, though they are +# also already handled on the `compose()` level +Base.:+(e::Identity{AdditionGroupOperation}) = e +Base.:+(e::Identity{AdditionGroupOperation}, ::Identity{AdditionGroupOperation}) = e +Base.:+(::Identity{AdditionGroupOperation}, g) = g +Base.:+(g, ::Identity{AdditionGroupOperation}) = g + +Base.:-(e::Identity{AdditionGroupOperation}) = e +Base.:-(e::Identity{AdditionGroupOperation}, ::Identity{AdditionGroupOperation}) = e +Base.:-(::Identity{AdditionGroupOperation}, g) = -g +Base.:-(g, ::Identity{AdditionGroupOperation}) = g + +_doc_compose_add = """ + compose(G::LieGroup{𝔽,AdditionGroupOperation}, g, h) + compose!(G::LieGroup{𝔽,AdditionGroupOperation}, k, g, h) + +Compute the group operation composition of `g` and `h` with respect to +the [`AdditionGroupOperation`](@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_add)" +compose(::LieGroup{𝔽,AdditionGroupOperation}, g, h) where {𝔽} + +@doc "$(_doc_compose_add)" +compose!(::LieGroup{𝔽,AdditionGroupOperation}, k, g, h) where {𝔽} + +function _compose!(G::LieGroup{𝔽,AdditionGroupOperation}, k, g, h) where {𝔽} + k .= g .+ h + return k +end + +_doc_diff_conjugate_add = """ + diff_conjugate(G::LieGroup{𝔽,AdditionGroupOperation}, g, h, X) + diff_conjugate!(G::LieGroup{𝔽,AdditionGroupOperation}, Y, g, h, X) + +Compute the differential of the conjutage ``c_g(h) = g$(_math(:∘))h$(_math(:∘))g^{-1} = g+h-g = h``, +which simplifies for [`AdditionGroupOperation`](@ref) to ``D(c_g(h))[X] = X``. +""" + +@doc "$(_doc_diff_conjugate_add)" +diff_conjugate(G::LieGroup{𝔽,AdditionGroupOperation}, g, h, X) where {𝔽} + +@doc "$(_doc_diff_conjugate_add)" +function diff_conjugate!(G::LieGroup{𝔽,AdditionGroupOperation}, Y, g, h, X) where {𝔽} + return copyto!(LieAlgebra(G), Y, X) +end + +_doc_diff_inv_add = """ + diff_inv(G::LieGroup{𝔽,AdditionGroupOperation}, g, X) + diff_inv!(G::LieGroup{𝔽,AdditionGroupOperation}, Y, g, X) + +Compute the differential of the inverse operation ``ι_{$(_math(:G))}(g) = g^-1 = -g``, +which simplifies for [`AdditionGroupOperation`](@ref) to ``Dι_{$(_math(:G))}(g)[X] = -X`` +""" + +@doc "$(_doc_diff_inv_add)" +diff_inv(G::LieGroup{𝔽,AdditionGroupOperation}, g, X) where {𝔽} = -X + +@doc "$(_doc_diff_inv_add)" +function diff_inv!(G::LieGroup{𝔽,AdditionGroupOperation}, Y, g, X) where {𝔽} + Y .= (-1) .* X + return Y +end + +_doc_diff_left_compose_add = """ + diff_left_compose(G::LieGroup{𝔽,AdditionGroupOperation}, g, h, X) + diff_left_compose!(G::LieGroup{𝔽,AdditionGroupOperation}, Y, g, h, X) + +Compute the differential of the left group multiplication ``λ_g(h) = g$(_math(:∘))h``, +which simplifies for [`AdditionGroupOperation`](@ref) to ``Dλ_g(h)[X] = X``. +""" + +@doc "$(_doc_diff_left_compose_add)" +diff_left_compose(G::LieGroup{𝔽,AdditionGroupOperation}, g, h, X) where {𝔽} = X + +@doc "$(_doc_diff_left_compose_add)" +function diff_left_compose!(G::LieGroup{𝔽,AdditionGroupOperation}, Y, g, h, X) where {𝔽} + return copyto!(LieAlgebra(G), Y, X) +end + +_doc_diff_right_compose_add = """ + diff_right_compose(G::LieGroup{𝔽,AdditionGroupOperation}, h, g, X) + diff_right_compose!(G::LieGroup{𝔽,AdditionGroupOperation}, Y, h, g, X) + +Compute the differential of the right group multiplication ``ρ_g(h) = h$(_math(:∘))g``, +which simplifies for [`AdditionGroupOperation`](@ref) to ``Dρ_g(h)[X] = X``. +""" + +@doc "$(_doc_diff_right_compose_add)" +diff_right_compose(G::LieGroup{𝔽,AdditionGroupOperation}, h, g, X) where {𝔽} + +@doc "$(_doc_diff_right_compose_add)" +function diff_right_compose!(G::LieGroup{𝔽,AdditionGroupOperation}, Y, g, h, X) where {𝔽} + return copyto!(LieAlgebra(G), Y, X) +end + +_doc_exp_add = """ + exp(G::LieGroup{𝔽,AdditionGroupOperation}, e::Identity{AdditionGroupOperation}, X, t::Number=1) + exp!(G::LieGroup{𝔽,AdditionGroupOperation}, g, e::Identity{AdditionGroupOperation}, X, t::Number=1) + +Compute the Lie group exponential on a [`LieGroup`](@ref) with an [`AdditionGroupOperation`](@ref). +This can be computed in-place of `g`. + +Since `e` is just the zero-element with respect to the corresponding `+`, the formula reads ``g=0+X=X``. +""" + +@doc "$(_doc_exp_add)" +Base.exp( + ::LieGroup{𝔽,AdditionGroupOperation}, ::Identity{AdditionGroupOperation}, X, t::Number=1 +) where {𝔽} = t * X + +@doc "$(_doc_exp_add)" +function ManifoldsBase.exp!( + G::LieGroup{𝔽,AdditionGroupOperation}, + g, + ::Identity{AdditionGroupOperation}, + X, + t::Number=1, +) where {𝔽} + g .= t .* X + return g +end + +_doc_identity_element_add = """ + identity_element(G::LieGroup{𝔽,AdditionGroupOperation}) + identity_element!(G::LieGroup{𝔽,AdditionGroupOperation}, e) + +Return the a point representation of the [`Identity`](@ref), +which for the [`AdditionGroupOperation`](@ref) is the zero element or array. +""" + +@doc "$(_doc_identity_element_add)" +identity_element(::LieGroup{𝔽,AdditionGroupOperation}) where {𝔽} + +@doc "$(_doc_identity_element_add)" +function identity_element!(::LieGroup{𝔽,AdditionGroupOperation}, e) where {𝔽} + return fill!(e, 0) +end + +_doc_inv_add = """ + inv(G::LieGroup{𝔽,AdditionGroupOperation}, g) + inv!(G::LieGroup{𝔽,AdditionGroupOperation}, h, g) + +Compute the inverse group element ``g^{-1}``, which for the [`AdditionGroupOperation`](@ref) +simplifies to ``-g``. This can be done in-place of `h`. +""" + +@doc "$(_doc_inv_add)" +inv(G::LieGroup{𝔽,AdditionGroupOperation}, g) where {𝔽} + +@doc "$(_doc_inv_add)" +function inv!(::LieGroup{𝔽,AdditionGroupOperation}, h, g) where {𝔽} + h .= (-1) .* g + return h +end + +_doc_lie_bracket_add = """ + lie_bracket!(𝔤::LieAlgebra{𝔽,AdditionGroupOperation}, X, Y) + lie_bracket!(𝔤::LieAlgebra{𝔽,AdditionGroupOperation}, Z, X, Y) + +Compute the Lie bracket ``[⋅,⋅]: $(_math(:𝔤))×$(_math(:𝔤)) → $(_math(:𝔤))``, +which for the for the [`AdditionGroupOperation`](@ref) simplifies to the +corresponding $(_link(:zero_vector)). +The computation can be done in-place of `Z`. +""" + +@doc "$(_doc_lie_bracket_add)" +lie_bracket(𝔤::LieAlgebra{𝔽,AdditionGroupOperation}, X, Y) where {𝔽} + +@doc "$(_doc_lie_bracket_add)" +function lie_bracket!(𝔤::LieAlgebra{𝔽,AdditionGroupOperation}, Z, X, Y) where {𝔽} + return zero_vector!(𝔤, Z) +end + +_doc_log_add = """ + log(G::LieGroup{𝔽,AdditionGroupOperation}, e::Identity{AdditionGroupOperation}, g) + log!(G::LieGroup{𝔽,AdditionGroupOperation}, X, e::Identity{AdditionGroupOperation}, g) + +Compute the Lie group logarithm on a [`LieGroup`](@ref) with an [`AdditionGroupOperation`](@ref). +This can be computed in-place of `X`. + +Since `e` is just the zero-element with respect to the corresponding `+`, the formula reads ``X=g-0=g``. +""" + +@doc "$(_doc_log_add)" +ManifoldsBase.log( + G::LieGroup{𝔽,AdditionGroupOperation}, ::Identity{AdditionGroupOperation}, q +) where {𝔽} = q + +@doc "$(_doc_log_add)" +function ManifoldsBase.log!( + G::LieGroup{𝔽,AdditionGroupOperation}, X, ::Identity{AdditionGroupOperation}, g +) where {𝔽} + return copyto!(G, X, g) +end diff --git a/src/groups/additive.jl b/src/groups/additive.jl deleted file mode 100644 index 8317db5..0000000 --- a/src/groups/additive.jl +++ /dev/null @@ -1,18 +0,0 @@ -@doc """ - AdditiveGroup <: LieGroup{bbF } - -The group ``(𝕂^n, +)`` is a [`LieGroup`](@ref) using the [`AdditiveGroupOperation`](@ref) -and serves as the prototype for the [`AdditiveGroupOperation`](@ref). - -# Constructor - - AdditiveGroup(n₁,n₂,...,nᵢ; field=ℝ, kwargs...) - -Generate the additive group. All parameters are used to initialise the internal -[`Euclidean`](@extref `Manifolds.Euclidean`) manifold. -""" -const AdditiveGroup{𝔽} = LieGroup{𝔽,Euclidean{T,𝔽} where T,AdditiveGroupOperation} where {𝔽} - -function AdditiveGroup(n::Vararg{Int,I}; field::AbstractNumbers=ℝ, kwargs...) where {I} - return AdditiveGroup(Euclidean(n; field=field, kwargs...), AdditiveGroupOperation()) -end diff --git a/src/groups/translation_group.jl b/src/groups/translation_group.jl new file mode 100644 index 0000000..a404c14 --- /dev/null +++ b/src/groups/translation_group.jl @@ -0,0 +1,24 @@ +""" + TranslationGroup{𝔽,T} + +The Lie group consisting of the [`AdditionGroupOperation`](@ref) on some +[`Euclidean`](@extref `Manifolds.Euclidean`) space. + +# Constructor + TranslationGroup(n₁,...,nᵢ; kwargs...) + +Generate the translation group on ``𝔽^{n₁,…,nᵢ}`` = `Euclidean(n₁,...,nᵢ; field=𝔽)`, +which is isomorphic to the group itself. All keyword arguments in `kwargs...` +are passed on to [`Euclidean`](@extref `Manifolds.Euclidean`) as well +""" +const TranslationGroup{𝔽,T} = LieGroup{𝔽,AdditionGroupOperation,Manifolds.Euclidean{T,𝔽}} + +function TranslationGroup(n::Int...; kwargs...) + Rn = Manifolds.Euclidean(n...; kwargs...) + return TranslationGroup{typeof(Rn).parameters[[2, 1]]...}(Rn, AdditionGroupOperation()) +end + +function Base.show(io::IO, G::TranslationGroup{𝔽}) where {𝔽} + size = Manifolds.get_parameter(G.manifold.size) + return print(io, "TranslationGroup($(join(size, ", ")); field=$(𝔽))") +end diff --git a/src/interface.jl b/src/interface.jl index ae64de1..2a93fcd 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -11,7 +11,7 @@ on elements of a Lie group ``$(_math(:G))``. abstract type AbstractGroupOperation end """ - LieGroup{𝔽,M<:AbstractManifold{𝔽}, O<:AbstractGroupOperation} + LieGroup{𝔽, O<:AbstractGroupOperation, M<:AbstractManifold{𝔽}} Represent a Lie Group ``$(_math(:G))``. @@ -29,18 +29,16 @@ Lie groups are named after the Norwegian mathematician [Marius Sophus Lie](https # Constructor - LieGroup(M::AbstractManifold, op::AbstractGroupOperation; vectors=LeftInvariantRepresentation()) + LieGroup(M::AbstractManifold, op::AbstractGroupOperation) Generate a Lie group based on a manifold `M` and a group operation `op`, where vectors by default are stored in the Lie Algebra. """ -struct LieGroup{𝔽,M<:ManifoldsBase.AbstractManifold{𝔽},O<:AbstractGroupOperation} <: +struct LieGroup{𝔽,O<:AbstractGroupOperation,M<:ManifoldsBase.AbstractManifold{𝔽}} <: ManifoldsBase.AbstractManifold{𝔽} manifold::M op::O end -Base.show(io::IO, G::LieGroup) = print(io, "LieGroup($(G.manifold), $(G.op))") - @doc """ Identity{O<:AbstractGroupOperation} @@ -54,7 +52,7 @@ See also [`identity_element`](@ref) on how to obtain the corresponding [`Abstrac # Constructors - Identity(::LieGroup{𝔽,M,O}) where {𝔽,M,O<:AbstractGroupOperation} + Identity(::LieGroup{𝔽,O}) where {𝔽,O<:AbstractGroupOperation} Identity(o::AbstractGroupOperation) Identity(::Type{AbstractGroupOperation}) @@ -62,74 +60,10 @@ create the identity of the corresponding subtype `O<:`[`AbstractGroupOperation`] """ struct Identity{O<:AbstractGroupOperation} end -ManifoldsBase.@trait_function Identity(M::ManifoldsBase.AbstractDecoratorManifold) -Identity(::LieGroup{𝔽,M,O}) where {𝔽,M,O<:AbstractGroupOperation} = Identity{O}() +Identity(::LieGroup{𝔽,O}) where {𝔽,O<:AbstractGroupOperation} = Identity{O}() Identity(::O) where {O<:AbstractGroupOperation} = Identity(O) Identity(::Type{O}) where {O<:AbstractGroupOperation} = Identity{O}() -""" - LieAlgebra{𝔽, G} <: AbstractManifold{𝔽} - -Represent the Lie Algebra ``$(_math(:𝔤))``, that is a ``𝔽``vector space with an associated -[`lie_bracket`](@ref) ``[⋅,⋅]: $(_math(:𝔤))×$(_math(:𝔤)) → $(_math(:𝔤))`` which fulfills - -1. ``[X,X] = 0`` for all ``X ∈ $(_math(:𝔤))`` -2. The Jacobi identity holds ``[X, [Y,Z]] = [[X,Y],Z] = [Y, [X,Z]]`` holds for all ``X, Y, Z ∈ $(_math(:𝔤))``. - -The Lie algebras considered here are those related to a [`LieGroup`](@ref) ``$(_math(:G))``, -namely the tangent space the tangent space ``T_{$(_math(:e))}$(_math(:G))`` at the [`Identity`](@ref), -this is internally just a `const` of the corresponding $(_link(:TangentSpace)). - -# Constructor - - LieAlgebra(G::LieGroup) - -Return the Lie Algebra belonging to the [`LieGroup`](@ref) `G`. -""" -const LieAlgebra{𝔽,G,I} = ManifoldsBase.Fiber{ - 𝔽,ManifoldsBase.TangentSpaceType,G,I -} where {𝔽,G<:LieGroup{𝔽},I<:Identity} - -function LieAlgebra(G::LieGroup{𝔽}) where {𝔽} - return LieAlgebra{𝔽,G,typeof(Identity(G))}( - G, Identity(G), ManifoldsBase.TangentSpaceType() - ) -end - -# -# -# Traits for LIe groups -""" - AbstractInvarianceTrait <: AbstractTrait - -A common supertype for anz [`AbstractTrait`](@extref `ManifoldsBase.AbstractTrait`) related to metric invariance -""" -abstract type AbstractInvarianceTrait <: ManifoldsBase.AbstractTrait end - -""" - HasLeftInvariantMetric <: AbstractInvarianceTrait - -Specify that the defaut metric on a [`LieGroup`](@ref) ``$(_math(:G))`` is left-invariant. -""" -struct HasLeftInvariantMetric <: AbstractInvarianceTrait end - -""" - HasRightInvariantMetric <: AbstractInvarianceTrait - -Specify that the defaut metric on a [`LieGroup`](@ref) ``$(_math(:G))`` is right-invariant. -""" -struct HasRightInvariantMetric <: AbstractInvarianceTrait end - -""" - HasBiinvariantMetric <: AbstractInvarianceTrait - -Specify that the defaut metric on a [`LieGroup`](@ref) ``$(_math(:G))`` is bi-invariant. -""" -struct HasBiinvariantMetric <: AbstractInvarianceTrait end -function parent_trait(::HasBiinvariantMetric) - return ManifoldsBase.TraitList(HasLeftInvariantMetric(), HasRightInvariantMetric()) -end - # # # --- Functions --- @@ -160,11 +94,19 @@ end function adjoint! end @doc "$(_doc_adjoint)" -function adjoint!(::LieGroup, Y, g, X) +function adjoint!(G::LieGroup, Y, g, X) diff_conjugate!(G, Y, g, Identity(G), X) return Y end +# +# Allocation hints +function ManifoldsBase.allocate_result(G::LieGroup, f::typeof(identity_element)) + apf = ManifoldsBase.allocation_promotion_function(G, f, ()) + rs = ManifoldsBase.representation_size(G) + return ManifoldsBase.allocate_result_array(G, f, apf(Float64), rs) +end + @doc """ base_manifold(G::LieGroup) @@ -172,104 +114,79 @@ Return the manifold stored within the [`LieGroup`](@ref) `G`. """ Manifolds.base_manifold(G::LieGroup) = G.manifold -# compose g_1 ∘ g_2 and its frieds +function ManifoldsBase.check_point( + G::LieGroup{𝔽,O}, g; kwargs... +) where {𝔽,O<:AbstractGroupOperation} + return ManifoldsBase.check_point(G.manifold, g; kwargs...) +end +function ManifoldsBase.check_point( + G::LieGroup{𝔽,O}, e::Identity{O}; kwargs... +) where {𝔽,O<:AbstractGroupOperation} + return nothing +end +function ManifoldsBase.check_point( + G::LieGroup{𝔽,O}, e::Identity{O2}; kwargs... +) where {𝔽,O<:AbstractGroupOperation,O2<:AbstractGroupOperation} + return DomainError( + e, + """ + The provided point $e is not the Identity on $G. + Expected an Identity corresponding to $(G.op). + """, + ) +end + +# compose g ∘ h _doc_compose = """ compose(G::LieGroup, g, h) compose!(G::LieGroup, k, g, h) Perform the group oepration ``g $(_math(:∘)) h`` for two ``g, h ∈ $(_math(:G))`` on the [`LieGroup`](@ref) `G`. This can also be done in-place of `h`. + +!!! info + This function also handles the case where `g` or/and `h` are the [`Identity`](@ref)`(G)`. + Since this would lead to ambiguities when implementing a new group operations, + this function calls `_compose` and `_compose!`, respectively, which is meant for the actual computation of + group operations on (non-[`Identity`](@ref)` but maybe its numerical representation) elements. """ @doc "$(_doc_compose)" -function compose(G::LieGroup, g, h) +compose(G::LieGroup, g, h) = _compose(G, g, h) +compose(::LieGroup{𝔽,O}, g::Identity{O}, h) where {𝔽,O<:AbstractGroupOperation} = h +compose(::LieGroup{𝔽,O}, g, h::Identity{O}) where {𝔽,O<:AbstractGroupOperation} = g +function compose( + ::LieGroup{𝔽,O}, g::Identity{O}, h::Identity{O} +) where {𝔽,O<:AbstractGroupOperation} + return g +end + +function _compose(G::LieGroup, g, h) k = ManifoldsBase.allocate_result(G, compose, g, h) - return compose!(G, k, g, h) + return _compose!(G, k, g, h) end function compose! end -@doc "$(_doc_compose)" -compose!(::LieGroup, k, g, h) - -_doc_diff_left_compose = """ - diff_left_compose(G::LieGroup, g, h, X) - diff_left_compose!(G::LieGroup, Y, g, h, X) -Compute the differential of the left group multiplication ``λ_g(h) = g$(_math(:∘))h``, -on the [`LieGroup`](@ref) `G`, that is Compute ``Dλ_g(h)[X]``, ``X ∈ 𝔤``. -This can be done in-place of `Y`. -""" -@doc "$(_doc_diff_left_compose)" -function diff_left_compose(G::LieGroup, g, h, X) - Y = ManifoldsBase.allocate_result(G, diff_left_compose, g, h, X) - return diff_left_compose!(G, Y, g, h, X) +@doc "$(_doc_compose)" +compose!(G::LieGroup, k, g, h) = _compose!(G, k, g, h) +function compose!(G::LieGroup{𝔽,O}, k, ::Identity{O}, h) where {𝔽,O<:AbstractGroupOperation} + return copyto!(G, k, h) end - -function diff_left_compose! end -@doc "$(_doc_diff_left_compose)" -diff_left_compose!(::LieGroup, Y, g, h, X) - -_doc_diff_right_compose = """ - diff_right_compose(G::LieGroup, h, g, X) - diff_right_compose!(G::LieGroup, Y, h, g, X) - -Compute the differential of the right group multiplication ``ρ_g(h) = h$(_math(:∘))g``, -on the [`LieGroup`](@ref) `G`, that is Compute ``Dρ_g(h)[X]``, ``X ∈ 𝔤`` -This can be done in-place of `Y`. -""" -@doc "$(_doc_diff_right_compose)" -function diff_right_compose(G::LieGroup, h, g, X) - Y = ManifoldsBase.allocate_result(G, diff_right_compose, h, g, X) - return diff_right_compose!(G, Y, h, g, X) +function compose!(G::LieGroup{𝔽,O}, k, g, ::Identity{O}) where {𝔽,O<:AbstractGroupOperation} + return copyto!(G, k, g) end - -function diff_right_compose! end -@doc "$(_doc_diff_right_compose)" -diff_right_compose!(::LieGroup, h, g1, g2) - -# --- -_doc_inv_left_compose = """ - inv_left_compose(G::LieGroup, g, h) - inv_left_compose!(G::LieGroup, k, g, h) - -Compute the inverse of the left group multiplication ``λ_g(h) = g$(_math(:∘))h``, -on the [`LieGroup`](@ref) `G`, that is, compute ``λ_g^{-1}(h) = g^{-1}$(_math(:∘))h``. -This can be done in-place of `k`. -""" -@doc "$(_doc_inv_left_compose)" -function inv_left_compose(G::LieGroup, g, h) - k = ManifoldsBase.allocate_result(G, inv_left_compose, g, h) - return inv_left_compose!(G, k, g, h) +function compose!( + G::LieGroup{𝔽,O}, k, ::Identity{O}, ::Identity{O} +) where {𝔽,O<:AbstractGroupOperation} + return identity_element!(G, k) end - -function inv_left_compose! end -@doc "$(_doc_compose)" -function inv_left_compose!(::LieGroup, k, g, h) - inv!(G, k, g) # g^{-1} in-place of k - compose!(G, k, k, h) # kh inplace of k +function compose!( + ::LieGroup{𝔽,O}, k::Identity{O}, ::Identity{O}, ::Identity{O} +) where {𝔽,O<:AbstractGroupOperation} return k end -_doc_inv_right_compose = """ - inv_right_compose(G::LieGroup, h, g) - inv_right_compose!(G::LieGroup, k, h, g) - -Compute the inverse of the right group multiplication ``ρ_g(h) = h$(_math(:∘))g``, -on the [`LieGroup`](@ref) `G`, that is Compute ``ρ_g^{-1}(h) = h$(_math(:∘))g^{-1}``. -This can be done in-place of `k`. -""" -@doc "$(_doc_inv_right_compose)" -function inv_right_compose(G::LieGroup, h, g) - k = ManifoldsBase.allocate_result(G, inv_right_compose, h, g) - return inv_right_compose!(G, k, h, g) -end - -function inv_right_compose! end -@doc "$(_doc_inv_right_compose)" -function inv_right_compose!(::LieGroup, k, h, g) - inv!(G, k, g) # g^{-1} in-place of k - compose!(G, k, h, h) # hk inplace of k - return k -end +function _compose! end _doc_conjugate = """ conjugate(G::LieGroup, g, h) @@ -286,13 +203,35 @@ end function conjugate! end @doc "$(_doc_conjugate)" -function conjugate!(::LieGroup, k, g, h) +function conjugate!(G::LieGroup, k, g, h) inv!(G, k, g) # g^{-1} in-place of k - compose!(G, k, h, h) # hk inplace of k - compose!(G, k, g, k) # gk inplace of k + compose!(G, k, h, k) # `h∘k` in-place of k + compose!(G, k, g, k) # `g∘k` in-place of k return k end +ManifoldsBase.copyto!(G::LieGroup, h, g) = copyto!(G.manifold, h, g) +function ManifoldsBase.copyto!( + G::LieGroup{𝔽,O}, h, g::Identity{O} +) where {𝔽,O<:AbstractGroupOperation} + return ManifoldsBase.copyto!(G.manifold, h, identity_element(G)) +end +function ManifoldsBase.copyto!( + ::LieGroup{𝔽,O}, h::Identity{O}, ::Identity{O} +) where {𝔽,O<:AbstractGroupOperation} + return h +end +function ManifoldsBase.copyto!( + G::LieGroup{𝔽,O}, h::Identity{O}, g +) where {𝔽,O<:AbstractGroupOperation} + (is_identity(G, g)) && return h + throw( + DomainError( + g, + "copyto! into the identity element of $G ($h) is not defined for a non-identity element g ($g)", + ), + ) +end _doc_diff_conjugate = """ diff_conjugate(G::LieGroup, g, h, X) diff_conjugate!(G::LieGroup, Y, g, h, X) @@ -314,6 +253,61 @@ function diff_conjugate! end @doc "$(_doc_diff_conjugate)" diff_conjugate!(::LieGroup, Y, g, h, X) +_doc_diff_inv = """ + diff_inv(G::LieGroup, g, X) + diff_inv!(G::LieGroup, Y, g, X) + +Compute the differential of the function ``ι_{$(_math(:G))}(g) = g^{-1}``, where +``Dι_{$(_math(:G))}(g): $(_math(:𝔤)) → $(_math(:𝔤))``. +This can be done in-place of `Y`. +""" + +@doc "$_doc_diff_inv" +function diff_inv(G::LieGroup, g, X) + Y = allocate_result(G, diff_inv, g, X) + return diff_inv!(G, Y, g, X) +end + +function diff_inv! end +@doc "$_doc_diff_inv" +diff_inv!(G::LieGroup, Y, g, X) + +_doc_diff_left_compose = """ + diff_left_compose(G::LieGroup, g, h, X) + diff_left_compose!(G::LieGroup, Y, g, h, X) + +Compute the differential of the left group multiplication ``λ_g(h) = g$(_math(:∘))h``, +on the [`LieGroup`](@ref) `G`, that is Compute ``Dλ_g(h)[X]``, ``X ∈ 𝔤``. +This can be done in-place of `Y`. +""" +@doc "$(_doc_diff_left_compose)" +function diff_left_compose(G::LieGroup, g, h, X) + Y = ManifoldsBase.allocate_result(G, diff_left_compose, g, h, X) + return diff_left_compose!(G, Y, g, h, X) +end + +function diff_left_compose! end +@doc "$(_doc_diff_left_compose)" +diff_left_compose!(::LieGroup, Y, g, h, X) + +_doc_diff_right_compose = """ + diff_right_compose(G::LieGroup, h, g, X) + diff_right_compose!(G::LieGroup, Y, h, g, X) + +Compute the differential of the right group multiplication ``ρ_g(h) = h$(_math(:∘))g``, +on the [`LieGroup`](@ref) `G`, that is Compute ``Dρ_g(h)[X]``, ``X ∈ 𝔤`` +This can be done in-place of `Y`. +""" +@doc "$(_doc_diff_right_compose)" +function diff_right_compose(G::LieGroup, h, g, X) + Y = ManifoldsBase.allocate_result(G, diff_right_compose, h, g, X) + return diff_right_compose!(G, Y, h, g, X) +end + +function diff_right_compose! end +@doc "$(_doc_diff_right_compose)" +diff_right_compose!(::LieGroup, Y, g, h, X) + _doc_exp = """ exp(G::LieGroup, g, X, t::Number=1) exp!(G::LieGroup, h, g, X, t::Number=1) @@ -334,7 +328,7 @@ and ``$(_tex(:exp))_{$(_math(:G))}`` denotes the [Lie group exponential functio !!! note The Lie group exponential map is usually different from the exponential map with respect to the metric of the underlying Riemannian manifold ``$(_math(:M))``. - To access the (Riemannian) exponential map, use `exp(`[`base_manifold`](@ref)`G, g, X)`. + To access the (Riemannian) exponential map, use `exp(`[`base_manifold`](@ref)`(G), g, X)`. """ @doc "$_doc_exp" @@ -374,47 +368,37 @@ See also [HilgertNeeb:2012; Definition 9.2.2](@cite). @doc "$(_doc_exp_id)" function ManifoldsBase.exp(G::LieGroup, e::Identity, X, t::Number=1) - h = identity_element(G) # allocate + h = identity_element(G) exp!(G, h, e, X, t) return h end +# Fallback to a MethodError to avoid stack overflow @doc "$(_doc_exp_id)" -ManifoldsBase.exp!(::LieGroup, h, ::Identity, X, ::Number=1) - -function is_identity end -@doc """ - is_identity(G::LieGroup, q; kwargs) - -Check whether `q` is the identity on the [`LieGroup`](@ref) ``$(_math(:G))``. -This means it is either the [`Identity`](@ref)`{O}` with the respect to the corresponding -[`AbstractGroupOperation`](@ref) `O`, or (approximately) the correct point representation. - -# See also - -[`identity_element`](@ref), [`identity_element!`](@ref) -""" -is_identity(G::AbstractDecoratorManifold, q) +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_identity_element = """ identity_element(G::LieGroup) - identity_element!(G::LieGroup, g) + identity_element!(G::LieGroup, e) Return a point representation of the [`Identity`](@ref) on the [`LieGroup`](@ref) `G`. By default this representation is the default array or number representation. It should return the corresponding default representation of ``e`` as a point on `G` if points are not represented by arrays. -This can be performed in-place of `g`. +This can be performed in-place of `e`. """ -function identity_element end +# `function identity_element end` @doc "$(_doc_identity_element)" -function identity_element(G::AbstractDecoratorManifold) - g = ManifoldsBase.allocate_result(G, identity_element) - return identity_element!(G, g) +function identity_element(G::LieGroup) + e = ManifoldsBase.allocate_result(G, identity_element) + return identity_element!(G, e) end + function identity_element! end @doc "$(_doc_identity_element)" -identity_element!(G::AbstractDecoratorManifold, g) +identity_element!(G::LieGroup, e) _doc_inv = """ inv(G::LieGroup, g) @@ -428,7 +412,7 @@ This can be done in-place of `h`, without side effects, that is you can do `inv! """ @doc "$_doc_inv" -function Base.inv(::LieGroup, g) +function Base.inv(G::LieGroup, g) h = allocate_result(G, inv, g) return inv!(G, h, g) end @@ -437,46 +421,143 @@ function inv! end @doc "$_doc_inv" inv!(G::LieGroup, h, g) -_doc_diff_inv = """ - diff_inv(G::LieGroup, g, X) - diff_inv!(G::LieGroup, Y, g, X) +_doc_inv_left_compose = """ + inv_left_compose(G::LieGroup, g, h) + inv_left_compose!(G::LieGroup, k, g, h) -Compute the differential of the function ``ι_{$(_math(:G))}(p) = p^-1``, where -``Dι_{$(_math(:G))}(p): $(_math(:𝔤)) → $(_math(:𝔤)). -This can be done in-place of `Y`. +Compute the inverse of the left group operation ``λ_g(h) = g$(_math(:∘))h``, +on the [`LieGroup`](@ref) `G`, that is, compute ``λ_g^{-1}(h) = g^{-1}$(_math(:∘))h``. +This can be done in-place of `k`. """ +@doc "$(_doc_inv_left_compose)" +function inv_left_compose(G::LieGroup, g, h) + k = ManifoldsBase.allocate_result(G, inv_left_compose, g, h) + return inv_left_compose!(G, k, g, h) +end -@doc "$_doc_diff_inv" -function diff_inv(::LieGroup, g, X) - Y = allocate_result(G, diff_inv, g, X) - return diff_inv!(G, Y, g, X) +function inv_left_compose! end +@doc "$(_doc_compose)" +function inv_left_compose!(G::LieGroup, k, g, h) + inv!(G, k, g) # g^{-1} in-place of k + compose!(G, k, k, h) # compose `k∘h` in-place of k + return k end -function diff_inv! end -@doc "$_doc_diff_inv" -diff_inv!(G::LieGroup, Y, g, X) +_doc_inv_right_compose = """ + inv_right_compose(G::LieGroup, h, g) + inv_right_compose!(G::LieGroup, k, h, g) -_doc_lie_bracket = """ - lie_bracket!(𝔤::LieAlgebra, X, Y) - lie_bracket!(𝔤::LieAlgebra, Z, X, Y) +Compute the inverse of the right group operation ``ρ_g(h) = h$(_math(:∘))g``, +on the [`LieGroup`](@ref) `G`, that is compute ``ρ_g^{-1}(h) = h$(_math(:∘))g^{-1}``. +This can be done in-place of `k`. +""" +@doc "$(_doc_inv_right_compose)" +function inv_right_compose(G::LieGroup, h, g) + k = ManifoldsBase.allocate_result(G, inv_right_compose, h, g) + return inv_right_compose!(G, k, h, g) +end + +function inv_right_compose! end +@doc "$(_doc_inv_right_compose)" +function inv_right_compose!(G::LieGroup, k, h, g) + inv!(G, k, g) # g^{-1} in-place of k + compose!(G, k, h, k) # compose `h∘k` in-place of k + return k +end -Compute the Lie bracket ``[⋅,⋅]: $(_math(:𝔤))×$(_math(:𝔤)) → $(_math(:𝔤))`` which fulfills +function is_identity end +@doc """ + is_identity(G::LieGroup, q; kwargs) + +Check whether `q` is the identity on the [`LieGroup`](@ref) ``$(_math(:G))``. +This means it is either the [`Identity`](@ref)`{O}` with the respect to the corresponding +[`AbstractGroupOperation`](@ref) `O`, or (approximately) the correct point representation. -1. ``[X,X] = 0`` for all ``X ∈ $(_math(:𝔤))`` -2. The Jacobi identity holds ``[X, [Y,Z]] = [[X,Y],Z] = [Y, [X,Z]]`` holds for all ``X, Y, Z ∈ $(_math(:𝔤))``. +# See also -The computation can be done in-place of `Z`. +[`identity_element`](@ref), [`identity_element!`](@ref) +""" +is_identity(G::LieGroup, q) + +function is_identity(G::LieGroup{𝔽,O}, h; kwargs...) where {𝔽,O<:AbstractGroupOperation} + return ManifoldsBase.isapprox(G, Identity{O}(), h; kwargs...) +end +function is_identity( + ::LieGroup{𝔽,O}, ::Identity{O}; kwargs... +) where {𝔽,O<:AbstractGroupOperation} + return true +end +# any other identity than the fitting one +function is_identity( + G::LieGroup{𝔽,<:AbstractGroupOperation}, + h::Identity{<:AbstractGroupOperation}; + kwargs..., +) where {𝔽} + return false +end + +""" + is_point(G::LieGroup, g; kwargs...) + +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`. +""" +ManifoldsBase.is_point(G::LieGroup, g; kwargs...) + +_doc_is_vector = """ + is_vector(G::LieGroup, X; kwargs...) + is_vector(G::LieGroup{𝔽,O}, e::Indentity{O}, X; kwargs...) + +Check whether `X` is a tangent vector, that is an element of the [`LieAlgebra`](@ref) +of `G`. +The first variant calls [`is_point`](@extref ManifoldsBase.is_point) on the [`LieAlgebra`](@ref) `𝔤` of `G`. +The second variant calls [`is_vector`](@extref ManifoldsBase.is_vector) on the $(_link(:AbstractManifold)) at the [`identity_element`](@ref). + +All keyword arguments are passed on to the corresponding call """ -function lie_bracket end -@doc "$(_doc_lie_bracket)" -function lie_bracket(𝔤::LieAlgebra, X, Y) - Z = ManifoldsBase.allocate_result(𝔤, lie_bracket, X, Y) - return lie_bracket!(𝔤, Z, X, Y) + +@doc "$(_doc_is_vector)" +ManifoldsBase.is_vector(G::LieGroup, X; kwargs...) = is_point(LieAlgebra(G), X; kwargs...) + +@doc "$(_doc_is_vector)" +function ManifoldsBase.is_vector( + G::LieGroup{𝔽,O}, e::Identity{O}, X; kwargs... +) where {𝔽,O<:AbstractGroupOperation} + return is_vector(G.manifold, identity_element(G), X; kwargs...) end -function lie_bracket! end -@doc "$(_doc_lie_bracket)" -lie_bracket!(𝔤::LieAlgebra, Z, X, Y) +""" + isapprox(M::LieGroup, g, h; kwargs...) + +Check if points `g` and `h` from [`LieGroup`](@ref) are approximately equal. +this function calls the corresponding $(_link(:isapprox)) on the $(_link(:AbstractManifold)) +after handling the cases where one or more +of the points are the [`Identity`](@ref). +All keyword argments are passed to this function as well. +""" +ManifoldsBase.isapprox(G::LieGroup, g, h; kwargs...) = isapprox(G.manifold, g, h; kwargs...) +function ManifoldsBase.isapprox( + G::LieGroup{𝔽,O}, g::Identity{O}, h; kwargs... +) where {𝔽,O<:AbstractGroupOperation} + return ManifoldsBase.isapprox(G.manifold, identity_element(G), h; kwargs...) +end +function ManifoldsBase.isapprox( + G::LieGroup{𝔽,O}, g, h::Identity{O}; kwargs... +) where {𝔽,O<:AbstractGroupOperation} + return ManifoldsBase.isapprox(G.manifold, g, identity_element(G); kwargs...) +end +function ManifoldsBase.isapprox( + G::LieGroup{𝔽,O}, g::Identity{O}, h::Identity{O}; kwargs... +) where {𝔽,O<:AbstractGroupOperation} + return true +end +function ManifoldsBase.isapprox( + G::LieGroup{𝔽,O}, g::Identity{O}, h::Identity{O2}; kwargs... +) where {𝔽,O<:AbstractGroupOperation,O2<:AbstractGroupOperation} + return false +end _doc_log = """ log(G::LieGroup, g, h) @@ -485,7 +566,7 @@ _doc_log = """ Compute the Lie group logarithmic map ```math -$(_tex(:log))_g h = $(_math(:∘))$(_tex(:log))_{$(_math(:G))}(g^{-1}$(_math(:∘))h) +$(_tex(:log))_g h = $(_tex(:log))_{$(_math(:G))}(g^{-1}$(_math(:∘))h) ``` where ``$(_tex(:log))_{$(_math(:G))}`` denotes the [Lie group logarithmic function](@ref log(::LieGroup, ::Identity, :Any)) @@ -530,5 +611,30 @@ function ManifoldsBase.log(G::LieGroup, e::Identity, g) return X end +# explicit method error to avoid stack overflow @doc "$(_doc_log_id)" -ManifoldsBase.log!(::LieGroup, X, ::Identity, g) +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) + +function ManifoldsBase.representation_size(G::LieGroup) + return representation_size(G.manifold) +end + +function Base.show(io::IO, G::LieGroup) + return print(io, "LieGroup($(G.manifold), $(G.op))") +end + +function ManifoldsBase.zero_vector( + G::LieGroup{𝔽,O}, ::Identity{O} +) where {𝔽,O<:AbstractGroupOperation} + return zero_vector(G, identity_element(G)) +end + +function ManifoldsBase.zero_vector!( + G::LieGroup{𝔽,O}, X, ::Identity{O} +) where {𝔽,O<:AbstractGroupOperation} + return zero_vector!(G, X, identity_element(G)) +end diff --git a/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl new file mode 100644 index 0000000..2344b11 --- /dev/null +++ b/test/LieGroupsTestSuite.jl/LieGroupsTestSuite.jl @@ -0,0 +1,773 @@ +""" + LieGroupsTestSuite.jl + +This module provides tools and dummy structures to test functionality provided +within `LieGroups.jl`. + +For every test function, several interactions to other functions can be activated. +The following functions are expected to be available, since their defaults just pass through to the manifold +* `is_point` both on the Lie group `G` and the Lie algebra `𝔤` +* `isapprox(G, g, h)` and `isapprox(𝔤, X, Y)` +* `copy(G, g)` +* `norm(𝔤, X)` +""" +module LieGroupsTestSuite +using LieGroups +using Test + +# +# +# Dummy Types +struct DummyOperation <: AbstractGroupOperation end +struct DummySecondOperation <: AbstractGroupOperation end +struct DummyManifold <: LieGroups.AbstractManifold{LieGroups.ℝ} end +struct DummyActionType <: AbstractGroupActionType end +const DummyLieGroup = LieGroup{LieGroups.ℝ,DummyOperation,DummyManifold} +struct DummyGroupAction <: AbstractGroupAction{DummyActionType,DummyLieGroup,DummyManifold} end + +# === Test single functions === +# +# +# --- A +""" + test_adjoint(G::LieGroup, g, X; kwargs...) + +Test `adjoint` function for a given Lie group element `g` and a Lie Algebra vector `X` + +# Keyword arguments +* `expected=missing` provide the value expected. If none is provided, the + default from `diff_conjugate` is used +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_adjoint(G::LieGroup, g, X; expected=missing, test_mutating::Bool=true) + @testset "adjoint" begin + v = if ismissing(expected) + diff_conjugate(G, g, identity_element(G), X) + else + expected + end + 𝔤 = LieAlgebra(G) + Y1 = adjoint(G, g, X) + @test is_vector(G, identity_element(G), Y1) + if test_mutating + Y2 = copy(𝔤, X) + adjoint!(G, Y2, g, X) + @test isapprox(𝔤, Y1, Y2) + end + @test isapprox(𝔤, Y1, v) + end + return nothing +end + +""" + test_apply(A::AbstractGroupAction, g, p; expected=missing) + +Test `apply`. + +# Keyword arguments +* `expected=missing`: the result of the application of the group action. +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_apply( + A::AbstractGroupAction, g, p; expected=missing, test_mutating::Bool=true +) + @testset "apply" begin + q1 = apply(A, g, p) + M = base_manifold(A) + @test is_point(M, q1) + if test_mutating + q2 = copy(M, p) + apply!(A, q2, g, p) + @test isapprox(M, q1, q2) + end + !ismissing(expected) && @test isapprox(M, q1, expected) + end +end +# +# +# --- C +""" + test_compose(G::LieGroup, g, h; kwargs...) + +Test `compose` for given Lie group elements `g`, `h`. + +# Keyword arguments + +* `test_identity::Bool=true`: test that composing with the identity yields the identity (requires `identity_element`) +* `test_inverse::Bool=true`: test that `g^{-1}g` is the identity (requires `inv`, `inv!`, and `is_identity`) +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_compose( + G::LieGroup, + g, + h; + test_inverse::Bool=true, + test_identity::Bool=true, + test_mutating::Bool=true, +) + @testset "compose" begin + k1 = compose(G, g, h) + k2 = copy(G, g) + compose!(G, k2, g, h) + @test isapprox(G, k1, k2) + if test_inverse + for g_ in [g, h] + g_inv = inv(G, g_) + k1 = compose(G, g_inv, g_) + @test is_identity(G, k1) + if test_mutating + compose!(G, k2, g_inv, g_) + @test isapprox(G, k1, k2) + @test is_identity(G, k2) + end + end + end + if test_identity && test_mutating + for g_ in [g, h] + for e in [Identity(G), identity_element(G)] + k1 = compose(G, g_, e) + compose!(G, k2, g_, e) + @test isapprox(G, k1, k2) + k1 = compose(G, e, g_) + compose!(G, k2, e, g_) + @test isapprox(G, k1, k2) + end + end + e = Identity(G) + k3 = copy(G, g) + compose!(G, k3, e, e) + @test is_identity(G, k3) + end + end + return nothing +end + +""" + test_conjugate(G::LieGroup, g, h; expected=missing) + +Test `conjugate`. + +# Keyword arguments +* `expected=missing`: the result of the conjugate can also be provided directly, + then neither `compose` nor `inv` are not required. +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_conjugate(G::LieGroup, g, h; expected=missing, test_mutating::Bool=true) + @testset "conjugate" begin + v = if ismissing(expected) + compose(G, g, compose(G, h, inv(G, g))) + else + expected + end + k1 = conjugate(G, g, h) + @test is_point(G, k1) + if test_mutating + k2 = copy(G, g) + conjugate!(G, k2, g, h) + @test isapprox(G, k1, k2) + end + @test isapprox(G, k1, v) + end + return nothing +end + +# +# +# --- D +""" + test_diff_apply(A::AbstractGroupAction, g, p, X; expected=missing) + +Test `diff_apply`. + +# Keyword arguments +* `expected=missing`: the result of the application of the group action. +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_diff_apply( + A::AbstractGroupAction, g, p, X; expected=missing, test_mutating::Bool=true +) + @testset "diff_apply" begin + Y1 = diff_apply(A, g, p, X) + M = base_manifold(A) + q = apply(A, g, p) + G = base_lie_group(A) + is_vector(G, identity_element(G), Y1) + if test_mutating + Y2 = copy(M, p, X) + diff_apply!(A, Y2, g, p, X) + @test isapprox(M, q, Y1, Y2) + end + !ismissing(expected) && @test isapprox(M, q, Y1, expected) + end +end + +""" + test_diff_apply(A::AbstractGroupAction, g, p, X; expected=missing) + +Test `diff_group_apply`. + +# Keyword arguments +* `expected=missing`: the result of the application of the group action. +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_diff_group_apply( + A::AbstractGroupAction, g, p, X; expected=missing, test_mutating::Bool=true +) + @testset "diff_group_apply" begin + Y1 = diff_group_apply(A, g, p, X) + G = base_lie_group(A) + @test is_vector(G, identity_element(G), Y1) + 𝔤 = LieAlgebra(G) + if test_mutating + Y2 = copy(𝔤, X) + diff_group_apply!(A, Y2, g, p, X) + @test isapprox(𝔤, Y1, Y2) + end + !ismissing(expected) && @test isapprox(𝔤, Y1, expected) + end +end + +""" + test_diff_inv(G::LieGroup, g, X; expected=missing) + +Test `diff_inv`. + +# Keyword arguments +* `expected=missing`: the result of the differential of the inverse, if not provided, + only consistency between the allocating and the in-place variant is checked. +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_diff_inv(G::LieGroup, g, X; expected=missing, test_mutating::Bool=true) + @testset "diff_inv" begin + 𝔤 = LieAlgebra(G) + Y1 = diff_inv(G, g, X) + @test is_vector(G, identity_element(G), Y1) + if test_mutating + Y2 = zero_vector(𝔤) + Y2 = diff_inv!(G, Y2, g, X) + @test isapprox(𝔤, Y1, Y2) + end + if !ismissing(expected) + @test isapprox(𝔤, Y1, expected) + end + end +end + +""" + test_diff_left_compose(G::LieGroup, g, h, X; expected=missing) + +Test `diff_left_compose`. + +# Keyword arguments +* `expected=missing`: the result of the differential of the compose's left argument, + if not provided, only consistency between the allocating and the in-place variant is checked. +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_diff_left_compose( + G::LieGroup, g, h, X; expected=missing, test_mutating::Bool=true +) + @testset "diff_left_compose" begin + 𝔤 = LieAlgebra(G) + Y1 = diff_left_compose(G, g, h, X) + @test is_vector(G, identity_element(G), Y1) + if test_mutating + Y2 = zero_vector(𝔤) + diff_left_compose!(G, Y2, g, h, X) + @test isapprox(LieAlgebra(G), Y1, Y2) + end + if !ismissing(expected) + @test isapprox(LieAlgebra(G), Y1, expected) + end + end +end + +""" + test_diff_right_compose(G::LieGroup, g, h, X; expected=missing) + +Test `diff_right_compose`. + +# Keyword arguments +* `expected=missing`: the result of the differential of the compose's right argument, + if not provided, only consistency between the allocating and the in-place variant is checked. +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_diff_right_compose( + G::LieGroup, g, h, X; expected=missing, test_mutating::Bool=true +) + @testset "diff_right_compose" begin + 𝔤 = LieAlgebra(G) + Y1 = diff_right_compose(G, g, h, X) + @test is_vector(G, identity_element(G), Y1) + if test_mutating + Y2 = zero_vector(𝔤) + diff_right_compose!(G, Y2, g, h, X) + @test isapprox(𝔤, Y1, Y2) + end + if !ismissing(expected) + @test isapprox(𝔤, Y1, expected) + end + end +end + +""" + test_copyto(G::LieGroup, g) + +Test that `copyto!` works also when copying over an `Identity`. + +The point `g` can be any point _but_ the `identity_element`. +The group has to be a mutating one, that is, not work on isbit types. +""" +function test_copyto(G::LieGroup, g) + @testset "copyto!" begin + k = copy(G, g) + e = Identity(G) + copyto!(G, k, e) + # also check that this holds both ways + @test isapprox(G, k, Identity(G)) + @test isapprox(G, Identity(G), k) + # Test that copying back also works + copyto!(G, k, g) + @test isapprox(G, k, g) + # copy into identity only works if provided the identity + @test copyto!(G, e, Identity(G)) == e + # but then also always returns e + copyto!(G, k, e) + @test copyto!(G, e, k) == e + # and fails if the point to copy in is not (numerically) e + @test_throws DomainError copyto!(G, e, g) + return nothing + end +end + +# +# +# --- E +""" + test_exp_log(G::LieGroup, g, h, X) + +Test `exp` and `log` for given Lie group elements `g`, `h` and +a vector `X` from the Lie Algebra. + +!!! note + This function requires `is_point(G, g)` and `is_point(LieAlgebra(G), X)` to be implemented + +# Keyword arguments + +* `test_exp::Bool=true`: test the exponential map yields a point on `G` +* `test_log::Bool=true`: test the logarithmic map. +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_exp_log( + G::LieGroup, g, h, X; test_exp::Bool=true, test_mutating::Bool=true, test_log::Bool=true +) + @testset "(Lie group) exp & log" begin + 𝔤 = LieAlgebra(G) + e = Identity(G) + if test_exp + # Lie group exp + k1 = exp(G, e, X) + if test_mutating + k2 = copy(G, g) + exp!(G, k2, e, X) + @test isapprox(G, k1, k2) + end + @test is_point(G, k1) + # exp + k1 = exp(G, g, X) + if test_mutating + k2 = copy(G, g) + exp!(G, k2, g, X) + @test isapprox(G, k1, k2) + end + @test is_point(G, k1) + end + if test_log + # Lie group log + Y1 = log(G, e, g) + if test_mutating + Y2 = zero_vector(G, e) + log!(G, Y2, e, g) + @test isapprox(𝔤, Y1, Y2) + end + @test is_point(𝔤, Y1) + @test norm(𝔤, log(G, g, g)) ≈ 0 + @test norm(𝔤, log(G, h, h)) ≈ 0 + # log + Y1 = log(G, g, h) + if test_mutating + Y2 = copy(G, g) + log!(G, Y2, g, h) + @test isapprox(𝔤, Y1, Y2) + end + @test is_point(𝔤, Y1) + # or equivalently + @test is_vector(G, Y1) + @test is_vector(G, Identity(G), Y1) + @test norm(𝔤, log(G, g, g)) ≈ 0 + @test norm(𝔤, log(G, h, h)) ≈ 0 + end + if test_exp && test_log + # Lie group exp / log + k1 = exp(G, e, X) + Y1 = log(G, e, k1) + @test isapprox(𝔤, X, Y1) + if test_mutating + k2 = copy(G, g) + exp!(G, k2, e, X) + Y2 = copy(G, g) + log!(G, Y2, e, k2) + @test isapprox(𝔤, Y1, Y2) + end + # exp & log + k1 = exp(G, g, X) + Y1 = log(G, g, k1) + @test isapprox(𝔤, X, Y1) + if test_mutating + k2 = copy(G, g) + exp!(G, k2, g, X) + Y2 = copy(G, g) + log!(G, Y2, g, k2) + @test isapprox(𝔤, Y1, Y2) + end + end + end + return nothing +end + +# +# +# --- I +""" + test_inv_compose(G::LieGroup, g, h, X) + +Test the special functions combining inv and compose, `inv_left_compose` and `inv_right_compose`. +For these tests both `compose` and `inv` are required. + +# Keyword arguments + +* `test_left::Bool=true`: test ``g^{-1}∘h`` +* `test_mutating::Bool=true`: test the mutating functions +* `test_right::Bool=true`: test ``g∘h^{-1}`` +""" +function test_inv_compose( + G::LieGroup, + g, + h; + expected_left=missing, + expected_right=missing, + test_left::Bool=true, + test_mutating::Bool=true, + test_right::Bool=true, +) + @testset "test compose inv combinations" begin + if test_left + v = if ismissing(expected_left) + compose(G, inv(G, g), h) + else + expected_left + end + @testset "g^{-1}∘h" begin + k1 = inv_left_compose(G, g, h) + @test isapprox(G, k1, v) + if test_mutating + k2 = copy(G, g) + inv_left_compose!(G, k2, g, h) + @test isapprox(G, k1, k2) + end + end + end + if test_right + v = if ismissing(expected_right) + compose(G, g, inv(G, h)) + else + expected_right + end + @testset "g∘h^{-1}" begin + k1 = inv_right_compose(G, g, h) + @test isapprox(G, k1, v) + if test_mutating + k2 = copy(G, g) + inv_right_compose!(G, k2, g, h) + @test isapprox(G, k1, k2) + end + end + end + end + return nothing +end + +# +# +# --- L +""" + test_lie_bracket(G::LieGroup, X, Y; expected=missing) + +Test `lie_bracket`. + +# Keyword arguments +* `expected=missing`: the result of the lie bracket + if not provided, only consistency between the allocating and the in-place variant is checked. +* `test_mutating::Bool=true`: test the mutating functions +""" +function test_lie_bracket(G::LieGroup, X, Y; expected=missing, test_mutating::Bool=true) + @testset "lie_bracket" begin + 𝔤 = LieAlgebra(G) + Z1 = lie_bracket(𝔤, X, Y) + if test_mutating + Z2 = copy(𝔤, X) + lie_bracket!(𝔤, Z2, X, Y) + @test isapprox(𝔤, Z1, Z2) + end + if !ismissing(expected) + @test isapprox(𝔤, Z1, expected) + end + end +end + +# +# +# --- S +""" + test_show(G, repr_string::AbstractString) + +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. +""" +function test_show(G::Union{AbstractGroupAction,LieGroup}, repr_string::AbstractString) + @testset "repr(G, g, h)" begin + @test repr(G) == repr_string + end + return nothing +end + +# The global test function for a Lie group +# +# +""" + test_lie_group(G::LieGroup, properties::Dict, expectations::Dict) + +Test the Lie group ``G`` based on a `Dict` of properties and a `Dict` of `expectations + +Possible properties are + +* `:Functions` is a vector of all defined functions for `G` + Note that if `f` is in `:Functions`, and `f!` makes sense, for example for `compose`, + it is assumed that both are defined. +* `:Points` is a vector of at least three points on `G`, the first is not allowed to be the identity numerically +* `: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"` + +Possible `expectations` are + +* `:adjoint` for the result of `conjgate` in the case where `diff_conjugate` is not implemented +* `:atol` a global absolute tolerance, defaults to `1e-8` +* `:conjugate` for the result of `conjgate in the case where `compose`, `inv` are not implemented +* `:diff_inv` for the result of `diff_inv` with respect to the first point and the first vector. +* `:diff_left_compose` for the result of `diff_left_compose` with respect to the first two points and the first vector. +* `:diff_right_compose` for the result of `diff_right_compose` with respect to the first two points and the first vector. +* `: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)` +""" +function test_lie_group(G::LieGroup, properties::Dict, expectations::Dict=Dict()) + a_tol = get(expectations, :atol, 1e-8) + mutating = get(properties, :Mutating, true) + functions = get(properties, :Functions, Function[]) + points = get(properties, :Points, []) + @assert length(points) > 2 + vectors = get(properties, :Vectors, []) + @assert length(vectors) > 2 + test_name = get(properties, :Name, "$G") + @testset "$(test_name)" begin + # Call function tests based on their presence in alphabetical order + # + # + # --- A + if (adjoint in functions) + v = get(expectations, :adjoint, missing) + test_adjoint(G, points[1], vectors[1]; expected=v, test_mutating=mutating) + end + # + # + # --- C + if (compose in functions) + ti = all(in.([inv, is_identity], Ref(functions))) + test_compose(G, points[1], points[2]; test_inverse=ti, test_mutating=mutating) + end + # since there is a default, also providing compose&inv suffices + if (conjugate in functions) || (all(in.([compose, inv], Ref(functions)))) + v = get(expectations, :conjugate, missing) + test_conjugate(G, points[1], points[2]; expected=v, test_mutating=mutating) + end + # Either `copyto` or the default with `identity_element` available + if any(in.([copyto!, identity_element], Ref(functions))) && (mutating) + test_copyto(G, points[1]) + end + # + # + # --- D + if (diff_inv in functions) + v = get(expectations, :diff_inv, missing) + test_diff_inv(G, points[1], vectors[1]; expected=v, test_mutating=mutating) + end + + if (diff_left_compose in functions) + v = get(expectations, :diff_left_compose, missing) + test_diff_left_compose( + G, points[1], points[2], vectors[1]; expected=v, test_mutating=mutating + ) + end + + if (diff_right_compose in functions) + v = get(expectations, :diff_right_compose, missing) + test_diff_right_compose( + G, points[1], points[2], vectors[1]; expected=v, test_mutating=mutating + ) + end + + # + # + # --- E + if any(in.([exp, log], Ref(functions))) + test_exp_log( + G, + points[1], + points[2], + vectors[1]; + test_exp=(exp in functions), + test_log=(log in functions), + test_mutating=mutating, + ) + end + + # + # + # --- `I` + if any(in.([inv_left_compose, inv_right_compose], Ref(functions))) + vl = get(expectations, :inv_left_compose, missing) + vr = get(expectations, :inv_right_compose, missing) + test_inv_compose( + G, + points[1], + points[2]; + expected_left=vl, + expected_right=vr, + test_left=(inv_left_compose in functions), + test_mutating=mutating, + test_right=(inv_right_compose in functions), + ) + end + # + # + # --- L + if (lie_bracket in functions) + v = get(expectations, :lie_bracket, missing) + test_lie_bracket(G, vectors[1], vectors[2]; expected=v, test_mutating=mutating) + end + + # + # + # --- S + if (any(in.([show, repr], Ref(functions)))) && haskey(expectations, :repr) + test_show(G, expectations[:repr]) + end + end +end + +""" + test_group_action(G::LieGroup, properties::Dict, expectations::Dict) + +Test the Lie group ``G`` based on a `Dict` of properties and a `Dict` of `expectations`. + +Possible properties are + +* `:AlgebraVectors` is a vector of at least 3 elements from the Lie algebra `𝔤` of `G` +* `:Functions` is a vector of all defined functions for `G` + Note that if `f` is in `:Functions`, and `f!` makes sense, for example for `compose`, + it is assumed that both are defined. +* `:GroupPoints` is a vector of at least three points on `G`, the first is not allowed to be the identity numerically +* `:ManifoldPoints` is a vector of at least three points on `M` +* `:TangentVectors` is a vector of at least three tangent vectors on `M`, each in the tangent space of the corresponting `:ManifoldPoint` +* `: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"` + +Possible `expectations` are + +* `:apply` for the result of `apply` on the first group and manifold point +* `:diff_apply` for the result of `apply` on the first group and manifold point together with the first tangent vector +* `:atol` a global absolute tolerance, defaults to `1e-8` +* `:group` is the `LieGroup` describing the action +* `:manifold` is the `AbstractManifold` the action acts upon +* `:repr` is a sting one gets from `repr(G)` +""" +function test_group_action( + A::AbstractGroupAction, properties::Dict, expectations::Dict=Dict() +) + a_tol = get(expectations, :atol, 1e-8) + mutating = get(properties, :Mutating, true) + functions = get(properties, :Functions, Function[]) + group_points = get(properties, :GroupPoints, []) + @assert length(group_points) > 2 + algebra_vectors = get(properties, :AlgebraVectors, []) + @assert length(algebra_vectors) > 2 + manifold_points = get(properties, :ManifoldPoints, []) + @assert length(manifold_points) > 2 + tangent_vectors = get(properties, :TangentVectors, []) + @assert length(tangent_vectors) > 2 + test_name = get(properties, :Name, "$A") + @testset "$(test_name)" begin + # Call function tests based on their presence in alphabetical order + # + # + # --- A + if (apply in functions) + v = get(expectations, :apply, missing) + test_apply( + A, group_points[1], manifold_points[1]; expected=v, test_mutating=mutating + ) + end + if (diff_apply in functions) + v = get(expectations, :diff_apply, missing) + test_diff_apply( + A, + group_points[1], + manifold_points[1], + tangent_vectors[1]; + expected=v, + test_mutating=mutating, + ) + end + if (diff_group_apply in functions) + v = get(expectations, :diff_group_apply, missing) + test_diff_group_apply( + A, + group_points[1], + manifold_points[1], + algebra_vectors[1]; + expected=v, + test_mutating=mutating, + ) + end + if (base_lie_group in functions) + v = get(expectations, :group, missing) + !ismissing(v) && @testset "base_lie_group" begin + @test base_lie_group(A) == v + end + end + if (base_manifold in functions) + v = get(expectations, :manifold, missing) + !ismissing(v) && @testset "base_manifold" begin + @test base_manifold(A) == v + end + end + if (any(in.([show, repr], Ref(functions)))) && haskey(expectations, :repr) + test_show(A, expectations[:repr]) + end + end +end + +export test_lie_group, test_group_action +end # module diff --git a/test/actions/test_action_interface.jl b/test/actions/test_action_interface.jl new file mode 100644 index 0000000..dee799a --- /dev/null +++ b/test/actions/test_action_interface.jl @@ -0,0 +1,7 @@ +using LieGroups, Test + +s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +@testset "Group Action Interface" begin end diff --git a/test/actions/test_operation_action.jl b/test/actions/test_operation_action.jl new file mode 100644 index 0000000..8cacc84 --- /dev/null +++ b/test/actions/test_operation_action.jl @@ -0,0 +1,35 @@ +using LieGroups, Test + +s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +@testset "Group Operation as a Group Action" begin + a1 = RightGroupOperation() + a2 = LeftGroupOperation() + @test switch(a1) == a2 + @test switch(a2) == a1 + a3 = inv(a1) + @test inv(a3) == a1 + a4 = inv(a2) + @test inv(a4) == a2 + @test switch(a3) == a4 + @test switch(a4) == a3 + + M = LieGroupsTestSuite.DummyManifold() + op = LieGroupsTestSuite.DummyOperation() + G = LieGroup(M, op) + A1 = GroupOperationAction(a1, G) + A2 = GroupOperationAction(a2, G) + A3 = GroupOperationAction(a3, G) + A4 = GroupOperationAction(a4, G) + @test inv(A1) == A3 + @test inv(A2) == A4 + @test inv(A3) == A1 + @test inv(A4) == A2 + As = [A1, A2, A3, A4] + for A in As + @test base_lie_group(A) == G + @test base_manifold(A) == G + end +end diff --git a/test/groups/test_translation_group.jl b/test/groups/test_translation_group.jl new file mode 100644 index 0000000..a349918 --- /dev/null +++ b/test/groups/test_translation_group.jl @@ -0,0 +1,67 @@ +using LieGroups, Test + +s = joinpath(@__DIR__, "..", "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +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], + :Functions => [ + adjoint, + compose, + conjugate, + diff_inv, + diff_left_compose, + diff_right_compose, + exp, + identity_element, + inv, + inv_left_compose, + inv_right_compose, + is_identity, + lie_bracket, + log, + show, + ], + ) + expectations = Dict( + :repr => "TranslationGroup(3; field=ℝ)", + :diff_inv => -X1, + :diff_left_compose => X1, + :diff_right_compose => X1, + :lie_bracket => zero(X1), + ) + test_lie_group(G, properties, expectations) + + properties2 = Dict( + :AlgebraVectors => [X1, X2, X3], + :Functions => + [apply, diff_apply, diff_group_apply, base_lie_group, base_manifold, show], + :GroupPoints => [g1, g2, g3], + :ManifoldPoints => [g1, g2, g3], + :TangentVectors => [X1, X2, X3], + :Name => "", + ) + expectations2 = Dict(:manifold => G, :group => G, :repr => "") + @testset "Translation group operation action" begin + # A first group action Test + for t in [ + RightGroupOperation(), + LeftGroupOperation(), + InverseLeftGroupOperation(), + InverseRightGroupOperation(), + ] + A = GroupOperationAction(t, G) + properties2[:Name] = "with $A" + expectations2[:repr] = "GroupOperationAction($t, $G)" + test_group_action(A, properties2, expectations2) + end + end +end diff --git a/test/operations/test_addidion_operation.jl b/test/operations/test_addidion_operation.jl new file mode 100644 index 0000000..3d60218 --- /dev/null +++ b/test/operations/test_addidion_operation.jl @@ -0,0 +1,16 @@ +using LieGroups, Test + +@testset "Addition Operation" begin + @testset "Base.:+ and Base.:- with the Identity" begin + e = Identity(AdditionGroupOperation()) + @test (+e) === e + @test (e + e) === e + g = 2 + @test (g + e) == g + @test (e + g) == g + @test (g - e) == g + @test (e - g) == -g + @test (e - e) === e + @test (-e) === e + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 7058a48..21773ca 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,3 +1,27 @@ -using Manifolds, LieGroups, Test +using LieGroups, Test -@testset "Lie Groups" begin end +s = joinpath(@__DIR__, "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +function include_test(path) + @info "Testing $path" + @time include(path) # show basic timing, (this prints a newline at end) +end + +@testset "LieGroups.jl" begin + @testset "Lie Group Interface" begin + include_test("test_interface.jl") + end + @testset "Generic Group Operations" begin + include_test("operations/test_addidion_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_translation_group.jl") + end + include("test_aqua.jl") +end diff --git a/test/test_aqua.jl b/test/test_aqua.jl new file mode 100644 index 0000000..cb44ece --- /dev/null +++ b/test/test_aqua.jl @@ -0,0 +1,8 @@ +using Aqua, LieGroups, Test + +@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 + ])) +end diff --git a/test/test_interface.jl b/test/test_interface.jl new file mode 100644 index 0000000..442389d --- /dev/null +++ b/test/test_interface.jl @@ -0,0 +1,33 @@ +using LieGroups, Test + +s = joinpath(@__DIR__, "LieGroupsTestSuite.jl") +!(s in LOAD_PATH) && (push!(LOAD_PATH, s)) +using LieGroupsTestSuite + +@testset "Generic Lie Group Interface functions" begin + M = LieGroupsTestSuite.DummyManifold() + op = LieGroupsTestSuite.DummyOperation() + G = LieGroup(M, op) + rs = "LieGroup(LieGroupsTestSuite.DummyManifold(), LieGroupsTestSuite.DummyOperation())" + @test repr(G) == rs + 𝔤 = LieAlgebra(G) + op2 = LieGroupsTestSuite.DummySecondOperation() + rs2 = "LieAlgebra( LieGroup(LieGroupsTestSuite.DummyManifold(), LieGroupsTestSuite.DummyOperation()) )" + @test repr(𝔤) == rs2 + @test is_identity(G, Identity(op)) + @test !is_identity(G, Identity(op2)) + @test base_manifold(G) === M + e = Identity(op) + @test compose(G, e, e) == e + @test compose!(G, e, e, e) === e + @test isapprox(G, e, Identity(op)) + @test !isapprox(G, e, Identity(op2)) + @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) +end