Skip to content

Fix remove_svd_gauge_dependence! for rank-deficient SVDs - #286

Merged
lkdvos merged 3 commits into
mainfrom
lb/fix_svd_gauge_rank
Oct 1, 2026
Merged

lkdvos merged 3 commits into
mainfrom
lb/fix_svd_gauge_rank

Conversation

@leburgel

Copy link
Copy Markdown
Member

When some singular values are below rank_atol (r < length(S)), remove_svd_gauge_dependence! has two bugs:

  • The degeneracy mask is built from all singular values, but gaugepart is only r × r, so the function throws a BoundsError.
  • The columns of ΔU (rows of ΔVᴴ) beyond r are meant to be zeroed, but zero!(ΔU[:, (r + 1):end]) zeroes a copy. The first bug has kept this one from surfacing.

This PR builds the mask from Sdiag[1:r] and zeroes views. The full-rank case is unchanged.

Reproducer
using LinearAlgebra, Random
using MatrixAlgebraKit
using MatrixAlgebraKit: remove_svd_gauge_dependence!

Random.seed!(1)
m, n, r = 6, 5, 3
Q₁ = Matrix(qr(randn(ComplexF64, m, m)).Q)
Q₂ = Matrix(qr(randn(ComplexF64, n, n)).Q)
A = Q₁[:, 1:n] * Diagonal([1.0, 0.5, 0.25, 0.0, 0.0]) * Q₂' # rank 3
U, S, Vᴴ = svd_compact(A)
ΔU, ΔVᴴ = randn(ComplexF64, size(U)), randn(ComplexF64, size(Vᴴ))

ΔU, ΔVᴴ = remove_svd_gauge_dependence!(ΔU, ΔVᴴ, U, S, Vᴴ)
@show norm(ΔU[:, (r + 1):end]) norm(ΔVᴴ[(r + 1):end, :])

On main:

ERROR: LoadError: BoundsError: attempt to access 3×3 Matrix{ComplexF64} at index [5×5 BitMatrix]

With only the mask fixed, the rank-deficient part is not zeroed:

norm(ΔU[:, r + 1:end]) = 3.595426447598924
norm(ΔVᴴ[r + 1:end, :]) = 3.181565844025091

With this PR:

norm(ΔU[:, r + 1:end]) = 0.0
norm(ΔVᴴ[r + 1:end, :]) = 0.0

I have a small regression test I could add, but that would be another instance of testing pullback-related things directly like the test added in #282, so I didn't add it right away. Let me know if this would be good to add anyway.

@lkdvos lkdvos left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for the views, looks good to me!

(I am guessing @Jutho will oppose to the @view instead of directly using view, so that would be my only other suggestion 😉 )

Comment thread src/pullbacks/svd.jl Outdated
@leburgel
leburgel force-pushed the lb/fix_svd_gauge_rank branch from d9b5795 to 5afca7c Compare October 1, 2026 06:24
@lkdvos
lkdvos merged commit 2997686 into main Oct 1, 2026
44 of 47 checks passed
@lkdvos
lkdvos deleted the lb/fix_svd_gauge_rank branch October 1, 2026 14:43
Comment thread src/pullbacks/svd.jl
if size(ΔU, 2) > r
if r < length(Sdiag) # rank-deficient case, no stable information can be extracted from extra columns of U
zero!(ΔU[:, (r + 1):end])
zero!(view(ΔU, :, (r + 1):size(ΔU, 2)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was quite stupid indeed 😄 .

Comment thread src/pullbacks/svd.jl
gaugepart = mul!(U₁' * ΔU₁, Vᴴ₁, ΔVᴴ₁', true, true)
gaugepart = project_antihermitian!(gaugepart)
gaugepart[abs.(transpose(Sdiag) .- Sdiag) .>= degeneracy_atol] .= 0
gaugepart[abs.(transpose(view(Sdiag, 1:r)) .- view(Sdiag, 1:r)) .>= degeneracy_atol] .= 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could have had an S₁ = view(Sdiag, 1:r) definition and use this here, but it's fine like this.

@codecov

codecov Bot commented Oct 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 33.33333% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/pullbacks/svd.jl 33.33% 2 Missing ⚠️
Files with missing lines Coverage Δ
src/pullbacks/svd.jl 93.36% <33.33%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants