Skip to content

Commit d0a6107

Browse files
Reuse Arpack in order to compute norm of Bk (#159)
Enhance robustness of Arpack usage by catching NCV-related errors Catch the XYAUPD_Exception error in Arpack calls and dynamically increase NCV as needed to handle convergence issues. This modification addresses instability in LSR1 computations due to insufficient NCV. Co-authored-by: Dominique <[email protected]>
1 parent 9109051 commit d0a6107

File tree

7 files changed

+94
-13
lines changed

7 files changed

+94
-13
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ ProximalOperators = "a725b495-10eb-56fe-b38b-717eba820537"
1515
RegularizedProblems = "ea076b23-609f-44d2-bb12-a4ae45328278"
1616
ShiftedProximalOperators = "d4fd37fa-580c-4e43-9b30-361c21aae263"
1717
SolverCore = "ff4d7338-4cf1-434d-91df-b86cb86fb843"
18-
TSVD = "9449cd9e-2762-5aa3-a617-5413e99d722e"
18+
Arpack = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97"
1919

2020
[compat]
2121
LinearOperators = "2.9"
@@ -26,7 +26,7 @@ ProximalOperators = "0.15"
2626
RegularizedProblems = "0.1.1"
2727
ShiftedProximalOperators = "0.2"
2828
SolverCore = "0.3.0"
29-
TSVD = "0.4"
29+
Arpack = "0.5"
3030
julia = "^1.6.0"
3131

3232
[extras]

src/LMTR_alg.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ function LMTR(
130130
JdFk = similar(Fk) # temporary storage
131131
Jt_Fk = similar(∇fk) # temporary storage
132132

133-
σmax = opnorm(Jk)
133+
σmax, found_σ = opnorm(Jk)
134+
found_σ || error("operator norm computation failed")
134135
νInv = (1 + θ) * σmax^2 # ‖J'J‖ = ‖J‖²
135136

136137
mν∇fk = -∇fk / νInv
@@ -252,7 +253,8 @@ function LMTR(
252253
shift!(ψ, xk)
253254
Jk = jac_op_residual(nls, xk)
254255
jtprod_residual!(nls, xk, Fk, ∇fk)
255-
σmax = opnorm(Jk)
256+
σmax, found_σ = opnorm(Jk)
257+
found_σ || error("operator norm computation failed")
256258
νInv = (1 + θ) * σmax^2 # ‖J'J‖ = ‖J‖²
257259
@. mν∇fk = -∇fk / νInv
258260
end

src/LM_alg.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ function LM(
123123
JdFk = similar(Fk) # temporary storage
124124
Jt_Fk = similar(∇fk)
125125

126-
σmax = opnorm(Jk)
126+
σmax, found_σ = opnorm(Jk)
127+
found_σ || error("operator norm computation failed")
127128
νInv = (1 + θ) * (σmax^2 + σk) # ‖J'J + σₖ I‖ = ‖J‖² + σₖ
128129

129130
s = zero(xk)
@@ -243,7 +244,8 @@ function LM(
243244
Jk = jac_op_residual(nls, xk)
244245
jtprod_residual!(nls, xk, Fk, ∇fk)
245246

246-
σmax = opnorm(Jk)
247+
σmax, found_σ = opnorm(Jk)
248+
found_σ || error("operator norm computation failed")
247249

248250
Complex_hist[k] += 1
249251
end

src/R2N.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ function R2N(
132132
quasiNewtTest = isa(f, QuasiNewtonModel)
133133
Bk = hess_op(f, xk)
134134

135-
λmax = opnorm(Bk)
135+
λmax, found_λ = opnorm(Bk)
136+
found_λ || error("operator norm computation failed")
136137
νInv = (1 + θ) * (σk + λmax)
137138
sqrt_ξ1_νInv = one(R)
138139

@@ -247,7 +248,8 @@ function R2N(
247248
push!(f, s, ∇fk - ∇fk⁻)
248249
end
249250
Bk = hess_op(f, xk)
250-
λmax = opnorm(Bk)
251+
λmax, found_λ = opnorm(Bk)
252+
found_λ || error("operator norm computation failed")
251253
∇fk⁻ .= ∇fk
252254
end
253255

src/RegularizedOptimization.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module RegularizedOptimization
44
using LinearAlgebra, Logging, Printf
55

66
# external dependencies
7-
using ProximalOperators, TSVD
7+
using Arpack, ProximalOperators
88

99
# dependencies from us
1010
using LinearOperators,

src/TR_alg.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ function TR(
131131
quasiNewtTest = isa(f, QuasiNewtonModel)
132132
Bk = hess_op(f, xk)
133133

134-
λmax = opnorm(Bk)
134+
λmax, found_λ = opnorm(Bk)
135+
found_λ || error("operator norm computation failed")
135136
α⁻¹Δ⁻¹ = 1 /* Δk)
136137
ν = 1 / (α⁻¹Δ⁻¹ + λmax * (α⁻¹Δ⁻¹ + 1))
137138
sqrt_ξ1_νInv = one(R)
@@ -241,7 +242,8 @@ function TR(
241242
push!(f, s, ∇fk - ∇fk⁻)
242243
end
243244
Bk = hess_op(f, xk)
244-
λmax = opnorm(Bk)
245+
λmax, found_λ = opnorm(Bk)
246+
found_λ || error("operator norm computation failed")
245247
∇fk⁻ .= ∇fk
246248
end
247249

src/utils.jl

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,81 @@ import SolverCore.GenericExecutionStats
44

55
# use Arpack to obtain largest eigenvalue in magnitude with a minimum of robustness
66
function LinearAlgebra.opnorm(B; kwargs...)
7-
_, s, _ = tsvd(B)
8-
return s[1]
7+
m, n = size(B)
8+
opnorm_fcn = m == n ? opnorm_eig : opnorm_svd
9+
return opnorm_fcn(B; kwargs...)
10+
end
11+
12+
function opnorm_eig(B; max_attempts::Int = 3)
13+
have_eig = false
14+
attempt = 0
15+
λ = zero(eltype(B))
16+
n = size(B, 1)
17+
nev = 1
18+
ncv = max(20, 2 * nev + 1)
19+
20+
while !(have_eig || attempt >= max_attempts)
21+
attempt += 1
22+
try
23+
# Estimate largest eigenvalue in absolute value
24+
d, nconv, niter, nmult, resid = eigs(B; nev = nev, ncv = ncv, which = :LM, ritzvec = false, check = 1)
25+
26+
# Check if eigenvalue has converged
27+
have_eig = nconv == 1
28+
if have_eig
29+
λ = abs(d[1]) # Take absolute value of the largest eigenvalue
30+
break # Exit loop if successful
31+
else
32+
# Increase NCV for the next attempt if convergence wasn't achieved
33+
ncv = min(2 * ncv, n)
34+
end
35+
catch e
36+
if occursin("XYAUPD_Exception", string(e)) && ncv < n
37+
@warn "Arpack error: $e. Increasing NCV to $ncv and retrying."
38+
ncv = min(2 * ncv, n) # Increase NCV but don't exceed matrix size
39+
else
40+
rethrow(e) # Re-raise if it's a different error
41+
end
42+
end
43+
end
44+
45+
return λ, have_eig
46+
end
47+
48+
function opnorm_svd(J; max_attempts::Int = 3)
49+
have_svd = false
50+
attempt = 0
51+
σ = zero(eltype(J))
52+
n = min(size(J)...) # Minimum dimension of the matrix
53+
nsv = 1
54+
ncv = 10
55+
56+
while !(have_svd || attempt >= max_attempts)
57+
attempt += 1
58+
try
59+
# Estimate largest singular value
60+
s, nconv, niter, nmult, resid = svds(J; nsv = nsv, ncv = ncv, ritzvec = false, check = 1)
61+
62+
# Check if singular value has converged
63+
have_svd = nconv >= 1
64+
if have_svd
65+
σ = maximum(s.S) # Take the largest singular value
66+
break # Exit loop if successful
67+
else
68+
# Increase NCV for the next attempt if convergence wasn't achieved
69+
ncv = min(2 * ncv, n)
70+
end
71+
catch e
72+
if occursin("XYAUPD_Exception", string(e)) && ncv < n
73+
@warn "Arpack error: $e. Increasing NCV to $ncv and retrying."
74+
ncv = min(2 * ncv, n) # Increase NCV but don't exceed matrix size
75+
else
76+
rethrow(e) # Re-raise if it's a different error
77+
end
78+
end
79+
end
80+
81+
return σ, have_svd
982
end
1083

1184
ShiftedProximalOperators.iprox!(

0 commit comments

Comments
 (0)