Repository: JuliaManifolds/Manifolds.jl
Pull request #788: Add matrices of determinant one
Author: login: kellertuer
Labels: name: preview docs
description: Add this label if you want to see a PR-preview of the documentation
Created: 2025-03-21T16:29:20Z | Merged: 2025-03-28T05:02:53Z

@mateuszbaran 
I had an idea how to maybe (slowly) restructure and replace the traits from ManifoldsBase.

I was a bit bored to yet again write a wrapper to check for det being one, so here is a new idea how to maybe do that.
One could also put the tuple type as a first argument in a THHT trick sense.

What do you think? Or is this not efficient?
One difference is that this is not a nested but a tuple approach.

For now I wanted to first introduce that only for matrices sub typing `MatrixManifold` to slowly introduce that for others as well – maybe in the long run replacing the decorated manifold.


Diff:
diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml
index 688c5c4c06..83c104e2da 100644
--- a/.github/workflows/CompatHelper.yml
+++ b/.github/workflows/CompatHelper.yml
@@ -18,7 +18,7 @@ jobs:
       - name: "Run CompatHelper"
         run: |
           import CompatHelper
-          CompatHelper.main()
+          CompatHelper.main(; subdirs = ["", "docs", "tutorials"])
         shell: julia --color=yes {0}
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/NEWS.md b/NEWS.md
index af2356cb30..6ee396e631 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
 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.10.15] unreleased
+
+### Added
+
+* a `DeterminantOneMatrices` manifold of matrices of determinant one.
+
+### Changed
+
+* the following internal types were renamed since their super type also uses the singular
+  * `AbsoluteDeterminantOneMatrices` to `AbsoluteDeterminantOneMatrixType`
+  * `DeterminantOneMatrices` to `DeterminantOneMatrixType`
+
 ## [0.10.14] - 2025-02-18
 
 ## Changed
diff --git a/Project.toml b/Project.toml
index 8b581a6df7..98fa2c0b95 100644
--- a/Project.toml
+++ b/Project.toml
@@ -56,7 +56,7 @@ LinearAlgebra = "1.6"
 ManifoldDiff = "0.4.0"
 ManifoldsBase = "1"
 Markdown = "1.6"
-MatrixEquations = "2.2"
+MatrixEquations = "2.2 - 2.4.2"
 NLsolve = "4"
 NonlinearSolve = "1 - 3.7.3, 3.8.3"
 OrdinaryDiffEq = "6.31"
diff --git a/docs/Project.toml b/docs/Project.toml
index 41ab642a85..a65469e210 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -25,7 +25,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
 [compat]
 BoundaryValueDiffEq = "4, 5"
 CondaPkg = "0.2"
-DiffEqCallbacks = "2, 3"
+DiffEqCallbacks = "2, 3, 4"
 Distributions = "0.22.6, 0.23, 0.24, 0.25"
 Documenter = "1"
 DocumenterCitations = "1.3"
diff --git a/docs/make.jl b/docs/make.jl
index 58d7d3319d..b3ccde8a52 100755
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -177,6 +177,7 @@ makedocs(;
                 "Centered matrices" => "manifolds/centeredmatrices.md",
                 "Cholesky space" => "manifolds/choleskyspace.md",
                 "Circle" => "manifolds/circle.md",
+                "Determinant one matrices" => "manifolds/determinantone.md",
                 "Elliptope" => "manifolds/elliptope.md",
                 "Essential manifold" => "manifolds/essentialmanifold.md",
                 "Euclidean" => "manifolds/euclidean.md",
diff --git a/docs/src/manifolds/determinantone.md b/docs/src/manifolds/determinantone.md
new file mode 100644
index 0000000000..a80699e11f
--- /dev/null
+++ b/docs/src/manifolds/determinantone.md
@@ -0,0 +1,7 @@
+# Matrices of determinant one
+
+```@autodocs
+Modules = [Manifolds]
+Pages = ["manifolds/DeterminantOneMatrices.jl"]
+Order = [:type, :function]
+```
diff --git a/src/Manifolds.jl b/src/Manifolds.jl
index 22324a23cd..ea1cddecb5 100644
--- a/src/Manifolds.jl
+++ b/src/Manifolds.jl
@@ -444,6 +444,7 @@ include("manifolds/Lorentz.jl")
 include("manifolds/CenteredMatrices.jl")
 include("manifolds/CholeskySpace.jl")
 include("manifolds/Circle.jl")
+include("manifolds/DeterminantOneMatrices.jl")
 include("manifolds/Elliptope.jl")
 include("manifolds/EmbeddedTorus.jl")
 include("manifolds/FixedRankMatrices.jl")
@@ -642,6 +643,7 @@ export Euclidean,
     CenteredMatrices,
     CholeskySpace,
     Circle,
+    DeterminantOneMatrices,
     Elliptope,
     EssentialManifold,
     FixedRankMatrices,
diff --git a/src/groups/orthogonal.jl b/src/groups/orthogonal.jl
index 2448d0356a..37b6f1e678 100644
--- a/src/groups/orthogonal.jl
+++ b/src/groups/orthogonal.jl
@@ -1,5 +1,5 @@
 @doc raw"""
-    Orthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,AbsoluteDeterminantOneMatrices}
+    Orthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,AbsoluteDeterminantOneMatrixType}
 
 Orthogonal group $\mathrm{O}(n)$ represented by [`OrthogonalMatrices`](@ref).
 
@@ -7,7 +7,8 @@ Orthogonal group $\mathrm{O}(n)$ represented by [`OrthogonalMatrices`](@ref).
 
     Orthogonal(n::Int; parameter::Symbol=:type)
 """
-const Orthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,AbsoluteDeterminantOneMatrices}
+const Orthogonal{T} =
+    GeneralUnitaryMultiplicationGroup{T,ℝ,AbsoluteDeterminantOneMatrixType}
 
 function Orthogonal(n::Int; parameter::Symbol=:type)
     return GeneralUnitaryMultiplicationGroup(OrthogonalMatrices(n; parameter=parameter))
diff --git a/src/groups/special_orthogonal.jl b/src/groups/special_orthogonal.jl
index b93eb19986..b843939465 100644
--- a/src/groups/special_orthogonal.jl
+++ b/src/groups/special_orthogonal.jl
@@ -1,12 +1,12 @@
 @doc raw"""
-    SpecialOrthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,DeterminantOneMatrices}
+    SpecialOrthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,DeterminantOneMatrixType}
 
 Special orthogonal group ``\mathrm{SO}(n)`` represented by rotation matrices, see [`Rotations`](@ref).
 
 # Constructor
     SpecialOrthogonal(n)
 """
-const SpecialOrthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,DeterminantOneMatrices}
+const SpecialOrthogonal{T} = GeneralUnitaryMultiplicationGroup{T,ℝ,DeterminantOneMatrixType}
 
 function SpecialOrthogonal(n; parameter::Symbol=:type)
     return GeneralUnitaryMultiplicationGroup(Rotations(n; parameter=parameter))
diff --git a/src/groups/special_unitary.jl b/src/groups/special_unitary.jl
index be2d3af60d..88867be64c 100644
--- a/src/groups/special_unitary.jl
+++ b/src/groups/special_unitary.jl
@@ -1,5 +1,5 @@
 @doc raw"""
-    SpecialUnitary{n} = GeneralUnitaryMultiplicationGroup{n,ℝ,GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrices}}
+    SpecialUnitary{n} = GeneralUnitaryMultiplicationGroup{n,ℝ,GeneralUnitaryMatrices{n,ℂ,DeterminantOneMatrixType}}
 
 The special unitary group ``\mathrm{SU}(n)`` represented by unitary matrices of determinant +1.
 
@@ -18,11 +18,11 @@ or in other words we represent the tangent spaces employing the Lie algebra ``\m
 
 Generate the Lie group of ``n×n`` unitary matrices with determinant +1.
 """
-const SpecialUnitary{T} = GeneralUnitaryMultiplicationGroup{T,ℂ,DeterminantOneMatrices}
+const SpecialUnitary{T} = GeneralUnitaryMultiplicationGroup{T,ℂ,DeterminantOneMatrixType}
 
 function SpecialUnitary(n::Int; parameter::Symbol=:type)
     return GeneralUnitaryMultiplicationGroup(
-        GeneralUnitaryMatrices(n, ℂ, DeterminantOneMatrices; parameter=parameter),
+        GeneralUnitaryMatrices(n, ℂ, DeterminantOneMatrixType; parameter=parameter),
     )
 end
 
diff --git a/src/groups/unitary.jl b/src/groups/unitary.jl
index 3dd7255287..50be3b9b87 100644
--- a/src/groups/unitary.jl
+++ b/src/groups/unitary.jl
@@ -1,5 +1,5 @@
 @doc raw"""
-     Unitary{n,𝔽} = GeneralUnitaryMultiplicationGroup{n,𝔽,AbsoluteDeterminantOneMatrices}
+     Unitary{n,𝔽} = GeneralUnitaryMultiplicationGroup{n,𝔽,AbsoluteDeterminantOneMatrixType}
 
 The group of unitary matrices ``\mathrm{U}(n, 𝔽)``, either complex (when 𝔽=ℂ) or quaternionic
 (when 𝔽=ℍ)
@@ -24,7 +24,7 @@ Quaternionic unitary group is isomorphic to the compact symplectic group of the
 Construct ``\mathrm{U}(n, 𝔽)``.
 See also [`Orthogonal(n)`](@ref) for the real-valued case.
 """
-const Unitary{n,𝔽} = GeneralUnitaryMultiplicationGroup{n,𝔽,AbsoluteDeterminantOneMatrices}
+const Unitary{n,𝔽} = GeneralUnitaryMultiplicationGroup{n,𝔽,AbsoluteDeterminantOneMatrixType}
 
 function Unitary(n, 𝔽::AbstractNumbers=ℂ; parameter::Symbol=:type)
     return GeneralUnitaryMultiplicationGroup(UnitaryMatrices(n, 𝔽; parameter=parameter))
diff --git a/src/manifolds/DeterminantOneMatrices.jl b/src/manifolds/DeterminantOneMatrices.jl
new file mode 100644
index 0000000000..517f965841
--- /dev/null
+++ b/src/manifolds/DeterminantOneMatrices.jl
@@ -0,0 +1,134 @@
+@doc raw"""
+    DeterminantOneMatrices{𝔽,T} <: AbstractDecoratorManifold{𝔽}
+
+The [`AbstractManifold`](@extref `ManifoldsBase.AbstractManifold`)
+consisting of the real- or complex-valued (invertible) matrices od determinant one,
+that is the set
+
+```math
+\bigl\{p  ∈ 𝔽^{n×n}\ \big|\ \det(p) = 1 \bigr\},
+```
+where the field ``𝔽 ∈ \{ ℝ, ℂ\}``.
+
+Note that this is a subset of [`InvertibleMatrices`](@ref),
+and a superset of any of the [`GeneralUnitaryMatrices`](@ref)
+
+The tangent space at any point `p` is the set of matrices with trace 0.
+
+# Constructor
+
+    DeterminantOneMatrices(n::Int, field::AbstractNumbers=ℝ)
+
+Generate the manifold of ``n×n`` matrices of determinant one.
+"""
+struct DeterminantOneMatrices{𝔽,T} <: AbstractDecoratorManifold{𝔽}
+    size::T
+end
+
+function DeterminantOneMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type)
+    size = wrap_type_parameter(parameter, (n,))
+    return DeterminantOneMatrices{field,typeof(size)}(size)
+end
+
+function active_traits(f, ::DeterminantOneMatrices, args...)
+    return merge_traits(IsEmbeddedSubmanifold())
+end
+
+@doc raw"""
+    check_point(M::DeterminantOneMatrices{n,𝔽}, p; kwargs...)
+
+Check whether `p` is a valid manifold point on the [`DeterminantOneMatrices`](@ref) `M`, i.e.
+whether `p` has a determinant of ``1``.
+
+The check is perfomed with `isapprox` and all keyword arguments are passed to this
+"""
+function check_point(M::DeterminantOneMatrices, p; kwargs...)
+    if !isapprox(det(p), 1; kwargs...)
+        return DomainError(
+            det(p),
+            "The point $(p) does not lie on $(M), since its determinant is $(det(p)) and not 1.",
+        )
+    end
+    return nothing
+end
+
+"""
+    check_vector(M::DeterminantOneMatrices{n,𝔽}, p, X; kwargs... )
+
+Check whether `X` is a tangent vector to manifold point `p` on the
+[`DeterminantOneMatrices`](@ref) `M`, which are all matrices of size ``n×n``
+with trace 0.
+"""
+function check_vector(M::DeterminantOneMatrices, p, X; kwargs...)
+    if !isapprox(tr(X), 0; kwargs...)
+        return DomainError(
+            det(p),
+            "The point $(p) does not lie on $(M), since its determinant is $(det(p)) and not 1.",
+        )
+    end
+    return nothing
+end
+
+embed(::DeterminantOneMatrices, p) = p
+embed(::DeterminantOneMatrices, p, X) = X
+
+function get_embedding(::DeterminantOneMatrices{𝔽,TypeParameter{Tuple{n}}}) where {n,𝔽}
+    return Euclidean(n, n; field=𝔽)
+end
+function get_embedding(M::DeterminantOneMatrices{𝔽,Tuple{Int}}) where {𝔽}
+    n = get_parameter(M.size)[1]
+    return Euclidean(n, n; field=𝔽, parameter=:field)
+end
+
+@doc raw"""
+    manifold_dimension(M::DeterminantOneMatrices{n,𝔽})
+
+Return the dimension of the [`DeterminantOneMatrices`](@ref) matrix `M` over the number system
+`𝔽`, which is one dimension less than its embedding, the [`Euclidean`](@ref)`(n, n; field=𝔽)`.
+"""
+function manifold_dimension(M::DeterminantOneMatrices{<:Any,𝔽}) where {𝔽}
+    return manifold_dimension(get_embedding(M)) - 1
+end
+
+@doc raw"""
+    Random.rand(M::DeterminantOneMatrices; vector_at=nothing, kwargs...)
+
+If `vector_at` is `nothing`, return a random point on the [`DeterminantOneMatrices`](@ref)
+manifold `M` by using `rand` in the embedding.
+
+If `vector_at` is not `nothing`, return a random tangent vector from the tangent space of
+the point `vector_at` on the [`DeterminantOneMatrices`](@ref) by using by using `rand` in the
+embedding.
+"""
+rand(M::DeterminantOneMatrices; kwargs...)
+
+function Random.rand!(M::DeterminantOneMatrices, pX; kwargs...)
+    return rand!(Random.default_rng(), M, pX; kwargs...)
+end
+function Random.rand!(
+    rng::AbstractRNG,
+    M::DeterminantOneMatrices,
+    pX;
+    vector_at=nothing,
+    kwargs...,
+)
+    rand!(rng, get_embedding(M), pX; kwargs...)
+    if vector_at === nothing # for points ensure invertibility
+        n = size(pX)[1]
+        _ensure_nonzero_rng_determinant!(rng, get_embedding(M), pX; kwargs...)
+        det_pX = det(pX)
+        pX[1, :] ./= sign(det_pX)
+        pX ./= abs(det_pX)^(1 / n)
+    else # tangent vectors: trace 0
+        pX[diagind(pX)] .= 0
+    end
+    return pX
+end
+
+function Base.show(io::IO, ::DeterminantOneMatrices{𝔽,TypeParameter{Tuple{n}}}) where {n,𝔽}
+    return print(io, "DeterminantOneMatrices($(n), $(𝔽))")
+end
+function Base.show(io::IO, M::DeterminantOneMatrices{𝔽,Tuple{Int}}) where {𝔽}
+    n = get_parameter(M.size)[1]
+    return print(io, "DeterminantOneMatrices($(n), $(𝔽); parameter=:field)")
+end
diff --git a/src/manifolds/GeneralUnitaryMatrices.jl b/src/manifolds/GeneralUnitaryMatrices.jl
index 16b8c39d82..0751b80c3b 100644
--- a/src/manifolds/GeneralUnitaryMatrices.jl
+++ b/src/manifolds/GeneralUnitaryMatrices.jl
@@ -1,31 +1,31 @@
 @doc """
     AbstractMatrixType
 
-A plain type to distinguish different types of matrices, for example [`DeterminantOneMatrices`](@ref)
-and [`AbsoluteDeterminantOneMatrices`](@ref).
+A plain type to distinguish different types of matrices, for example [`DeterminantOneMatrixType`](@ref)
+and [`AbsoluteDeterminantOneMatrixType`](@ref).
 """
 abstract type AbstractMatrixType end
 
 @doc """
-    DeterminantOneMatrices <: AbstractMatrixType
+    DeterminantOneMatrixType <: AbstractMatrixType
 
 A type to indicate that we require special (orthogonal / unitary) matrices, i.e. of determinant 1.
 """
-struct DeterminantOneMatrices <: AbstractMatrixType end
+struct DeterminantOneMatrixType <: AbstractMatrixType end
 
 @doc """
-    AbsoluteDeterminantOneMatrices <: AbstractMatrixType
+    AbsoluteDeterminantOneMatrixType <: AbstractMatrixType
 
 A type to indicate that we require (orthogonal / unitary) matrices with normed determinant,
 i.e. that the absolute value of the determinant is 1.
 """
-struct AbsoluteDeterminantOneMatrices <: AbstractMatrixType end
+struct AbsoluteDeterminantOneMatrixType <: AbstractMatrixType end
 
 @doc raw"""
     GeneralUnitaryMatrices{T,𝔽,S<:AbstractMatrixType} <: AbstractDecoratorManifold
 
 A common parametric type for matrices with a unitary property of size ``n×n`` over the field ``𝔽``
-which additionally have the `AbstractMatrixType`, e.g. are `DeterminantOneMatrices`.
+which additionally have the `AbstractMatrixType`, e.g. are [`DeterminantOneMatrixType`](@ref).
 """
 struct GeneralUnitaryMatrices{T,𝔽,S<:AbstractMatrixType} <: AbstractDecoratorManifold{𝔽}
     size::T
@@ -56,7 +56,7 @@ i.e. that ``p`` has a determinant of absolute value one.
 The tolerance for the last test can be set using the `kwargs...`.
 """
 function check_point(
-    M::GeneralUnitaryMatrices{<:Any,𝔽,AbsoluteDeterminantOneMatrices},
+    M::GeneralUnitaryMatrices{<:Any,𝔽,AbsoluteDeterminantOneMatrixType},
     p;
     kwargs...,
 ) where {𝔽}
@@ -84,7 +84,7 @@ i.e. that ``p`` has a determinant of absolute value one, i.e. that ``p^{\mathrm{
 The tolerance for the last test can be set using the `kwargs...`.
 """
 function check_point(
-    M::GeneralUnitaryMatrices{<:Any,𝔽,DeterminantOneMatrices},
+    M::GeneralUnitaryMatrices{<:Any,𝔽,DeterminantOneMatrixType},
     p;
     kwargs...,
 ) where {𝔽}
@@ -661,7 +661,7 @@ Return the injectivity radius for general unitary matrix manifolds, which is[^1]
 injectivity_radius(::GeneralUnitaryMatrices) = π
 
 @doc raw"""
-    injectivity_radius(G::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices})
+    injectivity_radius(G::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrixType})
 
 Return the injectivity radius for general complex unitary matrix manifolds, where the determinant is ``+1``,
 which is[^1]
@@ -670,7 +670,7 @@ which is[^1]
     \operatorname{inj}_{\mathrm{SU}(n)} = π \sqrt{2}.
 ```
 """
-function injectivity_radius(::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices})
+function injectivity_radius(::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrixType})
     return π * sqrt(2.0)
 end
 
@@ -849,20 +849,20 @@ function manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℝ})
     return div(n * (n - 1), 2)
 end
 @doc raw"""
-    manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices})
+    manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrixType})
 
 Return the dimension of the manifold of special unitary matrices.
 ```math
 \dim_{\mathrm{SU}(n)} = n^2-1.
 ```
 """
-function manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices})
+function manifold_dimension(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrixType})
     n = get_parameter(M.size)[1]
     return n^2 - 1
 end
 
 @doc raw"""
-    manifold_volume(::GeneralUnitaryMatrices{<:Any,ℝ,AbsoluteDeterminantOneMatrices})
+    manifold_volume(::GeneralUnitaryMatrices{<:Any,ℝ,AbsoluteDeterminantOneMatrixType})
 
 Volume of the manifold of real orthogonal matrices of absolute determinant one. The
 formula reads [BoyaSudarshanTilma:2003](@cite):
@@ -875,12 +875,14 @@ formula reads [BoyaSudarshanTilma:2003](@cite):
 \end{cases}
 ```
 """
-function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℝ,AbsoluteDeterminantOneMatrices})
+function manifold_volume(
+    M::GeneralUnitaryMatrices{<:Any,ℝ,AbsoluteDeterminantOneMatrixType},
+)
     n = get_parameter(M.size)[1]
-    return 2 * manifold_volume(GeneralUnitaryMatrices(n, ℝ, DeterminantOneMatrices))
+    return 2 * manifold_volume(GeneralUnitaryMatrices(n, ℝ, DeterminantOneMatrixType))
 end
 @doc raw"""
-    manifold_volume(::GeneralUnitaryMatrices{<:Any,ℝ,DeterminantOneMatrices})
+    manifold_volume(::GeneralUnitaryMatrices{<:Any,ℝ,DeterminantOneMatrixType})
 
 Volume of the manifold of real orthogonal matrices of determinant one. The
 formula reads [BoyaSudarshanTilma:2003](@cite):
@@ -896,7 +898,7 @@ formula reads [BoyaSudarshanTilma:2003](@cite):
 It differs from the paper by a factor of `sqrt(2)` due to a different choice of
 normalization.
 """
-function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℝ,DeterminantOneMatrices})
+function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℝ,DeterminantOneMatrixType})
     n = get_parameter(M.size)[1]
     vol = 1.0
     if n % 2 == 0
@@ -918,7 +920,7 @@ function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℝ,DeterminantOneMatri
     return vol
 end
 @doc raw"""
-    manifold_volume(::GeneralUnitaryMatrices{<:Any,ℂ,AbsoluteDeterminantOneMatrices})
+    manifold_volume(::GeneralUnitaryMatrices{<:Any,ℂ,AbsoluteDeterminantOneMatrixType})
 
 Volume of the manifold of complex general unitary matrices of absolute determinant one. The
 formula reads [BoyaSudarshanTilma:2003](@cite)
@@ -927,7 +929,9 @@ formula reads [BoyaSudarshanTilma:2003](@cite)
 \sqrt{n 2^{n+1}} π^{n(n+1)/2} \prod_{k=1}^{n-1}\frac{1}{k!}.
 ```
 """
-function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℂ,AbsoluteDeterminantOneMatrices})
+function manifold_volume(
+    M::GeneralUnitaryMatrices{<:Any,ℂ,AbsoluteDeterminantOneMatrixType},
+)
     n = get_parameter(M.size)[1]
     vol = sqrt(n * 2^(n + 1)) * π^(((n + 1) * n) // 2)
     kf = 1
@@ -938,7 +942,7 @@ function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℂ,AbsoluteDeterminant
     return vol
 end
 @doc raw"""
-    manifold_volume(::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices})
+    manifold_volume(::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrixType})
 
 Volume of the manifold of complex general unitary matrices of determinant one. The formula
 reads [BoyaSudarshanTilma:2003](@cite)
@@ -947,7 +951,7 @@ reads [BoyaSudarshanTilma:2003](@cite)
 \sqrt{n 2^{n-1}} π^{(n-1)(n+2)/2} \prod_{k=1}^{n-1}\frac{1}{k!}.
 ```
 """
-function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrices})
+function manifold_volume(M::GeneralUnitaryMatrices{<:Any,ℂ,DeterminantOneMatrixType})
     n = get_parameter(M.size)[1]
     vol = sqrt(n * 2^(n - 1)) * π^(((n - 1) * (n + 2)) // 2)
     kf = 1
@@ -985,10 +989,10 @@ is
   \operatorname{proj}_{\mathrm{U}(n,𝔽)} \colon p ↦ U V^\mathrm{H}.
 ````
 """
-project(::GeneralUnitaryMatrices{<:Any,𝔽,AbsoluteDeterminantOneMatrices}, p) where {𝔽}
+project(::GeneralUnitaryMatrices{<:Any,𝔽,AbsoluteDeterminantOneMatrixType}, p) where {𝔽}
 
 function project!(
-    ::GeneralUnitaryMatrices{<:Any,𝔽,AbsoluteDeterminantOneMatrices},
+    ::GeneralUnitaryMatrices{<:Any,𝔽,AbsoluteDeterminantOneMatrixType},
     q,
     p,
 ) where {𝔽}
@@ -1080,7 +1084,7 @@ end
     riemann_tensor(::GeneralUnitaryMatrices, p, X, Y, Z)
 
 Compute the value of Riemann tensor on the [`GeneralUnitaryMatrices`](@ref) manifold.
-The formula reads [Rentmeesters:2011](@cite) 
+The formula reads [Rentmeesters:2011](@cite)
 
 ```math
 R(X,Y)Z=\frac{1}{4}[Z, [X, Y]].
diff --git a/src/manifolds/InvertibleMatrices.jl b/src/manifolds/InvertibleMatrices.jl
index f773302545..33dfab6f69 100644
--- a/src/manifolds/InvertibleMatrices.jl
+++ b/src/manifolds/InvertibleMatrices.jl
@@ -135,12 +135,17 @@ embedding.
 """
 rand(M::InvertibleMatrices; kwargs...)
 
-function Random.rand!(M::InvertibleMatrices, pX; kwargs...)
-    rand!(get_embedding(M), pX; kwargs...)
-    return pX
-end
-function Random.rand!(rng::AbstractRNG, M::InvertibleMatrices, pX; kwargs...)
+function Random.rand!(
+    rng::AbstractRNG,
+    M::InvertibleMatrices,
+    pX;
+    vector_at=nothing,
+    kwargs...,
+)
     rand!(rng, get_embedding(M), pX; kwargs...)
+    if vector_at === nothing # for points ensure invertibility
+        _ensure_nonzero_rng_determinant!(rng, get_embedding(M), pX; kwargs...)
+    end
     return pX
 end
 
diff --git a/src/manifolds/Orthogonal.jl b/src/manifolds/Orthogonal.jl
index ba9788db49..1d4d9baefc 100644
--- a/src/manifolds/Orthogonal.jl
+++ b/src/manifolds/Orthogonal.jl
@@ -1,5 +1,5 @@
 @doc raw"""
-     OrthogonalMatrices{n} = GeneralUnitaryMatrices{n,ℝ,AbsoluteDeterminantOneMatrices}
+     OrthogonalMatrices{n} = GeneralUnitaryMatrices{n,ℝ,AbsoluteDeterminantOneMatrixType}
 
 The manifold of (real) orthogonal ``n×n`` matrices ``\mathrm{O}(n)``. They are precisely the ``n×n`` real matrices ``M`` that satisfy
 
@@ -14,7 +14,7 @@ This is the same manifold as the [`Stiefel`](@ref)`(n, n, ℂ)` manifold.
 
     OrthogonalMatrices(n)
 """
-const OrthogonalMatrices{n} = GeneralUnitaryMatrices{n,ℝ,AbsoluteDeterminantOneMatrices}
+const OrthogonalMatrices{n} = GeneralUnitaryMatrices{n,ℝ,AbsoluteDeterminantOneMatrixType}
 
 function OrthogonalMatrices(n::Int; parameter::Symbol=:type)
     size = wrap_type_parameter(parameter, (n,))
diff --git a/src/manifolds/Rotations.jl b/src/manifolds/Rotations.jl
index 5cc00b6e50..0ba01267b3 100644
--- a/src/manifolds/Rotations.jl
+++ b/src/manifolds/Rotations.jl
@@ -10,7 +10,7 @@ real-valued orthogonal matrices with determinant ``+1``.
 
 Generate the manifold of ``n×n`` rotation matrices.
 """
-const Rotations{T} = GeneralUnitaryMatrices{T,ℝ,DeterminantOneMatrices}
+const Rotations{T} = GeneralUnitaryMatrices{T,ℝ,DeterminantOneMatrixType}
 
 function Rotations(n::Int; parameter::Symbol=:type)
     size = wrap_type_parameter(parameter, (n,))
diff --git a/src/manifolds/Unitary.jl b/src/manifolds/Unitary.jl
index a430660449..d030f2b8ca 100644
--- a/src/manifolds/Unitary.jl
+++ b/src/manifolds/Unitary.jl
@@ -1,6 +1,6 @@
 
 @doc raw"""
-    const UnitaryMatrices{n,𝔽} = GeneralUnitaryMatrices{T,𝔽,AbsoluteDeterminantOneMatrices}
+    const UnitaryMatrices{n,𝔽} = GeneralUnitaryMatrices{T,𝔽,AbsoluteDeterminantOneMatrixType}
 
 The manifold ``U(n,𝔽)`` of ``n×n`` complex matrices (when 𝔽=ℂ) or quaternionic matrices
 (when 𝔽=ℍ) such that
@@ -30,7 +30,7 @@ If you prefer the representation as `X` you can use the [`Stiefel`](@ref)`(n, n,
 
 see also [`OrthogonalMatrices`](@ref) for the real valued case.
 """
-const UnitaryMatrices{T,𝔽} = GeneralUnitaryMatrices{T,𝔽,AbsoluteDeterminantOneMatrices}
+const UnitaryMatrices{T,𝔽} = GeneralUnitaryMatrices{T,𝔽,AbsoluteDeterminantOneMatrixType}
 
 function UnitaryMatrices(n::Int, 𝔽::AbstractNumbers=ℂ; parameter::Symbol=:type)
     size = wrap_type_parameter(parameter, (n,))
diff --git a/src/utils.jl b/src/utils.jl
index 77bc1e01de..c8d66af68b 100644
--- a/src/utils.jl
+++ b/src/utils.jl
@@ -364,3 +364,15 @@ function estimated_sectional_curvature_matrix(
     end
     return result
 end
+
+function _ensure_nonzero_rng_determinant!(
+    rng,
+    M::AbstractManifold,
+    pX;
+    atol::Real=sqrt(eps(real(eltype(pX)))),
+    kwargs...,
+)
+    while abs(det(pX)) < atol
+        rand!(rng, M, pX; kwargs...)
+    end
+end
diff --git a/test/manifolds/determinant_one_matrices.jl b/test/manifolds/determinant_one_matrices.jl
new file mode 100644
index 0000000000..1042237246
--- /dev/null
+++ b/test/manifolds/determinant_one_matrices.jl
@@ -0,0 +1,61 @@
+using Manifolds, Random, Test, LinearAlgebra
+
+@testset "Invertible Matrices of Determinant one" begin
+    @testset "Real case" begin
+        M = DeterminantOneMatrices(2)
+        # is det 1 and inv
+        @test is_point(M, [1.0 0.0; 0.0 1.0]; error=:error)
+        # det 1 but for example not Rotation
+        @test is_point(M, [10.0 0.0; 0.0 0.1]; error=:error)
+        # Not invertible
+        @test_throws DomainError is_point(M, [10.0 0.0; 0.0 0.0]; error=:error)
+        # Det(2)
+        @test_throws DomainError is_point(M, [2.0 0.0; 0.0 1.0]; error=:error)
+        Random.seed!(42)
+        p = rand(M)
+        @test is_point(M, p)
+        X = rand(M; vector_at=p)
+        @test is_vector(M, p, X)
+        # not trace 0
+        Xf = [1.0 1.1; 1.2 0.0]
+        @test_throws DomainError is_vector(M, p, Xf; error=:error)
+        @test get_embedding(M) == Euclidean(2, 2)
+        @test manifold_dimension(M) == 3
+        @test manifold_dimension(DeterminantOneMatrices(2; parameter=:field)) == 3
+        @test repr(M) == "DeterminantOneMatrices(2, ℝ)"
+    end
+    @testset "Complex case" begin
+        M = DeterminantOneMatrices(2, ℂ)
+        # is det 1 and inv
+        @test is_point(M, [1.0im 0.0; 0.0 -1.0im], true)
+        # det 1 but for example not Rotation
+        @test is_point(M, [10.0im 0.0; 0.0 -0.1im], true)
+        # Not invertible
+        @test_throws DomainError is_point(M, [10.0 0.0; 0.0 0.0], true)
+        # Det(2)
+        @test_throws DomainError is_point(M, [2.0 0.0; 0.0 1.0], true)
+        Random.seed!(42)
+        p = rand(M)
+        @test is_point(M, p)
+        X = rand(M; vector_at=p)
+        @test is_vector(M, p, X)
+        Xf = [1.0+2.0im 1.1+0.1im; 1.2 0.0]
+        @test_throws DomainError is_vector(M, p, Xf; error=:error)
+        @test repr(M) == "DeterminantOneMatrices(2, ℂ)"
+    end
+
+    @testset "Field parameter" begin
+        M = DeterminantOneMatrices(2; parameter=:field)
+        @test repr(M) == "DeterminantOneMatrices(2, ℝ; parameter=:field)"
+    end
+    @testset "rng test" begin
+        M = DeterminantOneMatrices(2)
+        pX = zeros(2, 2)
+        Manifolds._ensure_nonzero_rng_determinant!(
+            Random.default_rng(),
+            get_embedding(M),
+            pX,
+        )
+        @test abs(det(pX)) > 1e-8
+    end
+end
diff --git a/test/runtests.jl b/test/runtests.jl
index 3e6f8a0655..30b11fca0d 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -142,6 +142,7 @@ end
         include_test("manifolds/centered_matrices.jl")
         include_test("manifolds/circle.jl")
         include_test("manifolds/cholesky_space.jl")
+        include_test("manifolds/determinant_one_matrices.jl")
         include_test("manifolds/elliptope.jl")
         include_test("manifolds/euclidean.jl")
         include_test("manifolds/fixed_rank.jl")


Reviews:
user: login: mateuszbaran
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-24T18:56:53Z
user: login: kellertuer
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-24T18:58:37Z
user: login: mateuszbaran
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-25T10:27:37Z
user: login: kellertuer
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-25T10:31:02Z
user: login: mateuszbaran
body: LGTM, let's just make sure coverage is fine.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-25T10:53:00Z
user: login: mateuszbaran
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-27T20:04:00Z

Review comments:
diff_hunk: @@ -0,0 +1,136 @@
+@doc raw"""
+    DeterminantOneMatrices{𝔽,T} <: AbstractDecoratorManifold{𝔽}
+
+The [`AbstractManifold`](@extref `ManifoldsBase.AbstractManifold`)
+consisting of the real- or complex-valued (invertible) matrices od determinant one,
+that is the set
+
+```math
+\bigl\{p  ∈ 𝔽^{n×n}\ \big|\ \det(p) = 1 \bigr\},
+```
+where the field ``𝔽 ∈ \{ ℝ, ℂ\}``.
+
+Note that this is a subset of [`InvertibleMatrices`](@ref),
+and a superset of any of the [`GeneralUnitaryMatrices`](@ref)
+
+The tangent space at any point `p` is the set of matrices with trace 0.
+
+# Constructor
+
+    DeterminantOneMatrices(n::Int, field::AbstractNumbers=ℝ)
+
+Generate the manifold of ``n×n`` matrices of determinant one.
+"""
+struct DeterminantOneMatrices{𝔽,T} <: AbstractDecoratorManifold{𝔽}
+    size::T
+end
+
+function DeterminantOneMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type)
+    size = wrap_type_parameter(parameter, (n,))
+    return DeterminantOneMatrices{field,typeof(size)}(size)
+end
+
+function active_traits(f, ::DeterminantOneMatrices, args...)
+    return merge_traits(IsEmbeddedSubmanifold())
+end
+
+@doc raw"""
+    check_point(M::DeterminantOneMatrices{n,𝔽}, p; kwargs...)
+
+Check whether `p` is a valid manifold point on the [`DeterminantOneMatrices`](@ref) `M`, i.e.
+whether `p` has a determinant of ``1``.
+
+The check is perfomed with `isapprox` and all keyword arguments are passed to this
+"""
+function check_point(M::DeterminantOneMatrices, p; kwargs...)
+    if !isapprox(det(p), 1; kwargs...)
+        return DomainError(
+            det(p),
+            "The point $(p) does not lie on $(M), since its determinant is $(det(p)) and not 1.",
+        )
+    end
+    return nothing
+end
+
+"""
+    check_vector(M::DeterminantOneMatrices{n,𝔽}, p, X; kwargs... )
+
+Check whether `X` is a tangent vector to manifold point `p` on the
+[`DeterminantOneMatrices`](@ref) `M`, which are all matrices of size ``n×n``
+with trace 0.
+"""
+function check_vector(M::DeterminantOneMatrices, p, X; kwargs...)
+    if !isapprox(tr(X), 0; kwargs...)
+        return DomainError(
+            det(p),
+            "The point $(p) does not lie on $(M), since its determinant is $(det(p)) and not 1.",
+        )
+    end
+    return nothing
+end
+
+embed(::DeterminantOneMatrices, p) = p
+embed(::DeterminantOneMatrices, p, X) = X
+
+function get_embedding(::DeterminantOneMatrices{𝔽,TypeParameter{Tuple{n}}}) where {n,𝔽}
+    return Euclidean(n, n; field=𝔽)
+end
+function get_embedding(M::DeterminantOneMatrices{𝔽,Tuple{Int}}) where {𝔽}
+    n = get_parameter(M.size)[1]
+    return Euclidean(n, n; field=𝔽, parameter=:field)
+end
+
+@doc raw"""
+    manifold_dimension(M::DeterminantOneMatrices{n,𝔽})
+
+Return the dimension of the [`DeterminantOneMatrices`](@ref) matrix `M` over the number system
+`𝔽`, which is one dimension less than its embedding, the [`Euclidean`](@ref)`(n, n; field=𝔽)`.
+"""
+function manifold_dimension(M::DeterminantOneMatrices{<:Any,𝔽}) where {𝔽}
+    return manifold_dimension(get_embedding(M)) - 1
+end
+
+@doc raw"""
+    Random.rand(M::DeterminantOneMatrices; vector_at=nothing, kwargs...)
+
+If `vector_at` is `nothing`, return a random point on the [`DeterminantOneMatrices`](@ref)
+manifold `M` by using `rand` in the embedding.
+
+If `vector_at` is not `nothing`, return a random tangent vector from the tangent space of
+the point `vector_at` on the [`DeterminantOneMatrices`](@ref) by using by using `rand` in the
+embedding.
+"""
+rand(M::DeterminantOneMatrices; kwargs...)
+
+function Random.rand!(M::DeterminantOneMatrices, pX; kwargs...)
+    return rand!(Random.default_rng(), M, pX; kwargs...)
+end
+function Random.rand!(
+    rng::AbstractRNG,
+    M::DeterminantOneMatrices,
+    pX;
+    vector_at=nothing,
+    kwargs...,
+)
+    rand!(rng, get_embedding(M), pX; kwargs...)
+    if vector_at == nothing # for points ensure invertibility
+        n = size(pX)[1]
+        e = eigen(Symmetric(pX' * pX)) # sym to get real EV
+        ev = real.(e.values)
+        (prod(ev) < 0) && (ev[1] *= -1)
+        # determinant is  already positive, rescale to one
+        ev .*= 1 / (prod(ev)^(1 / n))
+        pX .= e.vectors' * diagm(ev) * e.vectors
path: src/manifolds/DeterminantOneMatrices.jl
user: login: mateuszbaran
body: ```suggestion
while det(pX) < sqrt(eps(eltype(pX)))
            rand!(rng, get_embedding(M), pX; kwargs...)
        end
        if det(pX) < 0
            pX[1, :] .*= -1
        end
        pX ./= det(pX)^(1/n)
```
I think something like this would be much easier? And it seems a bit less prone to numerical errors.
created_at: 2025-03-24T18:56:53Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,136 @@
+@doc raw"""
+    DeterminantOneMatrices{𝔽,T} <: AbstractDecoratorManifold{𝔽}
+
+The [`AbstractManifold`](@extref `ManifoldsBase.AbstractManifold`)
+consisting of the real- or complex-valued (invertible) matrices od determinant one,
+that is the set
+
+```math
+\bigl\{p  ∈ 𝔽^{n×n}\ \big|\ \det(p) = 1 \bigr\},
+```
+where the field ``𝔽 ∈ \{ ℝ, ℂ\}``.
+
+Note that this is a subset of [`InvertibleMatrices`](@ref),
+and a superset of any of the [`GeneralUnitaryMatrices`](@ref)
+
+The tangent space at any point `p` is the set of matrices with trace 0.
+
+# Constructor
+
+    DeterminantOneMatrices(n::Int, field::AbstractNumbers=ℝ)
+
+Generate the manifold of ``n×n`` matrices of determinant one.
+"""
+struct DeterminantOneMatrices{𝔽,T} <: AbstractDecoratorManifold{𝔽}
+    size::T
+end
+
+function DeterminantOneMatrices(n::Int, field::AbstractNumbers=ℝ; parameter::Symbol=:type)
+    size = wrap_type_parameter(parameter, (n,))
+    return DeterminantOneMatrices{field,typeof(size)}(size)
+end
+
+function active_traits(f, ::DeterminantOneMatrices, args...)
+    return merge_traits(IsEmbeddedSubmanifold())
+end
+
+@doc raw"""
+    check_point(M::DeterminantOneMatrices{n,𝔽}, p; kwargs...)
+
+Check whether `p` is a valid manifold point on the [`DeterminantOneMatrices`](@ref) `M`, i.e.
+whether `p` has a determinant of ``1``.
+
+The check is perfomed with `isapprox` and all keyword arguments are passed to this
+"""
+function check_point(M::DeterminantOneMatrices, p; kwargs...)
+    if !isapprox(det(p), 1; kwargs...)
+        return DomainError(
+            det(p),
+            "The point $(p) does not lie on $(M), since its determinant is $(det(p)) and not 1.",
+        )
+    end
+    return nothing
+end
+
+"""
+    check_vector(M::DeterminantOneMatrices{n,𝔽}, p, X; kwargs... )
+
+Check whether `X` is a tangent vector to manifold point `p` on the
+[`DeterminantOneMatrices`](@ref) `M`, which are all matrices of size ``n×n``
+with trace 0.
+"""
+function check_vector(M::DeterminantOneMatrices, p, X; kwargs...)
+    if !isapprox(tr(X), 0; kwargs...)
+        return DomainError(
+            det(p),
+            "The point $(p) does not lie on $(M), since its determinant is $(det(p)) and not 1.",
+        )
+    end
+    return nothing
+end
+
+embed(::DeterminantOneMatrices, p) = p
+embed(::DeterminantOneMatrices, p, X) = X
+
+function get_embedding(::DeterminantOneMatrices{𝔽,TypeParameter{Tuple{n}}}) where {n,𝔽}
+    return Euclidean(n, n; field=𝔽)
+end
+function get_embedding(M::DeterminantOneMatrices{𝔽,Tuple{Int}}) where {𝔽}
+    n = get_parameter(M.size)[1]
+    return Euclidean(n, n; field=𝔽, parameter=:field)
+end
+
+@doc raw"""
+    manifold_dimension(M::DeterminantOneMatrices{n,𝔽})
+
+Return the dimension of the [`DeterminantOneMatrices`](@ref) matrix `M` over the number system
+`𝔽`, which is one dimension less than its embedding, the [`Euclidean`](@ref)`(n, n; field=𝔽)`.
+"""
+function manifold_dimension(M::DeterminantOneMatrices{<:Any,𝔽}) where {𝔽}
+    return manifold_dimension(get_embedding(M)) - 1
+end
+
+@doc raw"""
+    Random.rand(M::DeterminantOneMatrices; vector_at=nothing, kwargs...)
+
+If `vector_at` is `nothing`, return a random point on the [`DeterminantOneMatrices`](@ref)
+manifold `M` by using `rand` in the embedding.
+
+If `vector_at` is not `nothing`, return a random tangent vector from the tangent space of
+the point `vector_at` on the [`DeterminantOneMatrices`](@ref) by using by using `rand` in the
+embedding.
+"""
+rand(M::DeterminantOneMatrices; kwargs...)
+
+function Random.rand!(M::DeterminantOneMatrices, pX; kwargs...)
+    return rand!(Random.default_rng(), M, pX; kwargs...)
+end
+function Random.rand!(
+    rng::AbstractRNG,
+    M::DeterminantOneMatrices,
+    pX;
+    vector_at=nothing,
+    kwargs...,
+)
+    rand!(rng, get_embedding(M), pX; kwargs...)
+    if vector_at == nothing # for points ensure invertibility
+        n = size(pX)[1]
+        e = eigen(Symmetric(pX' * pX)) # sym to get real EV
+        ev = real.(e.values)
+        (prod(ev) < 0) && (ev[1] *= -1)
+        # determinant is  already positive, rescale to one
+        ev .*= 1 / (prod(ev)^(1 / n))
+        pX .= e.vectors' * diagm(ev) * e.vectors
path: src/manifolds/DeterminantOneMatrices.jl
user: login: kellertuer
body: I got lost for about an hour in this, do not ask me what is easier here.
created_at: 2025-03-24T18:58:37Z
author_association: MEMBER
diff_hunk: @@ -636,12 +637,14 @@ export AbstractCotangentVector,
     AbstractManifold, AbstractManifoldPoint, AbstractTangentVector
 # Manifolds
 export AbstractSphere, AbstractProjectiveSpace
+export MatrixManifold
path: src/Manifolds.jl
user: login: mateuszbaran
body: ```suggestion
```
created_at: 2025-03-25T10:27:36Z
author_association: MEMBER
diff_hunk: @@ -636,12 +637,14 @@ export AbstractCotangentVector,
     AbstractManifold, AbstractManifoldPoint, AbstractTangentVector
 # Manifolds
 export AbstractSphere, AbstractProjectiveSpace
+export MatrixManifold
path: src/Manifolds.jl
user: login: kellertuer
body: oops, missed that. Surprised Aqua did not complain that we export a non defined thingy.
created_at: 2025-03-25T10:31:02Z
author_association: MEMBER

PR comments:
user: login: codecov[bot]
created_at: 2025-03-21T17:54:07Z
body: ## [Codecov](https://app.codecov.io/gh/JuliaManifolds/Manifolds.jl/pull/788?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaManifolds) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 96.53%. Comparing base [(`5460a45`)](https://app.codecov.io/gh/JuliaManifolds/Manifolds.jl/commit/5460a45177a97f4454b89de3c1b3785a0826dbab?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaManifolds) to head [(`bf217e8`)](https://app.codecov.io/gh/JuliaManifolds/Manifolds.jl/commit/bf217e877692acea445d6cbca39ff8a0abefcd78?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaManifolds).
> Report is 1 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #788      +/-   ##
==========================================
+ Coverage   96.52%   96.53%   +0.01%     
==========================================
  Files         127      128       +1     
  Lines       11972    12015      +43     
==========================================
+ Hits        11556    11599      +43     
  Misses        416      416              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/JuliaManifolds/Manifolds.jl/pull/788?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaManifolds).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaManifolds).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: mateuszbaran
created_at: 2025-03-23T10:44:02Z
body: The main issue with this approach to traits is that it tries to appear modular but in practice it's not modular at all. The traits must be in a specific order for dispatch and any partial dispatches on properties are going to introduce massive ambiguity issues. Quite likely we could simplify `check_point`/`check_vector` call maze a bit with a generic `ArrayManifold` type but then I'd prefer to have `Properties` as a single `struct` instead of a tuple, roughly the same way `GeneralUnitaryMatrices` currently works. Maybe even we could start here by making `GeneralUnitaryMatrices` a special case of something like `GeneralMatrices` with `AbstractMatrixType` that is a subtype of `AbstractUnitaryMatrixType`, or something. Such hierarchy would necessarily look a bit arbitrary but is much less prone to ambiguities. Alternatively, we could offer `unitary`/`nonunitary` as a separate property type argument -- two simple type arguments for dispatch should be manageable.
author_association: MEMBER
user: login: kellertuer
created_at: 2025-03-23T10:55:05Z
body: Hm, then I have to think about this. Because I could implement this with not too many lines of code by basically copying everything from invertible matrices, but I felt a bit bad copying code again. Maybe I do that here then because it is faster than spending a year on redesigning traits.

I do not mind that this is not flexible – if we do not drown in ambiguities and it is easier to use then the nested traits. And the idea was here to maybe transition to something nicer.
But maybe I am also just not able to program that.

So I will probably just copy Invertivle Matrices then and change 2-3 places where det1 is checked.
author_association: MEMBER
user: login: mateuszbaran
created_at: 2025-03-23T19:26:51Z
body: I think we shouldn't introduce a new solution for matrix manifolds here. I'm fine with either copying invertible matrices or extending general unitary matrices to non-unitary cases. IMO copying is still better than introducing an unnecessary abstraction in case it might be useful in the future.
author_association: MEMBER
user: login: kellertuer
created_at: 2025-03-23T20:35:56Z
body: Well, my hope was this idea could serve as a replacement for treats, but it seems that is not the case.
It was not meant as another additional layer but a replacement idea. But it seems it is no good.

So I will copy yet again.
author_association: MEMBER
user: login: kellertuer
created_at: 2025-03-24T18:42:43Z
body: Hm, it seems in my internal renaming of `DeterminantOneMatrices` to `DeterminantOneMatiricesType` I might not have been careful enough. Will check tomorrow.
author_association: MEMBER
user: login: kellertuer
created_at: 2025-03-25T16:16:09Z
body: So CI is back, but I am not yet sure how to cover the lines for the cases where we want to be sure the matrix is invertible, when this has such a low probability to occur 
author_association: MEMBER
user: login: mateuszbaran
created_at: 2025-03-25T17:11:53Z
body: I've found a way to test that.
author_association: MEMBER
user: login: kellertuer
created_at: 2025-03-26T08:42:06Z
body: So this is hopefully only waiting for the docs to render again once the new versions in SciML are all up to date, but Chris said yesterday he is looking into this.
author_association: MEMBER
user: login: kellertuer
created_at: 2025-03-27T18:41:38Z
body: It finally works, merging this and updating the other two PRs should bring us up to date and to the next version 🥳
author_association: MEMBER
user: login: mateuszbaran
created_at: 2025-03-27T20:03:45Z
body: Great!
author_association: MEMBER