Skip to content

Commit 70d7fb0

Browse files
add powernorm iteration
1 parent ec02da6 commit 70d7fb0

File tree

4 files changed

+42
-15
lines changed

4 files changed

+42
-15
lines changed

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
- `sub_kwargs::NamedTuple = NamedTuple()`: a named tuple containing the keyword arguments to be sent to the subsolver. The solver will fail if invalid keyword arguments are provided to the subsolver. For example, if the subsolver is `R2Solver`, you can pass `sub_kwargs = (max_iter = 100, σmin = 1e-6,)`.
145145
@@ -232,7 +232,7 @@ function SolverCore.solve!(
232232
γ::T = T(3),
233233
β::T = 1 / eps(T),
234234
θ::T = 1/(1 + eps(T)^(1 / 5)),
235-
compute_opnorm::Bool = false,
235+
opnorm_maxiter::Int = 1,
236236
sub_kwargs::NamedTuple = NamedTuple(),
237237
) where {T, V, G}
238238
reset!(stats)
@@ -308,12 +308,12 @@ function SolverCore.solve!(
308308
found_λ = true
309309
solver.subpb.model.B = hess_op(nlp, xk)
310310

311-
if !compute_opnorm
312-
mul!(solver.subpb.model.v, solver.subpb.model.B, solver.v0)
313-
λmax = dot(solver.v0, solver.subpb.model.v)
314-
else
311+
if opnorm_maxiter 0
315312
λmax, found_λ = opnorm(solver.subpb.model.B)
313+
else
314+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
316315
end
316+
317317
found_λ || error("operator norm computation failed")
318318

319319
ν₁ = θ / (λmax + σk)
@@ -446,11 +446,10 @@ function SolverCore.solve!(
446446
end
447447
solver.subpb.model.B = hess_op(nlp, xk)
448448

449-
if !compute_opnorm
450-
mul!(solver.subpb.model.v, solver.subpb.model.B, solver.v0)
451-
λmax = dot(solver.v0, solver.subpb.model.v)
452-
else
449+
if opnorm_maxiter 0
453450
λmax, found_λ = opnorm(solver.subpb.model.B)
451+
else
452+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
454453
end
455454

456455
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
- `sub_kwargs::NamedTuple = NamedTuple()`: a named tuple containing the keyword arguments to be sent to the subsolver. The solver will fail if invalid keyword arguments are provided to the subsolver. For example, if the subsolver is `R2Solver`, you can pass `sub_kwargs = (max_iter = 100, σmin = 1e-6,)`.
@@ -201,6 +207,7 @@ function SolverCore.solve!(
201207
η2::T = T(0.9),
202208
γ::T = T(3),
203209
sub_kwargs::NamedTuple = NamedTuple(),
210+
opnorm_maxiter::Int = 1,
204211
) where {T, G, V}
205212
reset!(stats)
206213

@@ -275,9 +282,14 @@ function SolverCore.solve!(
275282
∇fk⁻ .= ∇fk
276283

277284
quasiNewtTest = isa(nlp, QuasiNewtonModel)
278-
λmax = T(1)
285+
λmax::T = T(1)
286+
found_λ = true
279287

280-
λmax, found_λ = opnorm(solver.subpb.model.B)
288+
if opnorm_maxiter 0
289+
λmax, found_λ = opnorm(solver.subpb.model.B)
290+
else
291+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
292+
end
281293
found_λ || error("operator norm computation failed")
282294

283295
ν₁ = α * Δk / (1 + λmax ** Δk + 1))
@@ -332,7 +344,6 @@ function SolverCore.solve!(
332344
callback(nlp, solver, stats)
333345

334346
done = stats.status != :unknown
335-
336347
while !done
337348
sub_atol = stats.iter == 0 ? 1e-5 : max(sub_atol, min(1e-2, sqrt_ξ1_νInv))
338349
∆_effective = min* χ(s), Δk)
@@ -431,7 +442,11 @@ function SolverCore.solve!(
431442
push!(nlp, s, ∇fk⁻) # update QN operator
432443
end
433444

434-
λmax, found_λ = opnorm(solver.subpb.model.B)
445+
if opnorm_maxiter 0
446+
λmax, found_λ = opnorm(solver.subpb.model.B)
447+
else
448+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
449+
end
435450
found_λ || error("operator norm computation failed")
436451

437452
∇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)