Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
eeed18a
Add robust opnorm implementation and tests
farhadrclass Aug 21, 2025
a67ac20
Update test_opnorm.jl
farhadrclass Aug 21, 2025
13c04c7
Add Arpack as dependency and import in utilities.jl
farhadrclass Nov 10, 2025
f2c3d06
Add robust opnorm implementation and tests
farhadrclass Aug 21, 2025
cf1031a
Update test_opnorm.jl
farhadrclass Aug 21, 2025
88b5df5
Add Arpack as dependency and import in utilities.jl
farhadrclass Nov 10, 2025
e29a76e
Rename opnorm to estimate_opnorm and update tests
farhadrclass Jan 20, 2026
4fce741
Apply suggestions from code review
farhadrclass Jan 20, 2026
3b17e79
Apply suggestions from code review
farhadrclass Jan 20, 2026
67aeac9
Update utilities.jl
farhadrclass Jan 20, 2026
829a552
Update src/utilities.jl
farhadrclass Jan 21, 2026
8b0da8c
Update utilities.jl
farhadrclass Jan 23, 2026
2d7ba5d
Update Project.toml
farhadrclass Jan 23, 2026
5af8752
Update Project.toml
farhadrclass Jan 23, 2026
a3d545f
Add new dependencies and update opnorm tests
farhadrclass Jan 23, 2026
a672263
Refactor opnorm estimation dispatch and improve tests
farhadrclass Jan 30, 2026
779431f
Add Arpack/TSVD weakdeps extension
farhadrclass Feb 5, 2026
b4dd14d
Update utilities.jl
farhadrclass Feb 5, 2026
ad73692
Merge branch 'JuliaSmoothOptimizers:main' into opNorm_branch
farhadrclass Feb 8, 2026
50b27b6
Merge branch 'JuliaSmoothOptimizers:main' into opNorm_branch
farhadrclass Feb 25, 2026
7111af1
Merge branch 'opNorm_branch' of https://github.com/Farhad-phd/LinearO…
farhadrclass Feb 25, 2026
15b6dbd
Support estimate_opnorm for AbstractLinearOperator
farhadrclass Feb 25, 2026
accfea2
Update test/Project.toml
farhadrclass Feb 26, 2026
15ae552
Merge branch 'JuliaSmoothOptimizers:main' into opNorm_branch
farhadrclass Apr 7, 2026
a1d8bcc
Update test/test_estimate_opnorm.jl
farhadrclass Apr 7, 2026
755f82f
Update src/utilities.jl
farhadrclass Apr 7, 2026
12deb75
Edits based on Tangi's review
farhadrclass Apr 7, 2026
1565241
extra end
farhadrclass Apr 7, 2026
9d68e4a
Update src/utilities.jl
farhadrclass Apr 23, 2026
d97f699
Update utilities.jl
farhadrclass Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ version = "2.11.0"

[deps]
FastClosures = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a"
GenericLinearAlgebra = "14197337-ba66-59df-a3e3-ca00e7dcff7a"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
TSVD = "9449cd9e-2762-5aa3-a617-5413e99d722e"
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"

[weakdeps]
Expand All @@ -31,13 +33,15 @@ AMDGPU = "2"
CUDA = "5"
ChainRulesCore = "1"
FastClosures = "0.2, 0.3"
GenericLinearAlgebra = "0.3.18"
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
JLArrays = "0.1, 0.2"
LDLFactorizations = "0.9, 0.10"
LinearAlgebra = "1"
Metal = "1.1"
Printf = "1"
Requires = "1"
SparseArrays = "1"
TSVD = "0.4.4"
TimerOutputs = "^0.5"
julia = "^1.6.0"

Expand Down
122 changes: 122 additions & 0 deletions src/utilities.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
export check_ctranspose,
check_hermitian, check_positive_definite, normest, solve_shifted_system!, ldiv!
import LinearAlgebra.ldiv!
import LinearAlgebra: opnorm
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
using GenericLinearAlgebra
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
using TSVD

export opnorm

Comment thread
farhadrclass marked this conversation as resolved.
Outdated
"""
normest(S) estimates the matrix 2-norm of S.
Expand Down Expand Up @@ -287,3 +292,120 @@ function ldiv!(
solve_shifted_system!(x, B, b, T(0.0))
return x
end

Comment thread
farhadrclass marked this conversation as resolved.
"""
opnorm(B; kwargs...)

Compute the operator 2-norm (largest singular value) of a matrix or linear operator `B`.
This method dispatches to efficient algorithms depending on the type and size of `B`:
for small dense matrices, it uses direct LAPACK routines; for larger or structured operators,
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
it uses iterative methods (ARPACK or TSVD) to estimate the norm efficiently.

# Arguments
- `B`: A matrix or linear operator.
- `kwargs...`: Optional keyword arguments passed to the underlying norm estimation routines.

# Returns
- The estimated operator 2-norm of `B` (largest singular value or eigenvalue in absolute value).
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
"""
Comment thread
farhadrclass marked this conversation as resolved.

function LinearAlgebra.opnorm(B; kwargs...)
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
_opnorm(B, eltype(B); kwargs...)
end
Comment thread
farhadrclass marked this conversation as resolved.
Outdated

# This method will be picked if eltype is one of the four types Arpack supports
# (Float32, Float64, ComplexF32, ComplexF64).
function _opnorm(
B,
::Type{T};
kwargs...,
) where {T <: Union{Float32, Float64, ComplexF32, ComplexF64}}
m, n = size(B)
return (m == n ? opnorm_eig : opnorm_svd)(B; kwargs...)
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
end

# Fallback for everything else
function _opnorm(B, ::Type{T}; kwargs...) where {T}
_, s, _ = tsvd(B)
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
return s[1], true # return largest singular value
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
end

function opnorm_eig(B; max_attempts::Int = 3)
n = size(B, 1)
# 1) tiny dense Float64: direct LAPACK
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
if n ≤ 5
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
return maximum(abs, eigen(Matrix(B)).values), true
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
end

# 2) iterative ARPACK
nev, ncv = 1, max(20, 2*1 + 1)
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
Comment thread
dpo marked this conversation as resolved.
Outdated
attempt, λ, have_eig = 0, zero(eltype(B)), false

while !(have_eig || attempt >= max_attempts)
attempt += 1
try
# Estimate largest eigenvalue in absolute value
d, nconv, niter, nmult, resid =
eigs(B; nev = nev, ncv = ncv, which = :LM, ritzvec = false, check = 1)

# Check if eigenvalue has converged
have_eig = nconv == 1
if have_eig
λ = abs(d[1]) # Take absolute value of the largest eigenvalue
break # Exit loop if successful
else
# Increase NCV for the next attempt if convergence wasn't achieved
ncv = min(2 * ncv, n)
end
catch e
if occursin("XYAUPD_Exception", string(e)) && ncv < n
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
@warn "Arpack error: $e. Increasing NCV to $ncv and retrying."
ncv = min(2 * ncv, n) # Increase NCV but don't exceed matrix size
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
else
rethrow(e) # Re-raise if it's a different error
end
end
end

return λ, have_eig
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
end

function opnorm_svd(J; max_attempts::Int = 3)
m, n = size(J)
# 1) tiny dense Float64: direct LAPACK
if min(m, n) ≤ 5
return maximum(svd(Matrix(J)).S), true
end

# 2) iterative ARPACK‐SVD
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
nsv, ncv = 1, 10
attempt, σ, have_svd = 0, zero(eltype(J)), false
n = min(m, n)
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
Comment thread
farhadrclass marked this conversation as resolved.
Outdated

while !(have_svd || attempt >= max_attempts)
attempt += 1
try
# Estimate largest singular value
s, nconv, niter, nmult, resid = svds(J; nsv = nsv, ncv = ncv, ritzvec = false, check = 1)

# Check if singular value has converged
have_svd = nconv >= 1
if have_svd
σ = maximum(s.S) # Take the largest singular value
break # Exit loop if successful
else
# Increase NCV for the next attempt if convergence wasn't achieved
ncv = min(2 * ncv, n)
end
catch e
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
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
else
rethrow(e) # Re-raise if it's a different error
end
end
end

return σ, have_svd
end
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ include("test_kron.jl")
include("test_callable.jl")
include("test_deprecated.jl")
include("test_normest.jl")
include("test_opnorm.jl")
include("test_diag.jl")
include("test_chainrules.jl")
include("test_solve_shifted_system.jl")
Expand Down
33 changes: 33 additions & 0 deletions test/test_opnorm.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
function test_opnorm()
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
@testset "opnorm and TSVD tests (LinearOperators)" begin
# 1) Square Float64 via direct LAPACK or ARPACK
A_mat = [2.0 0.0; 0.0 -1.0]
A_op = LinearOperator(A_mat)
λ, ok = opnorm(A_op)
@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
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
@test isapprox(σ_bf, BigFloat(3); rtol=1e-12)
end
Comment thread
farhadrclass marked this conversation as resolved.
Outdated
end

test_opnorm()
Loading