-
Notifications
You must be signed in to change notification settings - Fork 39
Add opnorm implementation #375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Introduces a new opnorm function that efficiently computes the operator 2-norm for matrices and linear operators, dispatching to LAPACK, ARPACK, or TSVD as appropriate. Adds comprehensive tests for opnorm covering both Float64 and BigFloat types, and integrates the new test file into the test suite. Update Project.toml
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #375 +/- ##
==========================================
- Coverage 95.00% 90.65% -4.35%
==========================================
Files 17 20 +3
Lines 1100 1209 +109
==========================================
+ Hits 1045 1096 +51
- Misses 55 113 +58 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
tmigot
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @farhadrclass that will be a great add. I made a first batch of comments
Added Arpack to Project.toml dependencies and compat section. Updated utilities.jl to import Arpack, preparing for usage of its functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces a new opnorm function to efficiently compute the operator 2-norm (largest singular value) for matrices and linear operators. The implementation uses a dispatching strategy that selects LAPACK for small dense matrices, ARPACK for larger matrices with Float32/Float64/ComplexF32/ComplexF64 element types, and TSVD for other element types like BigFloat.
Changes:
- Adds
opnormfunction with type-based dispatch for efficient norm computation - Adds comprehensive tests for Float64 and BigFloat matrix types
- Adds new dependencies: Arpack, GenericLinearAlgebra, and TSVD
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 18 comments.
| File | Description |
|---|---|
| src/utilities.jl | Implements opnorm function with dispatch to opnorm_eig for square matrices and opnorm_svd for rectangular matrices, with fallback to TSVD |
| test/test_opnorm.jl | Adds tests for square and rectangular matrices with Float64 and BigFloat types |
| test/runtests.jl | Integrates new test_opnorm.jl into the test suite |
| Project.toml | Adds Arpack, GenericLinearAlgebra, and TSVD dependencies with version constraints |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/utilities.jl
Outdated
| import LinearAlgebra: opnorm | ||
| using GenericLinearAlgebra | ||
| using TSVD | ||
| using Arpack | ||
| export opnorm |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The opnorm function is being exported before it is imported from LinearAlgebra. It's better practice to import first and then extend/export. Consider moving the import statement before the export statement.
src/utilities.jl
Outdated
| function LinearAlgebra.opnorm(B; kwargs...) | ||
| _opnorm(B, eltype(B); kwargs...) | ||
| end |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function returns a tuple (value, boolean) instead of just the norm value as the standard LinearAlgebra.opnorm would. This inconsistent API design might confuse users who expect the standard opnorm behavior. Consider documenting this difference clearly in the docstring or using a different function name.
src/utilities.jl
Outdated
| @warn "Arpack error: $e. Increasing NCV to $ncv and retrying." | ||
| ncv = min(2 * ncv, n) # Increase NCV but don't exceed matrix size |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warning message at line 362 displays "Increasing NCV to $ncv" but ncv is updated on line 363 after the warning is printed. The warning should either reference the new ncv value or be moved after the assignment. The same issue exists in opnorm_svd at line 402.
src/utilities.jl
Outdated
| if occursin("XYAUPD_Exception", string(e)) && ncv < n | ||
| @warn "Arpack error: $e. Increasing NCV to $ncv and retrying." | ||
| ncv = min(2 * ncv, n) # Increase NCV but don't exceed matrix size |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the error handling logic, the code checks for "XYAUPD_Exception" in the error string, but ARPACK exceptions typically contain "AUPD" (e.g., "dsaupd_", "dnaupd_", "znaupd_"). The substring "XYAUPD_Exception" appears to be a placeholder and may not match actual ARPACK errors. Verify that this error detection pattern is correct.
| if occursin("XYAUPD_Exception", string(e)) && ncv < n | |
| @warn "Arpack error: $e. Increasing NCV to $ncv and retrying." | |
| ncv = min(2 * ncv, n) # Increase NCV but don't exceed matrix size | |
| if occursin("AUPD", string(e)) && ncv < n | |
| new_ncv = min(2 * ncv, n) # Increased NCV but don't exceed matrix size | |
| @warn "Arpack error: $e. Increasing NCV to $new_ncv and retrying." | |
| ncv = new_ncv |
test/test_opnorm.jl
Outdated
| @@ -0,0 +1,33 @@ | |||
| function test_opnorm() | |||
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test file uses LinearOperator but doesn't import it. The test file should have proper imports at the top to be self-contained, even though it's included by runtests.jl which has the imports.
test/test_opnorm.jl
Outdated
| @test ok == true | ||
| @test isapprox(λ, 2.0; rtol=1e-12) | ||
|
|
||
| # 2) Rectangular Float64 via direct LAPACK or ARPACK SVD | ||
| J_mat = [3.0 0.0 0.0; 0.0 1.0 0.0] | ||
| J_op = LinearOperator(J_mat) | ||
| σ, ok_sv = opnorm(J_op) | ||
| @test ok_sv == true | ||
| @test isapprox(σ, 3.0; rtol=1e-12) | ||
|
|
||
| # 3) Square BigFloat via TSVD | ||
| B_mat = Matrix{BigFloat}([2.0 0.0; 0.0 -1.0]) | ||
| B_op = LinearOperator(B_mat) | ||
| λ_bf, ok_bf = opnorm(B_op) | ||
| @test ok_bf == true | ||
| @test isapprox(λ_bf, BigFloat(2); rtol=1e-12) | ||
|
|
||
| # 4) Rectangular BigFloat via rectangular TSVD | ||
| JR_mat = Matrix{BigFloat}([3.0 0.0 0.0; 0.0 1.0 0.0]) | ||
| JR_op = LinearOperator(JR_mat) | ||
| σ_bf, ok_bf2 = opnorm(JR_op) | ||
| @test ok_bf2 == true |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test only validates the returned boolean status but doesn't verify what happens when convergence fails (ok == false). Consider adding assertions or handling for when the second return value is false to ensure the function behavior is well-defined in failure cases.
Replaces the opnorm function with estimate_opnorm throughout the codebase for clarity. Updates all relevant documentation, exports, and test files to use the new function name, and renames the test file accordingly. Update utilities.jl Update test_estimate_opnorm.jl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 3 out of 4 changed files in this pull request and generated 15 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/utilities.jl
Outdated
| _, s, _ = tsvd(B) | ||
| return s[1], true |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TSVD fallback path doesn't include any error handling for the case where tsvd might fail or return an empty singular value array. Consider adding bounds checking or error handling for edge cases such as zero-dimensional or empty matrices.
| _, s, _ = tsvd(B) | |
| return s[1], true | |
| m, n = size(B) | |
| # Guard against zero-dimensional or empty matrices | |
| if m == 0 || n == 0 | |
| return zero(T), false | |
| end | |
| try | |
| _, s, _ = tsvd(B) | |
| # Ensure we have at least one singular value before indexing | |
| if isempty(s) | |
| return zero(T), false | |
| end | |
| return s[1], true | |
| catch e | |
| @warn "TSVD-based operator norm estimation failed: $e" | |
| return zero(T), false | |
| end |
src/utilities.jl
Outdated
| end | ||
|
|
||
| # 2) iterative ARPACK | ||
| nev, ncv = 1, max(20, 2*1 + 1) |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ncv calculation appears incorrect. The expression '21 + 1' always evaluates to 3. This should likely be '2nev + 1' to properly relate ncv to the number of eigenvalues requested, as is standard practice when using ARPACK's eigs function.
| nev, ncv = 1, max(20, 2*1 + 1) | |
| nev, ncv = 1, max(20, 2*nev + 1) |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
farhadrclass
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.
| """ | ||
|
|
||
| function estimate_opnorm(B; kwargs...) | ||
| _estimate_opnorm(B, eltype(B); kwargs...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this dispatch really necessary or could you parameterize B{T}?
julia> using LinearAlgebra, SparseArrays
julia> A = rand(2, 3);
julia> typeof(A)
Matrix{Float64} (alias for Array{Float64, 2})
julia> typeof(sparse(A))
SparseMatrixCSC{Float64, Int64}
julia> typeof(sparse(A)) <: AbstractMatrix{Float64}
true| _estimate_opnorm(B, eltype(B); kwargs...) | ||
| end | ||
|
|
||
| # 1. Fallback for Integer/Generic types (Uses TSVD) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In LinearAlgebra, opnorm is only defined for Real types. We should do the same for operators. Does TSVD even work for a matrix of Ints?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically, you always use ARPACK excepts for corner cases. But isn’t TSVD better sometimes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For TSVD, We did ended up doing the profile and compare and it turn out the allocation and accuracy, ARPACK was better, I think I share the results with you in our Zulip
|
|
||
| # Tiny dense optimization | ||
| if n ≤ tiny_dense_threshold | ||
| return maximum(abs, eigen(Matrix(B)).values), true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you profile for what sizes eigen was faster than whatever opnorm does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did, but also if I remember correctly, the ARPACK does not handle small matrices (I think under 5 I got an error)
This is one of the places where I found the issue too https://discourse.julialang.org/t/adjusting-nev-from-to-eigs-on-a-sparse-array-using-arpack/27163
Co-authored-by: Dominique <dominique.orban@gmail.com>
Introduces a new opnorm function that efficiently computes the operator 2-norm for matrices and linear operators, dispatching to LAPACK, ARPACK, or TSVD as appropriate. Adds comprehensive tests for opnorm covering both Float64 and BigFloat types, and integrates the new test file into the test suite.
Update Project.toml