Skip to content

Return the same float type from sparse cond for singular and nonsingular matrices - #1131

Merged
ViralBShah merged 2 commits into
mainfrom
vs/cond-result-type
Oct 27, 2026
Merged

ViralBShah merged 2 commits into
mainfrom
vs/cond-result-type

Conversation

@ViralBShah

Copy link
Copy Markdown
Member

Sparse cond of a Float32 or ComplexF32 matrix returned Float64 when the matrix went through the LU and was nonsingular, but Float32 when it was singular (since #1126) or triangular, diagonal or positive definite. Dense cond returns Float32 in every case. Raised in the post-merge review of #1126.

Before

julia> cond(sparse(Float32[1 2; 3 4]), 1)
20.99999999999999

julia> cond(sparse(Float32[1 2; 3 6]), 1)
Inf32

julia> Base.return_types(cond, (SparseMatrixCSC{Float32,Int}, Int))
1-element Vector{Any}:
 Union{Float32, Float64}

After

julia> cond(sparse(Float32[1 2; 3 4]), 1)
21.0f0

julia> cond(sparse(Float32[1 2; 3 6]), 1)
Inf32

julia> Base.return_types(cond, (SparseMatrixCSC{Float32,Int}, Int))
1-element Vector{Any}:
 Float32

UMFPACK works in double precision, so the estimate of the inverse's norm is a Float64 for any eltype that takes the LU (a nonsymmetric matrix, or an indefinite Hermitian one where factorize falls back to lu), and opnorm(A, p) * normAinv promoted to it. cond now computes T = float(real(eltype(A))) once and returns zero(T), convert(T, Inf) or convert(T, opnorm(A, p) * normAinv). A sweep against dense cond over Int, Float32, Float64, ComplexF32, ComplexF64, Rational{Int}, BigFloat and Complex{BigFloat}, on general, singular, triangular, diagonal and Hermitian matrices, gives the dense result type in every case that runs; only the Float32 and ComplexF32 LU cases changed.

Potential breakage: cond of a nonsingular Float32 or ComplexF32 sparse matrix that is not triangular, diagonal or positive definite now returns Float32 instead of Float64, as dense cond does.

Written by Claude Code.

🤖 Generated with Claude Code

…gular matrices

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.68%. Comparing base (2bef1fd) to head (ae3041d).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/linalg.jl 80.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1131      +/-   ##
==========================================
- Coverage   94.70%   94.68%   -0.02%     
==========================================
  Files          16       16              
  Lines       10423    10432       +9     
==========================================
+ Hits         9871     9878       +7     
- Misses        552      554       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…1132)

Stacked on #1131.

Since #1126 sparse `cond` wraps the whole `opnormestinv` estimator in a
`try`, so a `SingularException` from anywhere inside it becomes `Inf`,
and a singular triangular or diagonal matrix is only found once the
estimator has allocated its blocks and run its first solve. Raised in
the post-merge review of #1126.

**Before** (#1131), `cond(A, 1)` of a singular `A` with `n = 10^6` and
one zero on the diagonal
| `A` | time | allocated |
|---|---|---|
| diagonal | 64.6 ms | 125.3 MiB |
| lower bidiagonal | 16.1 ms | 68.7 MiB |

**After**
| `A` | time | allocated |
|---|---|---|
| diagonal | 9.4 ms | 56.6 MiB |
| lower bidiagonal | 2.6 ms | 0 |

Both return `Inf`, before and after. `factorize` throws a
`SingularException` for a singular LU, and for a singular Hermitian
matrix once Cholesky and LDLt have failed and it falls back to the LU
(#1116); only the triangular and diagonal factors it returns leave the
zero pivot to the solve. So `cond` now catches around `factorize` alone
and checks the diagonal of those factors with `_issingularfactor`, which
reuses the triangular solve's `_trisingular`, before it calls the
estimator; `opnormestinv(A, t)` keeps its behaviour and calls the new
`_opnormestinv(A, F, t)` with the factorization. A sweep of `cond`
against dense over `Int`, `Float32`, `Float64`, `ComplexF32`,
`ComplexF64`, `Rational{Int}`, `BigFloat` and `Complex{BigFloat}`, on
general, singular, triangular, diagonal and Hermitian matrices, gives
the same results as #1131 with a seeded RNG.

Written by Claude Code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@ViralBShah
ViralBShah enabled auto-merge (squash) October 27, 2026 00:13
@ViralBShah ViralBShah added backport 1.13 Change should be backported to release-1.13 backport 1.14 labels Oct 27, 2026
@ViralBShah
ViralBShah merged commit eb47441 into main Oct 27, 2026
12 checks passed
@ViralBShah
ViralBShah deleted the vs/cond-result-type branch October 27, 2026 00:23
@ViralBShah ViralBShah mentioned this pull request Oct 27, 2026
30 tasks done
ViralBShah added a commit that referenced this pull request Oct 27, 2026
Backports of the merged PRs labelled `backport 1.14`, cherry-picked with
`-x` in the order they were merged on `main`. `release-1.14` starts at
b0aa455 (#1104), the commit JuliaLang/julia's `release-1.14` pins, and
every PR merged on `main` since then is labelled, so this branch's tree
is identical to `main`'s at 9bfad27.

- [x] #1105
- [x] #1011
- [x] #1107
- [x] #1108
- [x] #1110
- [x] #1111
- [x] #1114
- [x] #1112
- [x] #1115
- [x] #1117
- [x] #1118
- [x] #1109
- [x] #1113
- [x] #1122
- [x] #1123
- [x] #1120
- [x] #1124
- [x] #1125
- [x] #1116
- [x] #1121
- [x] #1126
- [x] #1128
- [x] #1130
- [x] #1131
- [x] #1119
- [x] #1133
- [x] #1127
- [x] #1135
- [x] #1134
- [x] #1137

#1137 removes the first-call compile time that #1128 added, which the
TTFX check flagged on this PR.

#1132 merged into the branch of #1131 and is part of its commit.

Not bug fixes: #1011 adds a deprecation, #1109 changes the `sprand`
value stream for a value function, #1113 changes the result shape of
`SparseMatrixCSC` of a vector view, and #1119, #1133, #1134 and #1135
add methods for views. They are included because they are labelled and
1.14 is not released yet.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Viral B. Shah <ViralBShah@users.noreply.github.com>
@ViralBShah ViralBShah removed the backport 1.13 Change should be backported to release-1.13 label Oct 27, 2026
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.

1 participant