Skip to content

Commit 7fbfc5c

Browse files
add doc and unit tests for TRDH
1 parent b737d55 commit 7fbfc5c

File tree

2 files changed

+61
-29
lines changed

2 files changed

+61
-29
lines changed

src/TRDH_alg.jl

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ A trust-region method with diagonal Hessian approximation for the problem
9999
100100
min f(x) + h(x)
101101
102-
where f: ℝⁿ → ℝ has a Lipschitz-continuous Jacobian, and h: ℝⁿ → ℝ is
103-
lower semi-continuous and proper.
102+
where f: ℝⁿ → ℝ has a Lipschitz-continuous gradient,, and h: ℝⁿ → ℝ is
103+
lower semi-continuous, proper and prox-bounded.
104104
105105
About each iterate xₖ, a step sₖ is computed as an approximate solution of
106106
@@ -110,28 +110,57 @@ where φ(s ; xₖ) = f(xₖ) + ∇f(xₖ)ᵀs + ½ sᵀ Dₖ s is a quadratic a
110110
ψ(s; xₖ) = h(xₖ + s), ‖⋅‖ is a user-defined norm, Dₖ is a diagonal Hessian approximation
111111
and Δₖ > 0 is the trust-region radius.
112112
113-
### Arguments
114-
115-
* `nlp::AbstractDiagonalQNModel`: a smooth optimization problem
116-
* `h`: a regularizer such as those defined in ProximalOperators
117-
* `χ`: a norm used to define the trust region in the form of a regularizer
118-
* `options::ROSolverOptions`: a structure containing algorithmic parameters
119-
120-
The objective and gradient of `nlp` will be accessed.
121-
122-
In the second form, instead of `nlp`, the user may pass in
123-
124-
* `f` a function such that `f(x)` returns the value of f at x
125-
* `∇f!` a function to evaluate the gradient in place, i.e., such that `∇f!(g, x)` store ∇f(x) in `g`
126-
* `x0::AbstractVector`: an initial guess.
127-
128-
### Keyword arguments
129-
130-
#TODO
131-
132-
### Return values
133-
134-
#TODO
113+
For advanced usage, first define a solver "TRDHSolver" to preallocate the memory used in the algorithm, and then call `solve!`:
114+
115+
solver = TRDH(reg_nlp; D = nothing, χ = NormLinf(1))
116+
solve!(solver, reg_nlp)
117+
118+
stats = RegularizedExecutionStats(reg_nlp)
119+
solve!(solver, reg_nlp, stats)
120+
121+
# Arguments
122+
* `reg_nlp::AbstractRegularizedNLPModel{T, V}`: the problem to solve, see `RegularizedProblems.jl`, `NLPModels.jl`.
123+
124+
# Keyword arguments
125+
- `x::V = nlp.meta.x0`: the initial guess;
126+
- `atol::T = √eps(T)`: absolute tolerance;
127+
- `rtol::T = √eps(T)`: relative tolerance;
128+
- `neg_tol::T = eps(T)^(1 / 4)`: negative tolerance;
129+
- `max_eval::Int = -1`: maximum number of evaluation of the objective function (negative number means unlimited);
130+
- `max_time::Float64 = 30.0`: maximum time limit in seconds;
131+
- `max_iter::Int = 10000`: maximum number of iterations;
132+
- `verbose::Int = 0`: if > 0, display iteration details every `verbose` iteration;
133+
- `Δk::T = T(1)`: initial value of the trust-region radius;
134+
- `η1::T = √√eps(T)`: successful iteration threshold;
135+
- `η2::T = T(0.9)`: very successful iteration threshold;
136+
- `γ::T = T(3)`: trust-region radius parameter multiplier, Δ := Δ*γ when the iteration is very successful and Δ := Δ/γ when the iteration is unsuccessful;
137+
- `α::T = 1/eps(T)`: TODO
138+
- `β::T = 1/eps(T)`: TODO
139+
- `reduce_TR::Bool = True`: TODO
140+
- `χ::F = NormLinf(1)`: norm used to define the trust-region;`
141+
- `D::L = nothing`: diagonal quasi-Newton approximation used for the model φ. If nothing is provided and `reg_nlp.model` is not a diagonal quasi-Newton approximation, a spectral gradient approximation is used.`
142+
143+
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.
144+
145+
# Output
146+
The value returned is a `GenericExecutionStats`, see `SolverCore.jl`.
147+
148+
# Callback
149+
The callback is called at each iteration.
150+
The expected signature of the callback is `callback(nlp, solver, stats)`, and its output is ignored.
151+
Changing any of the input arguments will affect the subsequent iterations.
152+
In particular, setting `stats.status = :user` will stop the algorithm.
153+
All relevant information should be available in `nlp` and `solver`.
154+
Notably, you can access, and modify, the following:
155+
- `solver.xk`: current iterate;
156+
- `solver.∇fk`: current gradient;
157+
- `stats`: structure holding the output of the algorithm (`GenericExecutionStats`), which contains, among other things:
158+
- `stats.iter`: current iteration counter;
159+
- `stats.objective`: current objective function value;
160+
- `stats.solver_specific[:smooth_obj]`: current value of the smooth part of the objective function;
161+
- `stats.solver_specific[:nonsmooth_obj]`: current value of the nonsmooth part of the objective function;
162+
- `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;
163+
- `stats.elapsed_time`: elapsed time in seconds.
135164
"""
136165
function TRDH(
137166
nlp::AbstractDiagonalQNModel{T, V},

test/test_allocs.jl

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,21 @@ end
4242
# Test non allocating solve!
4343
@testset "allocs" begin
4444
for (h, h_name) ((NormL0(λ), "l0"),)
45-
for (solver, solver_name) ((:R2Solver, "R2"), (:R2DHSolver, "R2DH"), (:R2NSolver, "R2N"))
45+
for (solver, solver_name) ((:R2Solver, "R2"), (:R2DHSolver, "R2DH"), (:R2NSolver, "R2N"), (:TRDHSolver, "TRDH"), (:TRSolver, "TR"))
4646
@testset "$(solver_name)" begin
47-
solver_name == "R2N" && continue #FIXME
47+
(solver_name == "R2N" || solver_name == "TR") && continue #FIXME
4848
reg_nlp = RegularizedNLPModel(LBFGSModel(bpdn), h)
4949
solver = eval(solver)(reg_nlp)
5050
stats = RegularizedExecutionStats(reg_nlp)
51-
solver_name == "R2" &&
52-
@test @wrappedallocs(solve!(solver, reg_nlp, stats, ν = 1.0, atol = 1e-6, rtol = 1e-6)) ==
53-
0
51+
solver_name == "R2" && @test @wrappedallocs(
52+
solve!(solver, reg_nlp, stats, ν = 1.0, atol = 1e-6, rtol = 1e-6)
53+
) == 0
5454
solver_name == "R2DH" && @test @wrappedallocs(
5555
solve!(solver, reg_nlp, stats, σk = 1.0, atol = 1e-6, rtol = 1e-6)
5656
) == 0
57+
solver_name == "TRDH" && @test @wrappedallocs(
58+
solve!(solver, reg_nlp, stats, atol = 1e-6, rtol = 1e-6)
59+
) == 0
5760
@test stats.status == :first_order
5861
end
5962
end

0 commit comments

Comments
 (0)