Skip to content

Commit fd45839

Browse files
Merge branch 'JuliaSmoothOptimizers:master' into StepRegExecutionStats
2 parents 6727f9c + 83bc164 commit fd45839

File tree

14 files changed

+203
-85
lines changed

14 files changed

+203
-85
lines changed

.cirrus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ task:
22
matrix:
33
- name: FreeBSD
44
freebsd_instance:
5-
image_family: freebsd-13-3
5+
image_family: freebsd-14-2
66
env:
77
matrix:
88
- JULIA_VERSION: 1

.github/workflows/ci.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@ jobs:
1313
fail-fast: false
1414
matrix:
1515
version:
16-
- '1.10'
16+
- 'lts'
1717
- '1'
18-
- 'nightly'
18+
- 'pre'
1919
os:
2020
- ubuntu-latest
2121
- macOS-latest
2222
- windows-latest
2323
arch:
2424
- x64
2525
steps:
26-
- uses: actions/checkout@v2
27-
- uses: julia-actions/setup-julia@v1
26+
- uses: actions/checkout@v4
27+
- uses: julia-actions/setup-julia@v2
2828
with:
2929
version: ${{ matrix.version }}
3030
arch: ${{ matrix.arch }}
31-
- uses: actions/cache@v1
31+
- uses: actions/cache@v4
3232
env:
3333
cache-name: cache-artifacts
3434
with:
@@ -41,6 +41,6 @@ jobs:
4141
- uses: julia-actions/julia-buildpkg@v1
4242
- uses: julia-actions/julia-runtest@v1
4343
- uses: julia-actions/julia-processcoverage@v1
44-
- uses: codecov/codecov-action@v1
44+
- uses: codecov/codecov-action@v5
4545
with:
4646
file: lcov.info

.github/workflows/demos.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ jobs:
1919
arch:
2020
- x64
2121
steps:
22-
- uses: actions/checkout@v2
23-
- uses: julia-actions/setup-julia@v1
22+
- uses: actions/checkout@v4
23+
- uses: julia-actions/setup-julia@v2
2424
with:
2525
version: ${{ matrix.version }}
2626
arch: ${{ matrix.arch }}
27-
- uses: actions/cache@v1
27+
- uses: actions/cache@v4
2828
env:
2929
cache-name: cache-artifacts
3030
with:
@@ -77,7 +77,7 @@ jobs:
7777
include(joinpath(pkg_path, "..", "examples", "demo-nnmf-constr.jl"))
7878
shell: julia --project="examples" --color=yes {0}
7979
- name: Upload results
80-
uses: actions/upload-artifact@v3
80+
uses: actions/upload-artifact@v4
8181
with:
8282
name: demos-results
8383
path: ${{ github.workspace }}/*.pdf

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: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ function LMTR(
115115

116116
if verbose > 0
117117
#! format: off
118-
@info @sprintf "%6s %8s %8s %8s %7s %7s %8s %7s %7s %7s %7s %1s" "outer" "inner" "f(x)" "h(x)" "√ξ1" "√ξ" "ρ" "Δ" "‖x‖" "‖s‖" "1/ν" "TR"
118+
@info @sprintf "%6s %8s %8s %8s %7s %7s %8s %7s %7s %7s %7s %1s" "outer" "inner" "f(x)" "h(x)" "√ξ1" "√ξ" "ρ" "Δ" "‖x‖" "‖s‖" "ν" "TR"
119119
#! format: on
120120
end
121121

@@ -130,10 +130,11 @@ function LMTR(
130130
JdFk = similar(Fk) # temporary storage
131131
Jt_Fk = similar(∇fk) # temporary storage
132132

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

136-
mν∇fk = -∇fk / νInv
137+
mν∇fk = -∇fk * ν
137138

138139
optimal = false
139140
tired = k maxIter || elapsed_time > maxTime
@@ -173,8 +174,8 @@ function LMTR(
173174

174175
# Take first proximal gradient step s1 and see if current xk is nearly stationary.
175176
# s1 minimizes φ1(d) + ‖d‖² / 2 / ν + ψ(d) ⟺ s1 ∈ prox{νψ}(-ν∇φ1(0))
176-
ν = 1 / (νInv + 1 / (Δk * α))
177-
prox!(s, ψ, mν∇fk, ν)
177+
ν1 = 1 / (1/ν + 1 / (Δk * α))
178+
prox!(s, ψ, mν∇fk, ν1)
178179
ξ1 = fk + hk - mk1(s) + max(1, abs(fk + hk)) * 10 * eps()
179180
ξ1 > 0 || error("LMTR: first prox-gradient step should produce a decrease but ξ1 = $(ξ1)")
180181

@@ -196,8 +197,8 @@ function LMTR(
196197
set_bounds!(ψ, max.(-∆_effective, l_bound - xk), min.(∆_effective, u_bound - xk)) :
197198
set_radius!(ψ, ∆_effective)
198199
subsolver_options.Δk = ∆_effective / 10
199-
subsolver_options.ν = ν
200-
subsolver_args = subsolver == TRDH ? (SpectralGradient(1 / ν, nls.meta.nvar),) : ()
200+
subsolver_options.ν = ν1
201+
subsolver_args = subsolver == TRDH ? (SpectralGradient(1 / ν1, nls.meta.nvar),) : ()
201202
s, iter, _ = with_logger(subsolver_logger) do
202203
subsolver(φ, ∇φ!, ψ, subsolver_args..., subsolver_options, s)
203204
end
@@ -231,7 +232,7 @@ function LMTR(
231232

232233
if (verbose > 0) && (k % ptf == 0)
233234
#! format: off
234-
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8.1e %7.1e %7.1e %7.1e %7.1e %1s" k iter fk hk sqrt(ξ1) sqrt(ξ) ρk ∆_effective χ(xk) sNorm νInv TR_stat
235+
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8.1e %7.1e %7.1e %7.1e %7.1e %1s" k iter fk hk sqrt(ξ1) sqrt(ξ) ρk ∆_effective χ(xk) sNorm ν TR_stat
235236
#! format: on
236237
end
237238

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

260262
if ρk < η1 || ρk == Inf
@@ -271,7 +273,7 @@ function LMTR(
271273
@info @sprintf "%6d %8s %8.1e %8.1e" k "" fk hk
272274
elseif optimal
273275
#! format: off
274-
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8s %7.1e %7.1e %7.1e %7.1e" k 1 fk hk sqrt(ξ1) sqrt(ξ1) "" Δk χ(xk) χ(s) νInv
276+
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8s %7.1e %7.1e %7.1e %7.1e" k 1 fk hk sqrt(ξ1) sqrt(ξ1) "" Δk χ(xk) χ(s) ν
275277
#! format: on
276278
@info "LMTR: terminating with √ξ1 = $(sqrt(ξ1))"
277279
end

src/LM_alg.jl

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ function LM(
4747
subsolver = R2,
4848
subsolver_options = ROSolverOptions(ϵa = options.ϵa),
4949
selected::AbstractVector{<:Integer} = 1:(nls.meta.nvar),
50+
nonlinear::Bool = true,
5051
) where {H}
5152
start_time = time()
5253
elapsed_time = 0.0
@@ -62,6 +63,7 @@ function LM(
6263
γ = options.γ
6364
θ = options.θ
6465
σmin = options.σmin
66+
σk = options.σk
6567

6668
# store initial values of the subsolver_options fields that will be modified
6769
ν_subsolver = subsolver_options.ν
@@ -85,7 +87,6 @@ function LM(
8587
end
8688

8789
# initialize parameters
88-
σk = max(1 / options.ν, σmin)
8990
xk = copy(x0)
9091
hk = h(xk[selected])
9192
if hk == Inf
@@ -101,6 +102,7 @@ function LM(
101102
xkn = similar(xk)
102103

103104
local ξ1
105+
local sqrt_ξ1_νInv
104106
k = 0
105107
Fobj_hist = zeros(maxIter)
106108
Hobj_hist = zeros(maxIter)
@@ -110,7 +112,7 @@ function LM(
110112

111113
if verbose > 0
112114
#! format: off
113-
@info @sprintf "%6s %8s %8s %8s %7s %7s %8s %7s %7s %7s %7s %1s" "outer" "inner" "f(x)" "h(x)" "ξ1" "√ξ" "ρ" "σ" "‖x‖" "‖s‖" "‖Jₖ‖²" "reg"
115+
@info @sprintf "%6s %8s %8s %8s %7s %7s %8s %7s %7s %7s %7s %1s" "outer" "inner" "f(x)" "h(x)" "(ξ1/ν)" "√ξ" "ρ" "σ" "‖x‖" "‖s‖" "‖Jₖ‖²" "reg"
114116
#! format: on
115117
end
116118

@@ -123,8 +125,9 @@ function LM(
123125
JdFk = similar(Fk) # temporary storage
124126
Jt_Fk = similar(∇fk)
125127

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

129132
s = zero(xk)
130133

@@ -171,27 +174,28 @@ function LM(
171174

172175
# take first proximal gradient step s1 and see if current xk is nearly stationary
173176
# s1 minimizes φ1(s) + ‖s‖² / 2 / ν + ψ(s) ⟺ s1 ∈ prox{νψ}(-ν∇φ1(0)).
174-
ν = 1 / νInv
175177
∇fk .*= -ν # reuse gradient storage
176178
prox!(s, ψ, ∇fk, ν)
177179
ξ1 = fk + hk - mk1(s) + max(1, abs(fk + hk)) * 10 * eps() # TODO: isn't mk(s) returned by subsolver?
178180
ξ1 > 0 || error("LM: first prox-gradient step should produce a decrease but ξ1 = $(ξ1)")
181+
sqrt_ξ1_νInv = sqrt(ξ1 / ν)
179182

180183
if ξ1 0 && k == 1
181-
ϵ_increment = ϵr * sqrt(ξ1)
184+
ϵ_increment = ϵr * sqrt_ξ1_νInv
182185
ϵ += ϵ_increment # make stopping test absolute and relative
183186
ϵ_subsolver += ϵ_increment
184187
end
185188

186-
if sqrt(ξ1) < ϵ
189+
if sqrt_ξ1_νInv < ϵ
187190
# the current xk is approximately first-order stationary
188191
optimal = true
189192
continue
190193
end
191194

192-
subsolver_options.ϵa = k == 1 ? 1.0e-1 : max(ϵ_subsolver, min(1.0e-2, ξ1 / 10))
195+
# subsolver_options.ϵa = k == 1 ? 1.0e-1 : max(ϵ_subsolver, min(1.0e-2, ξ1 / 10))
196+
subsolver_options.ϵa = k == 1 ? 1.0e-3 : min(sqrt_ξ1_νInv^(1.5), sqrt_ξ1_νInv * 1e-3) # 1.0e-5 default
193197
subsolver_options.ν = ν
194-
subsolver_args = subsolver == TRDH ? (SpectralGradient(1 / ν, nls.meta.nvar),) : ()
198+
subsolver_args = subsolver == R2DH ? (SpectralGradient(1 / ν, nls.meta.nvar),) : ()
195199
@debug "setting inner stopping tolerance to" subsolver_options.optTol
196200
s, iter, _ = with_logger(subsolver_logger) do
197201
subsolver(φ, ∇φ!, ψ, subsolver_args..., subsolver_options, s)
@@ -221,7 +225,7 @@ function LM(
221225

222226
if (verbose > 0) && (k % ptf == 0)
223227
#! format: off
224-
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8.1e %7.1e %7.1e %7.1e %7.1e %1s" k iter fk hk sqrt(ξ1) sqrt(ξ) ρk σk norm(xk) norm(s) νInv σ_stat
228+
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8.1e %7.1e %7.1e %7.1e %7.1e %1s" k iter fk hk sqrt_ξ1_νInv sqrt(ξ) ρk σk norm(xk) norm(s) 1/ν σ_stat
225229
#! format: off
226230
end
227231

@@ -243,15 +247,19 @@ function LM(
243247
Jk = jac_op_residual(nls, xk)
244248
jtprod_residual!(nls, xk, Fk, ∇fk)
245249

246-
σmax = opnorm(Jk)
250+
# update opnorm if not linear least squares
251+
if nonlinear == true
252+
σmax, found_σ = opnorm(Jk)
253+
found_σ || error("operator norm computation failed")
254+
end
247255

248256
Complex_hist[k] += 1
249257
end
250258

251259
if ρk < η1 || ρk == Inf
252260
σk = σk * γ
253261
end
254-
νInv = (1 + θ) * (σmax^2 + σk) # ‖J'J + σₖ I‖ = ‖J‖² + σₖ
262+
ν = θ / (σmax^2 + σk) # ‖J'J + σₖ I‖ = ‖J‖² + σₖ
255263
tired = k maxIter || elapsed_time > maxTime
256264
end
257265

@@ -260,9 +268,9 @@ function LM(
260268
@info @sprintf "%6d %8s %8.1e %8.1e" k "" fk hk
261269
elseif optimal
262270
#! format: off
263-
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8s %7.1e %7.1e %7.1e %7.1e" k 1 fk hk sqrt(ξ1) sqrt(ξ1) "" σk norm(xk) norm(s) νInv
271+
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8s %7.1e %7.1e %7.1e %7.1e" k 1 fk hk sqrt_ξ1_νInv sqrt(ξ1) "" σk norm(xk) norm(s) 1/ν
264272
#! format: on
265-
@info "LM: terminating with √ξ1 = $(sqrt(ξ1))"
273+
@info "LM: terminating with √(ξ1/ν) = $(sqrt_ξ1_νInv)"
266274
end
267275
end
268276
status = if optimal
@@ -279,7 +287,7 @@ function LM(
279287
set_status!(stats, status)
280288
set_solution!(stats, xk)
281289
set_objective!(stats, fk + hk)
282-
set_residuals!(stats, zero(eltype(xk)), ξ1 0 ? sqrt(ξ1) : ξ1)
290+
set_residuals!(stats, zero(eltype(xk)), ξ1 0 ? sqrt_ξ1_νInv : ξ1)
283291
set_iter!(stats, k)
284292
set_time!(stats, elapsed_time)
285293
set_solver_specific!(stats, :sigma, σk)

src/R2DH.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ function R2DH(
192192
Dkσk = D.d .+ σk
193193
DNorm = norm(D.d, Inf)
194194

195-
ν = 1 / ((DNorm + σk) * (1 + θ))
195+
ν = θ / (DNorm + σk)
196196
mν∇fk = -ν * ∇fk
197197
sqrt_ξ_νInv = one(R)
198198

@@ -275,7 +275,7 @@ function R2DH(
275275
end
276276

277277
Dkσk .= D.d .+ σk
278-
ν = 1 / ((DNorm + σk) * (1 + θ))
278+
ν = θ / (DNorm + σk)
279279

280280
tired = maxIter > 0 && k maxIter
281281
if !tired

src/R2N.jl

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

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

139140
optimal = false
@@ -165,11 +166,11 @@ function R2N(
165166
# take first proximal gradient step s1 and see if current xk is nearly stationary
166167
# s1 minimizes φ1(s) + ‖s‖² / 2 / ν + ψ(s) ⟺ s1 ∈ prox{νψ}(-ν∇φ1(0)).
167168

168-
subsolver_options.ν = 1 / νInv
169+
subsolver_options.ν = ν
169170
prox!(s, ψ, -subsolver_options.ν * ∇fk, subsolver_options.ν)
170171
ξ1 = hk - mk1(s) + max(1, abs(hk)) * 10 * eps()
171172
ξ1 > 0 || error("R2N: first prox-gradient step should produce a decrease but ξ1 = $(ξ1)")
172-
sqrt_ξ1_νInv = sqrt(ξ1 * νInv)
173+
sqrt_ξ1_νInv = sqrt(ξ1 / ν)
173174

174175
if ξ1 0 && k == 1
175176
ϵ_increment = ϵr * sqrt_ξ1_νInv
@@ -187,7 +188,7 @@ function R2N(
187188
subsolver_options.ϵa = k == 1 ? 1.0e-3 : min(sqrt_ξ1_νInv^(1.5), sqrt_ξ1_νInv * 1e-3)
188189
verbose > 0 && @debug "setting inner stopping tolerance to" subsolver_options.optTol
189190
subsolver_options.σk = σk
190-
subsolver_args = subsolver == R2DH ? (SpectralGradient(νInv, f.meta.nvar),) : ()
191+
subsolver_args = subsolver == R2DH ? (SpectralGradient(1 / ν, f.meta.nvar),) : ()
191192
s, iter, _ = with_logger(subsolver_logger) do
192193
subsolver(φ, ∇φ!, ψ, subsolver_args..., subsolver_options, s)
193194
end
@@ -247,14 +248,15 @@ 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

254256
if ρk < η1 || ρk == Inf
255257
σk = σk * γ
256258
end
257-
νInv = (1 + θ) * (σk + λmax)
259+
ν = θ / (σk + λmax)
258260
tired = k maxIter || elapsed_time > maxTime
259261
end
260262

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,

0 commit comments

Comments
 (0)