Skip to content

Commit eaf8455

Browse files
add powernorm iteration
1 parent b3a23f8 commit eaf8455

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
@@ -218,7 +218,7 @@ function SolverCore.solve!(
218218
γ::T = T(3),
219219
β::T = 1 / eps(T),
220220
θ::T = 1/(1 + eps(T)^(1 / 5)),
221-
compute_opnorm::Bool = false,
221+
opnorm_maxiter::Int = 1,
222222
sub_kwargs::NamedTuple = NamedTuple(),
223223
) where {T, V, G}
224224
reset!(stats)
@@ -295,12 +295,12 @@ function SolverCore.solve!(
295295
found_λ = true
296296
solver.subpb.model.B = hess_op(nlp, xk)
297297

298-
if !compute_opnorm
299-
mul!(solver.subpb.model.v, solver.subpb.model.B, solver.v0)
300-
λmax = dot(solver.v0, solver.subpb.model.v)
301-
else
298+
if opnorm_maxiter 0
302299
λmax, found_λ = opnorm(solver.subpb.model.B)
300+
else
301+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
303302
end
303+
304304
found_λ || error("operator norm computation failed")
305305

306306
ν₁ = θ / (λmax + σk)
@@ -450,11 +450,10 @@ function SolverCore.solve!(
450450
end
451451
solver.subpb.model.B = hess_op(nlp, xk)
452452

453-
if !compute_opnorm
454-
mul!(solver.subpb.model.v, solver.subpb.model.B, solver.v0)
455-
λmax = dot(solver.v0, solver.subpb.model.v)
456-
else
453+
if opnorm_maxiter 0
457454
λmax, found_λ = opnorm(solver.subpb.model.B)
455+
else
456+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
458457
end
459458

460459
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
@@ -58,6 +59,9 @@ function TRSolver(
5859

5960
m_fh_hist = fill(T(-Inf), m_monotone - 1)
6061

62+
v0 = [(-1.0)^i for i in 0:(reg_nlp.model.meta.nvar-1)]
63+
v0 ./= sqrt(reg_nlp.model.meta.nvar)
64+
6165
ψ =
6266
has_bnds || subsolver == TRDHSolver ?
6367
shifted(reg_nlp.h, xk, l_bound_m_x, u_bound_m_x, reg_nlp.selected) :
@@ -80,6 +84,7 @@ function TRSolver(
8084
χ,
8185
xkn,
8286
s,
87+
v0,
8388
has_bnds,
8489
l_bound,
8590
u_bound,
@@ -135,6 +140,7 @@ For advanced usage, first define a solver "TRSolver" to preallocate the memory u
135140
- `η2::T = T(0.9)`: very successful iteration threshold;
136141
- `γ::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;
137142
- `m_monotone::Int = 1`: monotonicity parameter. By default, TR is monotone but the non-monotone variant will be used if `m_monotone > 1`;
143+
- `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;
138144
- `χ::F = NormLinf(1)`: norm used to define the trust-region;`
139145
- `subsolver::S = R2Solver`: subsolver used to solve the subproblem that appears at each iteration.
140146
- `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,)`.
@@ -208,6 +214,7 @@ function SolverCore.solve!(
208214
η2::T = T(0.9),
209215
γ::T = T(3),
210216
sub_kwargs::NamedTuple = NamedTuple(),
217+
opnorm_maxiter::Int = 1,
211218
) where {T, G, V}
212219
reset!(stats)
213220

@@ -286,9 +293,14 @@ function SolverCore.solve!(
286293
∇fk⁻ .= ∇fk
287294

288295
quasiNewtTest = isa(nlp, QuasiNewtonModel)
289-
λmax = T(1)
296+
λmax::T = T(1)
297+
found_λ = true
290298

291-
λmax, found_λ = opnorm(solver.subpb.model.B)
299+
if opnorm_maxiter 0
300+
λmax, found_λ = opnorm(solver.subpb.model.B)
301+
else
302+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
303+
end
292304
found_λ || error("operator norm computation failed")
293305

294306
ν₁ = α * Δk / (1 + λmax ** Δk + 1))
@@ -345,7 +357,6 @@ function SolverCore.solve!(
345357
callback(nlp, solver, stats)
346358

347359
done = stats.status != :unknown
348-
349360
while !done
350361
sub_atol = stats.iter == 0 ? 1e-5 : max(sub_atol, min(1e-2, sqrt_ξ1_νInv))
351362
∆_effective = min* χ(s), Δk)
@@ -446,7 +457,11 @@ function SolverCore.solve!(
446457
push!(nlp, s, ∇fk⁻) # update QN operator
447458
end
448459

449-
λmax, found_λ = opnorm(solver.subpb.model.B)
460+
if opnorm_maxiter 0
461+
λmax, found_λ = opnorm(solver.subpb.model.B)
462+
else
463+
λmax = power_method!(solver.subpb.model.B, solver.v0, solver.subpb.model.v, opnorm_maxiter)
464+
end
450465
found_λ || error("operator norm computation failed")
451466

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