Repository: JuliaLinearAlgebra/GenericLinearAlgebra.jl
Pull request #148 (2025-04-19 15:03:24)

Avoid abstractly typed field in SymmetricTridiagonalFactorization
This change removes a bit of overhead in the symmetric eigensolvers.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/JuliaLinearAlgebra/GenericLinearAlgebra.jl/pull/148?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaLinearAlgebra) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 95.96%. Comparing base [(`f496e5a`)](https://app.codecov.io/gh/JuliaLinearAlgebra/GenericLinearAlgebra.jl/commit/f496e5a0a6cc42d16c2e3f59fd42cebba631eb59?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaLinearAlgebra) to head [(`ac76d2b`)](https://app.codecov.io/gh/JuliaLinearAlgebra/GenericLinearAlgebra.jl/commit/ac76d2be1de8c0222f1435b2a825d22527428ba9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaLinearAlgebra).

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


```diff
@@            Coverage Diff             @@
##           master     #148      +/-   ##
==========================================
- Coverage   96.18%   95.96%   -0.23%     
==========================================
  Files          11       10       -1     
  Lines        1651     1511     -140     
==========================================
- Hits         1588     1450     -138     
+ Misses         63       61       -2     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/JuliaLinearAlgebra/GenericLinearAlgebra.jl/pull/148?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaLinearAlgebra).   
: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=JuliaLinearAlgebra).

<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>

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4667a67..8076296 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,8 +20,18 @@ jobs:
       - run: exit 1
         if: |
           (needs.test.result != 'success')
+
+  # format:
+  #   name: Format check
+  #   runs-on: ubuntu-latest
+  #   steps:
+  #     - uses: julia-actions/julia-format@v3
+  #       with:
+  #         version: '2.1.1'
+
   test:
     name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
+    # needs: [format]
     runs-on: ${{ matrix.os }}
     strategy:
       fail-fast: false
@@ -30,7 +40,7 @@ jobs:
           - 'min'
           - 'lts'
           - '1'
-          - 'pre'
+          # - 'pre'
         os:
           - ubuntu-latest
 #           - macos-latest
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
deleted file mode 100644
index a931f71..0000000
--- a/.github/workflows/format.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-name: Format check
-on:
-  pull_request:
-  workflow_dispatch:
-jobs:
-  code-style:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: julia-actions/julia-format@v3
-        with:
-          version: '1' # Set `version` to '1.0.54' if you need to use JuliaFormatter.jl v1.0.54 (default: '1')
diff --git a/src/eigenSelfAdjoint.jl b/src/eigenSelfAdjoint.jl
index 8f82850..140866c 100644
--- a/src/eigenSelfAdjoint.jl
+++ b/src/eigenSelfAdjoint.jl
@@ -2,29 +2,13 @@ using Printf
 using LinearAlgebra
 using LinearAlgebra: givensAlgorithm
 
-struct SymmetricTridiagonalFactorization{T} <: Factorization{T}
-    uplo::Char
-    factors::Matrix{T}
-    τ::Vector{T}
-    diagonals::SymTridiagonal
-end
-
-Base.size(S::SymmetricTridiagonalFactorization, i::Integer) = size(S.factors, i)
-
+## EigenQ
 struct EigenQ{T} <: AbstractMatrix{T}
     uplo::Char
     factors::Matrix{T}
     τ::Vector{T}
 end
 
-function Base.getproperty(S::SymmetricTridiagonalFactorization, s::Symbol)
-    if s == :Q
-        return EigenQ(S.uplo, S.factors, S.τ)
-    else
-        return getfield(S, s)
-    end
-end
-
 Base.size(Q::EigenQ) = (size(Q.factors, 1), size(Q.factors, 1))
 function Base.size(Q::EigenQ, i::Integer)
     if i < 1
@@ -42,9 +26,8 @@ function LinearAlgebra.lmul!(Q::EigenQ, B::StridedVecOrMat)
         throw(DimensionMismatch(""))
     end
     if Q.uplo == 'L'
-        for k = length(Q.τ):-1:1
+        @inbounds for k = length(Q.τ):-1:1
             for j = 1:size(B, 2)
-                b = view(B, :, j)
                 vb = B[k+1, j]
                 for i = (k+2):m
                     vb += Q.factors[i, k]'B[i, j]
@@ -57,9 +40,8 @@ function LinearAlgebra.lmul!(Q::EigenQ, B::StridedVecOrMat)
             end
         end
     elseif Q.uplo == 'U'
-        for k = length(Q.τ):-1:1
+        @inbounds for k = length(Q.τ):-1:1
             for j = 1:size(B, 2)
-                b = view(B, :, j)
                 vb = B[k+1, j]
                 for i = (k+2):m
                     vb += Q.factors[k, i] * B[i, j]
@@ -120,6 +102,24 @@ end
 
 Base.Array(Q::EigenQ) = lmul!(Q, Matrix{eltype(Q)}(I, size(Q, 1), size(Q, 1)))
 
+
+## SymmetricTridiagonalFactorization
+struct SymmetricTridiagonalFactorization{T,Treal,S} <: Factorization{T}
+    reflectors::EigenQ{T}
+    diagonals::SymTridiagonal{Treal,S}
+end
+
+Base.size(S::SymmetricTridiagonalFactorization, i::Integer) = size(S.reflectors.factors, i)
+
+function Base.getproperty(S::SymmetricTridiagonalFactorization, s::Symbol)
+    if s == :Q
+        return S.reflectors
+    else
+        return getfield(S, s)
+    end
+end
+
+## Eigen solvers
 function _updateVectors!(c, s, j, vectors)
     @inbounds for i = 1:size(vectors, 1)
         v1 = vectors[i, j+1]
@@ -510,9 +510,11 @@ function symtriLower!(
         end
     end
     SymmetricTridiagonalFactorization(
-        'L',
-        AS,
-        τ,
+        EigenQ(
+            'L',
+            AS,
+            τ,
+        ),
         SymTridiagonal(real(diag(AS)), real(diag(AS, -1))),
     )
 end
@@ -564,9 +566,11 @@ function symtriUpper!(
         end
     end
     SymmetricTridiagonalFactorization(
-        'U',
-        AS,
-        τ,
+        EigenQ(
+            'U',
+            AS,
+            τ,
+        ),
         SymTridiagonal(real(diag(AS)), real(diag(AS, 1))),
     )
 end


Test patch:
diff --git a/test/eigenselfadjoint.jl b/test/eigenselfadjoint.jl
index c5dafd4..3fe8eb7 100644
--- a/test/eigenselfadjoint.jl
+++ b/test/eigenselfadjoint.jl
@@ -35,16 +35,16 @@ using Test, GenericLinearAlgebra, LinearAlgebra, Quaternions
 
     @testset "(full) Symmetric" for uplo in (:L, :U)
         A = Symmetric(big.(randn(n, n)), uplo)
-        vals, vecs = eigen(A)
+        vals, vecs = @inferred(eigen(A))
         @testset "default" begin
             @test vecs' * A * vecs ≈ diagm(0 => vals)
-            @test eigvals(A) ≈ vals
+            @test @inferred(eigvals(A)) ≈ vals
             @test vecs'vecs ≈ Matrix(I, n, n)
             @test issorted(vals)
         end
 
         @testset "eigen2" begin
-            vals2, vecs2 = GenericLinearAlgebra.eigen2(A)
+            vals2, vecs2 = @inferred(GenericLinearAlgebra.eigen2(A))
             @test vals ≈ vals2
             @test vecs[[1, n], :] ≈ vecs2
             @test vecs2 * vecs2' ≈ Matrix(I, 2, 2)