Skip to content

Commit 92597d1

Browse files
add powernorm iteration
1 parent fbc7ab2 commit 92597d1

4 files changed

Lines changed: 42 additions & 15 deletions

File tree

src/R2N.jl

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ For advanced usage, first define a solver "R2NSolver" to preallocate the memory
139139
- `η2::T = T(0.9)`: very successful iteration threshold;
140140
- `γ::T = T(3)`: regularization parameter multiplier, σ := σ/γ when the iteration is very successful and σ := σγ when the iteration is unsuccessful;
141141
- `θ::T = 1/(1 + eps(T)^(1 / 5))`: is the model decrease fraction with respect to the decrease of the Cauchy model;
142-
- `compute_opnorm::Bool = false`: whether the operator norm of Bₖ should be computed at each iteration. If false, a Rayleigh quotient is computed instead. The first option causes the solver to converge in fewer iterations but the computational cost per iteration is larger;
142+
- `opnorm_maxiter::Int = 1`: how many iterations of the power method to use to compute the operator norm of Bₖ. If a negative number is provided, then Arpack is used instead;
143143
- `m_monotone::Int = 1`: monotonicity parameter. By default, R2N is monotone but the non-monotone variant will be used if `m_monotone > 1`;
144144
145145
The algorithm stops either when `√(ξₖ/νₖ) < atol + rtol*√(ξ₀/ν₀) ` or `ξₖ < 0` and `√(-ξₖ/νₖ) < neg_tol` where ξₖ := f(xₖ) + h(xₖ) - φ(sₖ; xₖ) - ψ(sₖ; xₖ), and √(ξₖ/νₖ) is a stationarity measure.
@@ -230,7 +230,7 @@ function SolverCore.solve!(
230230
γ::T = T(3),
231231
β::T = 1 / eps(T),
232232
θ::T = 1/(1 + eps(T)^(1 / 5)),
233-
compute_opnorm::Bool = false,
233+
opnorm_maxiter::Int = 1,
234234
) where {T, V, G}
235235
reset!(stats)
236236

@@ -305,12 +305,12 @@ function SolverCore.solve!(
305305
found_λ = true
306306
solver.subpb.model.B = hess_op(nlp, xk)
307307

308-
if !compute_opnorm
309-
mul!(solver.subpb.model.v, solver.subpb.model.B, solver.v0)
310-
λmax = dot(solver.v0, solver.subpb.model.v)
311-
else
308+
if opnorm_maxiter 0
312309
λmax, found_λ = opnorm(solver.subpb.model.B)
310+
else
311+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
313312
end
313+
314314
found_λ || error("operator norm computation failed")
315315

316316
ν₁ = θ / (λmax + σk)
@@ -443,11 +443,10 @@ function SolverCore.solve!(
443443
end
444444
solver.subpb.model.B = hess_op(nlp, xk)
445445

446-
if !compute_opnorm
447-
mul!(solver.subpb.model.v, solver.subpb.model.B, solver.v0)
448-
λmax = dot(solver.v0, solver.subpb.model.v)
449-
else
446+
if opnorm_maxiter 0
450447
λmax, found_λ = opnorm(solver.subpb.model.B)
448+
else
449+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
451450
end
452451

453452
found_λ || error("operator norm computation failed")

src/TR_alg.jl

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mutable struct TRSolver{
1818
χ::N
1919
xkn::V
2020
s::V
21+
v0::V
2122
has_bnds::Bool
2223
l_bound::V
2324
u_bound::V
@@ -54,6 +55,9 @@ function TRSolver(
5455
u_bound_m_x = similar(xk, 0)
5556
end
5657

58+
v0 = [(-1.0)^i for i in 0:(reg_nlp.model.meta.nvar-1)]
59+
v0 ./= sqrt(reg_nlp.model.meta.nvar)
60+
5761
ψ =
5862
has_bnds || subsolver == TRDHSolver ?
5963
shifted(reg_nlp.h, xk, l_bound_m_x, u_bound_m_x, reg_nlp.selected) :
@@ -76,6 +80,7 @@ function TRSolver(
7680
χ,
7781
xkn,
7882
s,
83+
v0,
7984
has_bnds,
8085
l_bound,
8186
u_bound,
@@ -129,6 +134,7 @@ For advanced usage, first define a solver "TRSolver" to preallocate the memory u
129134
- `η1::T = √√eps(T)`: successful iteration threshold;
130135
- `η2::T = T(0.9)`: very successful iteration threshold;
131136
- `γ::T = T(3)`: trust-region radius parameter multiplier. Must satisfy `γ > 1`. The trust-region radius is updated as Δ := Δ*γ when the iteration is very successful and Δ := Δ/γ when the iteration is unsuccessful;
137+
- `opnorm_maxiter::Int = 1`: how many iterations of the power method to use to compute the operator norm of Bₖ. If a negative number is provided, then Arpack is used instead;
132138
- `χ::F = NormLinf(1)`: norm used to define the trust-region;`
133139
- `subsolver::S = R2Solver`: subsolver used to solve the subproblem that appears at each iteration.
134140
@@ -199,6 +205,7 @@ function SolverCore.solve!(
199205
η1::T = √√eps(T),
200206
η2::T = T(0.9),
201207
γ::T = T(3),
208+
opnorm_maxiter::Int = 1,
202209
) where {T, G, V}
203210
reset!(stats)
204211

@@ -273,9 +280,14 @@ function SolverCore.solve!(
273280
∇fk⁻ .= ∇fk
274281

275282
quasiNewtTest = isa(nlp, QuasiNewtonModel)
276-
λmax = T(1)
283+
λmax::T = T(1)
284+
found_λ = true
277285

278-
λmax, found_λ = opnorm(solver.subpb.model.B)
286+
if opnorm_maxiter 0
287+
λmax, found_λ = opnorm(solver.subpb.model.B)
288+
else
289+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
290+
end
279291
found_λ || error("operator norm computation failed")
280292

281293
ν₁ = α * Δk / (1 + λmax ** Δk + 1))
@@ -330,7 +342,6 @@ function SolverCore.solve!(
330342
callback(nlp, solver, stats)
331343

332344
done = stats.status != :unknown
333-
334345
while !done
335346
sub_atol = stats.iter == 0 ? 1e-5 : max(sub_atol, min(1e-2, sqrt_ξ1_νInv))
336347
∆_effective = min* χ(s), Δk)
@@ -427,7 +438,11 @@ function SolverCore.solve!(
427438
push!(nlp, s, ∇fk⁻) # update QN operator
428439
end
429440

430-
λmax, found_λ = opnorm(solver.subpb.model.B)
441+
if opnorm_maxiter 0
442+
λmax, found_λ = opnorm(solver.subpb.model.B)
443+
else
444+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
445+
end
431446
found_λ || error("operator norm computation failed")
432447

433448
∇fk⁻ .= ∇fk

src/utils.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@ export RegularizedExecutionStats
22

33
import SolverCore.GenericExecutionStats
44

5+
function power_method!(B::M, v₀::S, v₁::S, max_iter::Int = 1) where{M, S}
6+
@assert max_iter >= 1
7+
mul!(v₁, B, v₀)
8+
normalize!(v₁) # v1 = B*v0 / ‖B*v0‖
9+
for i = 2:max_iter
10+
v₀ .= v₁ # v0 = v1
11+
mul!(v₁, B, v₀)
12+
normalize!(v₁)
13+
end
14+
mul!(v₁, B, v₀)
15+
return dot(v₀, v₁)
16+
end
17+
518
# use Arpack to obtain largest eigenvalue in magnitude with a minimum of robustness
619
function LinearAlgebra.opnorm(B; kwargs...)
720
m, n = size(B)

test/test_allocs.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ end
5959
(solver_name == "R2DH" || solver_name == "R2N") && @test @wrappedallocs(
6060
solve!(solver, reg_nlp, stats, σk = 1.0, atol = 1e-6, rtol = 1e-6)
6161
) == 0
62-
solver_name == "TRDH" &&
62+
(solver_name == "TRDH") &&
6363
@test @wrappedallocs(solve!(solver, reg_nlp, stats, atol = 1e-6, rtol = 1e-6)) == 0
6464
@test stats.status == :first_order
6565
end

0 commit comments

Comments
 (0)