|
1 | | -export LM |
| 1 | +export LM, LMSolver, solve! |
| 2 | + |
| 3 | +import SolverCore.solve! |
| 4 | + |
| 5 | +mutable struct TRDHSolver # FIXME |
| 6 | +end |
| 7 | + |
| 8 | +mutable struct LMSolver{ |
| 9 | + T <: Real, |
| 10 | + G <: ShiftedProximableFunction, |
| 11 | + V <: AbstractVector{T}, |
| 12 | + M <: AbstractLinearOperator{T}, |
| 13 | + ST <: AbstractOptimizationSolver, |
| 14 | + PB <: AbstractRegularizedNLPModel, |
| 15 | +} <: AbstractOptimizationSolver |
| 16 | + xk::V |
| 17 | + ∇fk::V |
| 18 | + mν∇fk::V |
| 19 | + Fk::V |
| 20 | + Fkn::V |
| 21 | + Jk::M |
| 22 | + ψ::G |
| 23 | + xkn::V |
| 24 | + s::V |
| 25 | + has_bnds::Bool |
| 26 | + l_bound::V |
| 27 | + u_bound::V |
| 28 | + l_bound_m_x::V |
| 29 | + u_bound_m_x::V |
| 30 | + subsolver::ST |
| 31 | + subpb::PB |
| 32 | + substats::GenericExecutionStats{T, V, V, T} |
| 33 | +end |
| 34 | + |
| 35 | +function LMSolver( |
| 36 | + reg_nls::AbstractRegularizedNLPModel{T, V}; |
| 37 | + subsolver = R2Solver, |
| 38 | +) where{T, V} |
| 39 | + x0 = reg_nls.model.meta.x0 |
| 40 | + l_bound = reg_nls.model.meta.lvar |
| 41 | + u_bound = reg_nls.model.meta.uvar |
| 42 | + |
| 43 | + xk = similar(x0) |
| 44 | + ∇fk = similar(x0) |
| 45 | + mν∇fk = similar(x0) |
| 46 | + Fk = similar(x0, reg_nls.model.nls_meta.nequ) |
| 47 | + Fkn = similar(Fk) |
| 48 | + Jk = jac_op_residual(reg_nls.model, xk) |
| 49 | + xkn = similar(x0) |
| 50 | + s = similar(x0) |
| 51 | + has_bnds = any(l_bound .!= T(-Inf)) || any(u_bound .!= T(Inf)) || subsolver == TRDHSolver |
| 52 | + if has_bnds |
| 53 | + l_bound_m_x = similar(xk) |
| 54 | + u_bound_m_x = similar(xk) |
| 55 | + @. l_bound_m_x = l_bound - x0 |
| 56 | + @. u_bound_m_x = u_bound - x0 |
| 57 | + else |
| 58 | + l_bound_m_x = similar(xk, 0) |
| 59 | + u_bound_m_x = similar(xk, 0) |
| 60 | + end |
| 61 | + |
| 62 | + ψ = |
| 63 | + has_bnds ? shifted(reg_nls.h, xk, l_bound_m_x, u_bound_m_x, reg_nls.selected) : |
| 64 | + shifted(reg_nls.h, xk) |
| 65 | + |
| 66 | + sub_nlp = LMModel(Jk, Fk, T(1), x0) |
| 67 | + subpb = RegularizedNLPModel(sub_nlp, ψ) |
| 68 | + substats = RegularizedExecutionStats(subpb) |
| 69 | + subsolver = subsolver(subpb) |
| 70 | + |
| 71 | + return LMSolver( |
| 72 | + xk, |
| 73 | + ∇fk, |
| 74 | + mν∇fk, |
| 75 | + Fk, |
| 76 | + Fkn, |
| 77 | + Jk, |
| 78 | + ψ, |
| 79 | + xkn, |
| 80 | + s, |
| 81 | + has_bnds, |
| 82 | + l_bound, |
| 83 | + u_bound, |
| 84 | + l_bound_m_x, |
| 85 | + u_bound_m_x, |
| 86 | + subsolver, |
| 87 | + subpb, |
| 88 | + substats |
| 89 | + ) |
| 90 | +end |
| 91 | + |
| 92 | +function SolverCore.solve!( |
| 93 | + solver::LMSolver{T, G, V}, |
| 94 | + reg_nls::AbstractRegularizedNLPModel{T, V}, |
| 95 | + stats::GenericExecutionStats{T, V}; |
| 96 | + callback = (args...) -> nothing, |
| 97 | + x::V = reg_nls.model.meta.x0, |
| 98 | + atol::T = √eps(T), |
| 99 | + rtol::T = √eps(T), |
| 100 | + verbose::Int = 0, |
| 101 | + max_iter::Int = 10000, |
| 102 | + max_time::Float64 = 30.0, |
| 103 | + max_eval::Int = -1, |
| 104 | + σk::T = eps(T)^(1 / 5), |
| 105 | + σmin::T = eps(T), |
| 106 | + η1::T = √√eps(T), |
| 107 | + η2::T = T(0.9), |
| 108 | + γ::T = T(3), |
| 109 | + θ::T = 1/(1 + eps(T)^(1 / 5)), |
| 110 | +) where {T, V, G} |
| 111 | + reset!(stats) |
| 112 | + |
| 113 | + # Retrieve workspace |
| 114 | + selected = reg_nls.selected |
| 115 | + h = reg_nls.h |
| 116 | + nls = reg_nls.model |
| 117 | + |
| 118 | + xk = solver.xk .= x |
| 119 | + |
| 120 | + # Make sure ψ has the correct shift |
| 121 | + shift!(solver.ψ, xk) |
| 122 | + |
| 123 | + Fk = solver.Fk |
| 124 | + Fkn = solver.Fkn |
| 125 | + Jk = solver.Jk |
| 126 | + ∇fk = solver.∇fk |
| 127 | + JdFk = solver.JdFk |
| 128 | + Jt_Fk = solver.Jt_Fk |
| 129 | + ψ = solver.ψ |
| 130 | + xkn = solver.xkn |
| 131 | + s = solver.s |
| 132 | + |
| 133 | + has_bnds = solver.has_bnds |
| 134 | + if has_bnds |
| 135 | + l_bound = solver.l_bound |
| 136 | + u_bound = solver.u_bound |
| 137 | + l_bound_m_x = solver.l_bound_m_x |
| 138 | + u_bound_m_x = solver.u_bound_m_x |
| 139 | + end |
| 140 | + |
| 141 | + # initialize parameters |
| 142 | + improper = false |
| 143 | + hk = @views h(xk[selected]) |
| 144 | + if hk == Inf |
| 145 | + verbose > 0 && @info "LM: finding initial guess where nonsmooth term is finite" |
| 146 | + prox!(xk, h, xk, one(eltype(x0))) |
| 147 | + hk = @views h(xk[selected]) |
| 148 | + hk < Inf || error("prox computation must be erroneous") |
| 149 | + verbose > 0 && @debug "LM: found point where h has value" hk |
| 150 | + end |
| 151 | + improper = (hk == -Inf) |
| 152 | + improper == true && @warn "LM: Improper term detected" |
| 153 | + improper == true && return stats |
| 154 | + |
| 155 | + if verbose > 0 |
| 156 | + @info log_header( |
| 157 | + [:outer, :inner, :fx, :hx, :xi, :ρ, :σ, :normx, :norms, :normJ, :arrow], |
| 158 | + [Int, Int, T, T, T, T, T, T, T, T, Char], |
| 159 | + hdr_override = Dict{Symbol, String}( |
| 160 | + :fx => "f(x)", |
| 161 | + :hx => "h(x)", |
| 162 | + :xi => "√(ξ1/ν)", |
| 163 | + :normx => "‖x‖", |
| 164 | + :norms => "‖s‖", |
| 165 | + :normB => "‖J‖²", |
| 166 | + :arrow => "R2N", |
| 167 | + ), |
| 168 | + colsep = 1, |
| 169 | + ) |
| 170 | + end |
| 171 | + |
| 172 | + local ξ1::T |
| 173 | + local ρk::T = zero(T) |
| 174 | + |
| 175 | + residual!(nls, xk, Fk) |
| 176 | + Jk = jac_op_residual(nls, xk) |
| 177 | + mul!(∇fk, Jk', Fk) |
| 178 | + fk = dot(Fk, Fk) / 2 |
| 179 | + |
| 180 | + σmax, found_σ = opnorm(Jk) |
| 181 | + found_σ || error("operator norm computation failed") |
| 182 | + ν = θ / (σmax^2 + σk) # ‖J'J + σₖ I‖ = ‖J‖² + σₖ |
| 183 | + sqrt_ξ1_νInv = one(T) |
| 184 | + |
| 185 | + @. mν∇fk = -ν * ∇fk |
| 186 | + |
| 187 | + φ1(d) = let Fk = Fk, Jk = Jk, |
| 188 | + d -> dot(Fk, Fk) / 2 |
| 189 | + end |
| 190 | + |
| 191 | + return |
| 192 | +end |
2 | 193 |
|
3 | 194 | """ |
4 | 195 | LM(nls, h, options; kwargs...) |
@@ -143,7 +334,7 @@ function LM( |
143 | 334 | Resid_hist[k] = nls.counters.neval_residual |
144 | 335 |
|
145 | 336 | # model for first prox-gradient iteration |
146 | | - φ1(d) = begin |
| 337 | + φ1(d) = begin # || Fk ||^2/2 + d*Jk'*Fk |
147 | 338 | jtprod_residual!(nls, xk, Fk, Jt_Fk) |
148 | 339 | dot(Fk, Fk) / 2 + dot(Jt_Fk, d) |
149 | 340 | end |
|
0 commit comments