|
1 | | -export FirstOrderModel, FirstOrderNLSModel |
| 1 | +export FirstOrderModel, |
| 2 | + FirstOrderNLSModel, AbstractRegularizedNLPModel, RegularizedNLPModel, RegularizedNLSModel |
2 | 3 |
|
3 | 4 | """ |
4 | 5 | model = FirstOrderModel(f, ∇f!; name = "first-order model") |
@@ -131,3 +132,98 @@ function NLPModels.jtprod_residual!( |
131 | 132 | nls.jtprod_resid!(Jtv, x, v) |
132 | 133 | Jtv |
133 | 134 | end |
| 135 | + |
| 136 | +abstract type AbstractRegularizedNLPModel{T, S} <: AbstractNLPModel{T, S} end |
| 137 | + |
| 138 | +""" |
| 139 | + rmodel = RegularizedNLPModel(model, regularizer) |
| 140 | + rmodel = RegularizedNLSModel(model, regularizer) |
| 141 | +
|
| 142 | +An aggregate type to represent a regularized optimization model, .i.e., |
| 143 | +of the form |
| 144 | +
|
| 145 | + minimize f(x) + h(x), |
| 146 | +
|
| 147 | +where f is smooth (and is usually assumed to have Lipschitz-continuous gradient), |
| 148 | +and h is lower semi-continuous (and may have to be prox-bounded). |
| 149 | +
|
| 150 | +The regularized model is made of |
| 151 | +
|
| 152 | +- `model <: AbstractNLPModel`: the smooth part of the model, for example a `FirstOrderModel` |
| 153 | +- `h`: the nonsmooth part of the model; typically a regularizer defined in `ProximalOperators.jl` |
| 154 | +- `selected`: the subset of variables to which the regularizer h should be applied (default: all). |
| 155 | +
|
| 156 | +This aggregate type can be used to call solvers with a single object representing the |
| 157 | +model, but is especially useful for use with SolverBenchmark.jl, which expects problems |
| 158 | +to be defined by a single object. |
| 159 | +""" |
| 160 | +mutable struct RegularizedNLPModel{T, S, M <: AbstractNLPModel{T, S}, H, I} <: |
| 161 | + AbstractRegularizedNLPModel{T, S} |
| 162 | + model::M # smooth model |
| 163 | + h::H # regularizer |
| 164 | + selected::I # set of variables to which the regularizer should be applied |
| 165 | +end |
| 166 | + |
| 167 | +function RegularizedNLPModel(model::AbstractNLPModel{T, S}, h::H) where {T, S, H} |
| 168 | + selected = 1:get_nvar(model) |
| 169 | + RegularizedNLPModel{T, S, typeof(model), typeof(h), typeof(selected)}(model, h, selected) |
| 170 | +end |
| 171 | + |
| 172 | +mutable struct RegularizedNLSModel{T, S, M <: AbstractNLSModel{T, S}, H, I} <: |
| 173 | + AbstractRegularizedNLPModel{T, S} |
| 174 | + model::M # smooth model |
| 175 | + h::H # regularizer |
| 176 | + selected::I # set of variables to which the regularizer should be applied |
| 177 | +end |
| 178 | + |
| 179 | +function RegularizedNLSModel(model::AbstractNLSModel{T, S}, h::H) where {T, S, H} |
| 180 | + selected = 1:get_nvar(model) |
| 181 | + RegularizedNLSModel{T, S, typeof(model), typeof(h), typeof(selected)}(model, h, selected) |
| 182 | +end |
| 183 | + |
| 184 | +function NLPModels.obj(rnlp::AbstractRegularizedNLPModel, x::AbstractVector) |
| 185 | + # The size check on x will be performed when evaluating the smooth model. |
| 186 | + # We intentionally do not increment an objective evaluation counter here |
| 187 | + # because the relevant counters are inside the smooth term. |
| 188 | + obj(rnlp.model, x) + rnlp.h(x) |
| 189 | +end |
| 190 | + |
| 191 | +# Forward meta getters so they grab info from the smooth model |
| 192 | +for field ∈ fieldnames(NLPModels.NLPModelMeta) |
| 193 | + meth = Symbol("get_", field) |
| 194 | + if field == :name |
| 195 | + @eval NLPModels.$meth(rnlp::RegularizedNLPModel) = |
| 196 | + NLPModels.$meth(rnlp.model) * "/" * string(typeof(rnlp.h).name.wrapper) |
| 197 | + @eval NLPModels.$meth(rnls::RegularizedNLSModel) = |
| 198 | + NLPModels.$meth(rnls.model) * "/" * string(typeof(rnls.h).name.wrapper) |
| 199 | + else |
| 200 | + @eval NLPModels.$meth(rnlp::RegularizedNLPModel) = NLPModels.$meth(rnlp.model) |
| 201 | + end |
| 202 | +end |
| 203 | + |
| 204 | +for field in fieldnames(NLPModels.NLSMeta) |
| 205 | + meth = Symbol("get_", field) |
| 206 | + @eval NLPModels.$meth(rnls::RegularizedNLSModel) = NLPModels.$meth(rnls.model) |
| 207 | +end |
| 208 | + |
| 209 | +# Forward counter getters so they grab info from the smooth model |
| 210 | +for model_type ∈ (RegularizedNLPModel, RegularizedNLSModel) |
| 211 | + for counter in fieldnames(Counters) |
| 212 | + @eval NLPModels.$counter(rnlp::$model_type) = NLPModels.$counter(rnlp.model) |
| 213 | + end |
| 214 | +end |
| 215 | + |
| 216 | +for counter in fieldnames(NLSCounters) |
| 217 | + counter == :counters && continue |
| 218 | + @eval NLPModels.$counter(rnls::RegularizedNLSModel) = NLPModels.$counter(rnls.model) |
| 219 | +end |
| 220 | + |
| 221 | +# simple show method for now |
| 222 | +function Base.show(io::IO, rnlp::AbstractRegularizedNLPModel) |
| 223 | + print(io, "Smooth model: ") |
| 224 | + show(io, rnlp.model) |
| 225 | + print(io, "\nRegularizer: ") |
| 226 | + show(io, rnlp.h) |
| 227 | + print(io, "\n\nSelected variables: ") |
| 228 | + show(io, rnlp.selected) |
| 229 | +end |
0 commit comments