Skip to content

Commit 836c02e

Browse files
TR function signatures
1 parent 3358ff0 commit 836c02e

File tree

1 file changed

+34
-240
lines changed

1 file changed

+34
-240
lines changed

src/TR_alg.jl

Lines changed: 34 additions & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ mutable struct TRSolver{
2929
end
3030

3131
function TRSolver(
32-
reg_nlp::AbstractRegularizedNLPModel{T, V},
33-
χ::X;
32+
reg_nlp::AbstractRegularizedNLPModel{T, V};
33+
χ::X = NormLinf(one(T)),
3434
subsolver = R2Solver
3535
) where {T, V, X}
3636
x0 = reg_nlp.model.meta.x0
@@ -138,245 +138,40 @@ function TR(
138138
subsolver = R2,
139139
subsolver_options = ROSolverOptions(ϵa = options.ϵa),
140140
selected::AbstractVector{<:Integer} = 1:(f.meta.nvar),
141+
kwargs...
141142
) where {H, X, R}
142-
start_time = time()
143-
elapsed_time = 0.0
144-
# initialize passed options
145-
ϵ = options.ϵa
146-
ϵ_subsolver = subsolver_options.ϵa
147-
ϵr = options.ϵr
148-
Δk = options.Δk
149-
verbose = options.verbose
150-
maxIter = options.maxIter
151-
maxTime = options.maxTime
152-
η1 = options.η1
153-
η2 = options.η2
154-
γ = options.γ
155-
α = options.α
156-
θ = options.θ
157-
β = options.β
158-
159-
# store initial values of the subsolver_options fields that will be modified
160-
ν_subsolver = subsolver_options.ν
161-
ϵa_subsolver = subsolver_options.ϵa
162-
Δk_subsolver = subsolver_options.Δk
163-
164-
local l_bound, u_bound
165-
if has_bounds(f) || subsolver == TRDH
166-
l_bound = f.meta.lvar
167-
u_bound = f.meta.uvar
168-
end
169-
170-
if verbose == 0
171-
ptf = Inf
172-
elseif verbose == 1
173-
ptf = round(maxIter / 10)
174-
elseif verbose == 2
175-
ptf = round(maxIter / 100)
176-
else
177-
ptf = 1
178-
end
179-
180-
# initialize parameters
181-
xk = copy(x0)
182-
hk = h(xk[selected])
183-
if hk == Inf
184-
verbose > 0 && @info "TR: finding initial guess where nonsmooth term is finite"
185-
prox!(xk, h, x0, one(eltype(x0)))
186-
hk = h(xk[selected])
187-
hk < Inf || error("prox computation must be erroneous")
188-
verbose > 0 && @debug "TR: found point where h has value" hk
189-
end
190-
hk == -Inf && error("nonsmooth term is not proper")
191-
192-
xkn = similar(xk)
193-
s = zero(xk)
194-
ψ =
195-
(has_bounds(f) || subsolver == TRDH) ?
196-
shifted(h, xk, max.(-Δk, l_bound - xk), min.(Δk, u_bound - xk), selected) :
197-
shifted(h, xk, Δk, χ)
198-
199-
Fobj_hist = zeros(maxIter)
200-
Hobj_hist = zeros(maxIter)
201-
Complex_hist = zeros(Int, maxIter)
202-
if verbose > 0
203-
#! format: off
204-
@info @sprintf "%6s %8s %8s %8s %7s %7s %8s %7s %7s %7s %7s %1s" "outer" "inner" "f(x)" "h(x)" "√(ξ1/ν)" "√ξ" "ρ" "Δ" "‖x‖" "‖s‖" "‖Bₖ‖" "TR"
205-
#! format: on
206-
end
207-
208-
local ξ1
209-
k = 0
210-
211-
fk = obj(f, xk)
212-
∇fk = grad(f, xk)
213-
∇fk⁻ = copy(∇fk)
214-
215-
quasiNewtTest = isa(f, QuasiNewtonModel)
216-
Bk = hess_op(f, xk)
217-
218-
λmax, found_λ = opnorm(Bk)
219-
found_λ || error("operator norm computation failed")
220-
α⁻¹Δ⁻¹ = 1 /* Δk)
221-
ν = 1 / (α⁻¹Δ⁻¹ + λmax * (α⁻¹Δ⁻¹ + 1))
222-
sqrt_ξ1_νInv = one(R)
223-
224-
optimal = false
225-
tired = k maxIter || elapsed_time > maxTime
226-
227-
while !(optimal || tired)
228-
k = k + 1
229-
elapsed_time = time() - start_time
230-
Fobj_hist[k] = fk
231-
Hobj_hist[k] = hk
232-
233-
# model for first prox-gradient step and ξ1
234-
φ1(d) = ∇fk' * d
235-
mk1(d) = φ1(d) + ψ(d)
236-
237-
# model for subsequent prox-gradient steps and ξ
238-
φ(d) = (d' * (Bk * d)) / 2 + ∇fk' * d
239-
240-
∇φ!(g, d) = begin
241-
mul!(g, Bk, d)
242-
g .+= ∇fk
243-
g
244-
end
245-
246-
mk(d) = φ(d) + ψ(d)
247-
248-
# Take first proximal gradient step s1 and see if current xk is nearly stationary.
249-
# s1 minimizes φ1(s) + ‖s‖² / 2 / ν + ψ(s) ⟺ s1 ∈ prox{νψ}(-ν∇φ1(0)).
250-
prox!(s, ψ, -ν * ∇fk, ν)
251-
ξ1 = hk - mk1(s) + max(1, abs(hk)) * 10 * eps()
252-
ξ1 > 0 || error("TR: first prox-gradient step should produce a decrease but ξ1 = $(ξ1)")
253-
sqrt_ξ1_νInv = sqrt(ξ1 / ν)
254-
255-
if ξ1 0 && k == 1
256-
ϵ_increment = ϵr * sqrt_ξ1_νInv
257-
ϵ += ϵ_increment # make stopping test absolute and relative
258-
ϵ_subsolver += ϵ_increment
259-
end
260-
261-
if sqrt_ξ1_νInv < ϵ
262-
# the current xk is approximately first-order stationary
263-
optimal = true
264-
continue
265-
end
266-
267-
subsolver_options.ϵa = k == 2 ? 1.0e-5 : max(ϵ_subsolver, min(1e-2, sqrt_ξ1_νInv))
268-
∆_effective = min* χ(s), Δk)
269-
(has_bounds(f) || subsolver == TRDH) ?
270-
set_bounds!(ψ, max.(-∆_effective, l_bound - xk), min.(∆_effective, u_bound - xk)) :
271-
set_radius!(ψ, ∆_effective)
272-
subsolver_options.Δk = ∆_effective / 10
273-
subsolver_options.ν = ν
274-
subsolver_args = subsolver == TRDH ? (SpectralGradient(1 / ν, f.meta.nvar),) : ()
275-
276-
stats = subsolver(φ, ∇φ!, ψ, subsolver_args..., subsolver_options, s)
277-
278-
s = stats.solution
279-
iter = stats.iter
280-
281-
# restore initial values of subsolver_options here so that it is not modified
282-
# if there is an error
283-
subsolver_options.ν = ν_subsolver
284-
subsolver_options.ϵa = ϵa_subsolver
285-
subsolver_options.Δk = Δk_subsolver
286-
287-
Complex_hist[k] = 1
288-
289-
sNorm = χ(s)
290-
xkn .= xk .+ s
291-
fkn = obj(f, xkn)
292-
hkn = h(xkn[selected])
293-
hkn == -Inf && error("nonsmooth term is not proper")
294-
295-
Δobj = fk + hk - (fkn + hkn) + max(1, abs(fk + hk)) * 10 * eps()
296-
ξ = hk - mk(s) + max(1, abs(hk)) * 10 * eps()
297-
298-
if 0 || isnan(ξ))
299-
error("TR: failed to compute a step: ξ = ")
300-
end
301-
302-
ρk = Δobj / ξ
303-
304-
TR_stat = (η2 ρk < Inf) ? "" : (ρk < η1 ? "" : "=")
305-
306-
if (verbose > 0) && (k % ptf == 0)
307-
#! format: off
308-
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8.1e %7.1e %7.1e %7.1e %7.1e %1s" k iter fk hk sqrt_ξ1_νInv sqrt(ξ) ρk ∆_effective χ(xk) sNorm λmax TR_stat
309-
#! format: on
310-
end
311-
312-
if η2 ρk < Inf
313-
Δk = max(Δk, γ * sNorm)
314-
!(has_bounds(f) || subsolver == TRDH) && set_radius!(ψ, Δk)
315-
end
316-
317-
if η1 ρk < Inf
318-
xk .= xkn
319-
(has_bounds(f) || subsolver == TRDH) &&
320-
set_bounds!(ψ, max.(-Δk, l_bound - xk), min.(Δk, u_bound - xk))
321-
322-
#update functions
323-
fk = fkn
324-
hk = hkn
325-
shift!(ψ, xk)
326-
∇fk = grad(f, xk)
327-
# grad!(f, xk, ∇fk)
328-
if quasiNewtTest
329-
push!(f, s, ∇fk - ∇fk⁻)
330-
end
331-
Bk = hess_op(f, xk)
332-
λmax, found_λ = opnorm(Bk)
333-
found_λ || error("operator norm computation failed")
334-
∇fk⁻ .= ∇fk
335-
end
336-
337-
if ρk < η1 || ρk == Inf
338-
Δk = Δk / 2
339-
(has_bounds(f) || subsolver == TRDH) ?
340-
set_bounds!(ψ, max.(-Δk, l_bound - xk), min.(Δk, u_bound - xk)) : set_radius!(ψ, Δk)
341-
end
342-
α⁻¹Δ⁻¹ = 1 /* Δk)
343-
ν = 1 / (α⁻¹Δ⁻¹ + λmax * (α⁻¹Δ⁻¹ + 1))
344-
tired = k maxIter || elapsed_time > maxTime
345-
end
346-
347-
if verbose > 0
348-
if k == 1
349-
@info @sprintf "%6d %8s %8.1e %8.1e" k "" fk hk
350-
elseif optimal
351-
#! format: off
352-
@info @sprintf "%6d %8d %8.1e %8.1e %7.1e %7.1e %8s %7.1e %7.1e %7.1e %7.1e" k 1 fk hk sqrt_ξ1_νInv sqrt(ξ1) "" Δk χ(xk) χ(s) λmax
353-
#! format: on
354-
@info "TR: terminating with √(ξ1/ν) = $(sqrt_ξ1_νInv)"
355-
end
356-
end
357-
358-
status = if optimal
359-
:first_order
360-
elseif elapsed_time > maxTime
361-
:max_time
362-
elseif tired
363-
:max_iter
364-
else
365-
:exception
366-
end
143+
reg_nlp = RegularizedNLPModel(f, h, selected)
144+
stats = TR(
145+
reg_nlp;
146+
x = x0,
147+
atol = options.ϵa,
148+
sub_atol = subsolver_options.ϵa,
149+
rtol = options.ϵr,
150+
neg_tol = options.neg_tol,
151+
verbose = options.verbose,
152+
max_iter = options.maxIter,
153+
max_time = options.maxTime,
154+
Δk = options.Δk,
155+
η1 = options.η1,
156+
η2 = options.η2,
157+
γ = options.γ,
158+
α = options.α,
159+
β = options.β,
160+
kwargs...
161+
)
162+
return stats
163+
end
367164

368-
stats = GenericExecutionStats(f)
369-
set_status!(stats, status)
370-
set_solution!(stats, xk)
371-
set_objective!(stats, fk + hk)
372-
set_residuals!(stats, zero(eltype(xk)), sqrt_ξ1_νInv)
373-
set_iter!(stats, k)
374-
set_time!(stats, elapsed_time)
375-
set_solver_specific!(stats, :radius, Δk)
376-
set_solver_specific!(stats, :Fhist, Fobj_hist[1:k])
377-
set_solver_specific!(stats, :Hhist, Hobj_hist[1:k])
378-
set_solver_specific!(stats, :NonSmooth, h)
379-
set_solver_specific!(stats, :SubsolverCounter, Complex_hist[1:k])
165+
function TR(
166+
reg_nlp::AbstractRegularizedNLPModel{T, V};
167+
kwargs...
168+
) where{T, V}
169+
kwargs_dict = Dict(kwargs...)
170+
subsolver = pop!(kwargs_dict, :subsolver, R2Solver)
171+
χ = pop!(kwargs_dict, , NormLinf(one(T)))
172+
solver = TRSolver(reg_nlp, subsolver = subsolver, χ = χ)
173+
stats = RegularizedExecutionStats(reg_nlp)
174+
solve!(solver, reg_nlp, stats; kwargs_dict...)
380175
return stats
381176
end
382177

@@ -394,7 +189,6 @@ function SolverCore.solve!(
394189
max_iter::Int = 10000,
395190
max_time::Float64 = 30.0,
396191
max_eval::Int = -1,
397-
reduce_TR::Bool = true,
398192
Δk::T = T(1),
399193
η1::T = √√eps(T),
400194
η2::T = T(0.9),

0 commit comments

Comments
 (0)