Skip to content

Commit 1564813

Browse files
Merge branch 'JuliaSmoothOptimizers:master' into LMTR-JSO
2 parents 6d413f1 + b9797b2 commit 1564813

File tree

5 files changed

+54
-16
lines changed

5 files changed

+54
-16
lines changed

src/R2N.jl

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mutable struct R2NSolver{
1212
xk::V
1313
∇fk::V
1414
∇fk⁻::V
15+
y::V
1516
mν∇fk::V
1617
ψ::G
1718
xkn::V
@@ -40,6 +41,7 @@ function R2NSolver(
4041
xk = similar(x0)
4142
∇fk = similar(x0)
4243
∇fk⁻ = similar(x0)
44+
y = similar(x0)
4345
mν∇fk = similar(x0)
4446
xkn = similar(x0)
4547
s = similar(x0)
@@ -70,6 +72,7 @@ function R2NSolver(
7072
xk,
7173
∇fk,
7274
∇fk⁻,
75+
y,
7376
mν∇fk,
7477
ψ,
7578
xkn,
@@ -154,6 +157,12 @@ Notably, you can access, and modify, the following:
154157
- `stats.solver_specific[:nonsmooth_obj]`: current value of the nonsmooth part of the objective function;
155158
- `stats.status`: current status of the algorithm. Should be `:unknown` unless the algorithm has attained a stopping criterion. Changing this to anything other than `:unknown` will stop the algorithm, but you should use `:user` to properly indicate the intention;
156159
- `stats.elapsed_time`: elapsed time in seconds.
160+
Similarly to the callback, when using a quasi-Newton approximation, two functions, `qn_update_y!(nlp, solver, stats)` and `qn_copy!(nlp, solver, stats)` are called at each update of the approximation.
161+
Namely, the former computes the `y` vector for which the pair `(s, y)` is pushed into the approximation.
162+
By default, `y := ∇fk⁻ - ∇fk`.
163+
The latter allows the user to tell which values should be copied for the next iteration.
164+
By default, only the gradient is copied: `∇fk⁻ .= ∇fk`.
165+
This might be useful when using R2N in a constrained optimization context, when the gradient of the Lagrangian function is pushed at each iteration rather than the gradient of the objective function.
157166
"""
158167
function R2N(
159168
nlp::AbstractNLPModel{T, V},
@@ -200,6 +209,8 @@ function SolverCore.solve!(
200209
reg_nlp::AbstractRegularizedNLPModel{T, V},
201210
stats::GenericExecutionStats{T, V};
202211
callback = (args...) -> nothing,
212+
qn_update_y!::Function = _qn_grad_update_y!,
213+
qn_copy!::Function = _qn_grad_copy!,
203214
x::V = reg_nlp.model.meta.x0,
204215
atol::T = eps(T),
205216
rtol::T = eps(T),
@@ -283,7 +294,7 @@ function SolverCore.solve!(
283294

284295
fk = obj(nlp, xk)
285296
grad!(nlp, xk, ∇fk)
286-
∇fk⁻ .= ∇fk
297+
qn_copy!(nlp, solver, stats)
287298

288299
quasiNewtTest = isa(nlp, QuasiNewtonModel)
289300
λmax::T = T(1)
@@ -416,15 +427,14 @@ function SolverCore.solve!(
416427
grad!(nlp, xk, ∇fk)
417428

418429
if quasiNewtTest
419-
@. ∇fk⁻ = ∇fk - ∇fk⁻
420-
push!(nlp, s, ∇fk⁻)
430+
qn_update_y!(nlp, solver, stats)
431+
push!(nlp, s, solver.y)
432+
qn_copy!(nlp, solver, stats)
421433
end
422434
solver.subpb.model.B = hess_op(nlp, xk)
423435

424436
λmax, found_λ = opnorm(solver.subpb.model.B)
425437
found_λ || error("operator norm computation failed")
426-
427-
∇fk⁻ .= ∇fk
428438
end
429439

430440
if η2 ρk < Inf
@@ -500,3 +510,19 @@ function SolverCore.solve!(
500510
set_residuals!(stats, zero(eltype(xk)), sqrt_ξ1_νInv)
501511
return stats
502512
end
513+
514+
function _qn_grad_update_y!(
515+
nlp::AbstractNLPModel{T, V},
516+
solver::R2NSolver{T, G, V},
517+
stats::GenericExecutionStats,
518+
) where {T, V, G}
519+
@. solver.y = solver.∇fk - solver.∇fk⁻
520+
end
521+
522+
function _qn_grad_copy!(
523+
nlp::AbstractNLPModel{T, V},
524+
solver::R2NSolver{T, G, V},
525+
stats::GenericExecutionStats,
526+
) where {T, V, G}
527+
solver.∇fk⁻ .= solver.∇fk
528+
end

src/RegularizedOptimization.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ using Arpack, ProximalOperators
88

99
# dependencies from us
1010
using LinearOperators,
11-
ManualNLPModels, NLPModels, NLPModelsModifiers, RegularizedProblems, ShiftedProximalOperators, SolverCore
11+
ManualNLPModels,
12+
NLPModels,
13+
NLPModelsModifiers,
14+
RegularizedProblems,
15+
ShiftedProximalOperators,
16+
SolverCore
1217
using Percival: AugLagModel, update_y!, update_μ!
1318

1419
const callback_docstring = "

src/TRDH_alg.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ function TRDH(
190190
selected::AbstractVector{<:Integer} = 1:length(x0),
191191
kwargs...,
192192
) where {R <: Real, F, G, H, DQN <: AbstractDiagonalQuasiNewtonOperator, X}
193-
nlp = NLPModel(x0, f, grad=∇f!)
193+
nlp = NLPModel(x0, f, grad = ∇f!)
194194
reg_nlp = RegularizedNLPModel(nlp, h, selected)
195195
stats = TRDH(
196196
reg_nlp;

src/TR_alg.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ function TRSolver(
5959
shifted(reg_nlp.h, xk, l_bound_m_x, u_bound_m_x, reg_nlp.selected) :
6060
shifted(reg_nlp.h, xk, T(1), χ)
6161

62-
Bk = isa(reg_nlp.model, QuasiNewtonModel) ? hess_op(reg_nlp.model, xk) : hess_op!(reg_nlp.model, xk, similar(xk))
62+
Bk =
63+
isa(reg_nlp.model, QuasiNewtonModel) ? hess_op(reg_nlp.model, xk) :
64+
hess_op!(reg_nlp.model, xk, similar(xk))
6365
sub_nlp = R2NModel(Bk, ∇fk, zero(T), x0) #FIXME
6466
subpb = RegularizedNLPModel(sub_nlp, ψ)
6567
substats = RegularizedExecutionStats(subpb)
@@ -341,7 +343,7 @@ function SolverCore.solve!(
341343
set_radius!(solver.subsolver.ψ, ∆_effective)
342344
set_radius!(ψ, ∆_effective)
343345
end
344-
with_logger(subsolver_logger) do
346+
with_logger(subsolver_logger) do
345347
if isa(solver.subsolver, TRDHSolver) #FIXME
346348
solver.subsolver.D.d[1] = 1/ν₁
347349
solve!(

test/test_allocs.jl

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,26 @@ end
4242
# Test non allocating solve!
4343
@testset "NLP allocs" begin
4444
for (h, h_name) ((NormL0(λ), "l0"),)
45-
for (solver, solver_name) ((:R2Solver, "R2"), (:R2DHSolver, "R2DH"), (:R2NSolver, "R2N"), (:TRDHSolver, "TRDH"), (:TRSolver, "TR"))
45+
for (solver, solver_name) (
46+
(:R2Solver, "R2"),
47+
(:R2DHSolver, "R2DH"),
48+
(:R2NSolver, "R2N"),
49+
(:TRDHSolver, "TRDH"),
50+
(:TRSolver, "TR"),
51+
)
4652
@testset "$(solver_name)" begin
4753
(solver_name == "R2N" || solver_name == "TR") && continue #FIXME
4854
reg_nlp = RegularizedNLPModel(LBFGSModel(bpdn), h)
4955
solver = eval(solver)(reg_nlp)
5056
stats = RegularizedExecutionStats(reg_nlp)
51-
solver_name == "R2" && @test @wrappedallocs(
52-
solve!(solver, reg_nlp, stats, ν = 1.0, atol = 1e-6, rtol = 1e-6)
53-
) == 0
57+
solver_name == "R2" &&
58+
@test @wrappedallocs(solve!(solver, reg_nlp, stats, ν = 1.0, atol = 1e-6, rtol = 1e-6)) ==
59+
0
5460
solver_name == "R2DH" && @test @wrappedallocs(
5561
solve!(solver, reg_nlp, stats, σk = 1.0, atol = 1e-6, rtol = 1e-6)
5662
) == 0
57-
solver_name == "TRDH" && @test @wrappedallocs(
58-
solve!(solver, reg_nlp, stats, atol = 1e-6, rtol = 1e-6)
59-
) == 0
63+
solver_name == "TRDH" &&
64+
@test @wrappedallocs(solve!(solver, reg_nlp, stats, atol = 1e-6, rtol = 1e-6)) == 0
6065
@test stats.status == :first_order
6166
end
6267
end

0 commit comments

Comments
 (0)