Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
[compat]
FastClosures = "0.2.1, 0.3.0"
LinearOperators = "1.1.0"
NLPModels = "0.14"
NLPModels = "0.15"
julia = "^1.3.0"

[extras]
Expand Down
49 changes: 35 additions & 14 deletions src/feasibility-form-nls.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ to
If you rather have the first problem, the `nls` model already works as an NLPModel of
that format.
"""
mutable struct FeasibilityFormNLS{M <: AbstractNLSModel} <: AbstractNLSModel
meta::NLPModelMeta
nls_meta::NLSMeta
mutable struct FeasibilityFormNLS{T, S, M <: AbstractNLSModel{T, S}} <: AbstractNLSModel{T, S}
meta::NLPModelMeta{T, S}
nls_meta::NLSMeta{T, S}
internal::M
counters::NLSCounters
end
Expand All @@ -39,38 +39,59 @@ end
Converts a nonlinear least-squares problem with residual `F(x)` to a nonlinear
optimization problem with constraints `F(x) = r` and objective `¹/₂‖r‖²`.
"""
function FeasibilityFormNLS(nls::AbstractNLSModel; name = "$(nls.meta.name)-ffnls")
function FeasibilityFormNLS(
nls::AbstractNLSModel{T, S};
name = "$(nls.meta.name)-ffnls",
) where {T,S}
nequ = nls.nls_meta.nequ
meta = nls.meta
nvar = meta.nvar + nequ
ncon = meta.ncon + nequ
nnzh = nls.nls_meta.nnzh + nequ + (meta.ncon == 0 ? 0 : meta.nnzh) # Some indexes can be repeated
meta = NLPModelMeta(
x0 = similar(meta.x0, nvar)
x0[1 : meta.nvar] .= meta.x0
x0[meta.nvar + 1 : end] .= zero(T)
lvar = similar(meta.x0, nvar)
lvar[1 : meta.nvar] .= meta.lvar
lvar[meta.nvar + 1 : end] .= T(-Inf)
uvar = similar(meta.x0, nvar)
uvar[1 : meta.nvar] .= meta.uvar
uvar[meta.nvar + 1 : end] .= T(Inf)
lcon = similar(meta.y0, ncon)
lcon[1 : nequ] .= zero(T)
lcon[nequ + 1 : end] .= meta.lcon
ucon = similar(meta.y0, ncon)
ucon[1 : nequ] .= zero(T)
ucon[nequ + 1 : end] .= meta.ucon
y0 = similar(meta.y0, ncon)
y0[1 : nequ] .= zero(T)
y0[nequ + 1 : end] .= meta.y0
meta = NLPModelMeta{T, S}(
nvar,
x0 = [meta.x0; zeros(nequ)],
lvar = [meta.lvar; fill(-Inf, nequ)],
uvar = [meta.uvar; fill(Inf, nequ)],
x0 = x0,
lvar = lvar,
uvar = uvar,
ncon = ncon,
lcon = [zeros(nequ); meta.lcon],
ucon = [zeros(nequ); meta.ucon],
y0 = [zeros(nequ); meta.y0],
lcon = lcon,
ucon = ucon,
y0 = y0,
lin = [nls.nls_meta.lin; meta.lin .+ nequ],
nln = [nls.nls_meta.nln; meta.nln .+ nequ],
nnzj = meta.nnzj + nls.nls_meta.nnzj + nequ,
nnzh = nnzh,
name = name,
)
nls_meta = NLSMeta(
nls_meta = NLSMeta{T, S}(
nequ,
nvar,
x0 = [meta.x0; zeros(nequ)],
x0 = x0,
nnzj = nequ,
nnzh = 0,
lin = 1:nequ,
nln = Int[],
)

nlp = FeasibilityFormNLS{typeof(nls)}(meta, nls_meta, nls, NLSCounters())
nlp = FeasibilityFormNLS{T, S, typeof(nls)}(meta, nls_meta, nls, NLSCounters())
finalizer(nlp -> finalize(nlp.internal), nlp)

return nlp
Expand Down
17 changes: 10 additions & 7 deletions src/feasibility-residual.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ The slack variables are created using SlackModel.
If ``\\ell_i = u_i``, no slack variable is created.
In particular, if there are only equality constrained of the form ``c(x) = 0``, the resulting NLS is simply ``\\min_x \\tfrac{1}{2}\\|c(x)\\|^2``.
"""
mutable struct FeasibilityResidual <: AbstractNLSModel
meta::NLPModelMeta
nls_meta::NLSMeta
mutable struct FeasibilityResidual{T, S} <: AbstractNLSModel{T, S}
meta::NLPModelMeta{T, S}
nls_meta::NLSMeta{T, S}
counters::NLSCounters
nlp::AbstractNLPModel
nlp::AbstractNLPModel{T, S}
end

function NLPModels.show_header(io::IO, nls::FeasibilityResidual)
Expand All @@ -39,7 +39,10 @@ function NLPModels.show_header(io::IO, nls::FeasibilityResidual)
)
end

function FeasibilityResidual(nlp::AbstractNLPModel; name = "$(nlp.meta.name)-feasres")
function FeasibilityResidual(
nlp::AbstractNLPModel{T, S};
name = "$(nlp.meta.name)-feasres",
) where {T, S}
if !equality_constrained(nlp)
if unconstrained(nlp)
throw(ErrorException("Can't handle unconstrained problem"))
Expand All @@ -52,15 +55,15 @@ function FeasibilityResidual(nlp::AbstractNLPModel; name = "$(nlp.meta.name)-fea

m, n = nlp.meta.ncon, nlp.meta.nvar
# TODO: What is copied?
meta = NLPModelMeta(
meta = NLPModelMeta{T, S}(
n,
x0 = nlp.meta.x0,
name = name,
lvar = nlp.meta.lvar,
uvar = nlp.meta.uvar,
nnzj = 0,
)
nls_meta = NLSMeta(
nls_meta = NLSMeta{T, S}(
m,
n,
nnzj = nlp.meta.nnzj,
Expand Down
26 changes: 19 additions & 7 deletions src/model-interaction.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
function FeasibilityFormNLS(nls::FeasibilityResidual; name = "$(nls.meta.name)-ffnls")
function FeasibilityFormNLS(
nls::FeasibilityResidual{T, S};
name = "$(nls.meta.name)-ffnls",
) where {T, S}
meta = nls.nlp.meta
nequ = meta.ncon
nvar = meta.nvar + nequ
ncon = meta.ncon
nnzj = meta.nnzj + nequ
nnzh = meta.nnzh + nequ
meta = NLPModelMeta(
x0 = similar(meta.x0, nvar)
x0[1 : meta.nvar] .= meta.x0
x0[meta.nvar : end] .= zero(T)
lvar = similar(meta.x0, nvar)
lvar[1 : meta.nvar] .= meta.lvar
lvar[meta.nvar + 1 : end] .= T(-Inf)
uvar = similar(meta.x0, nvar)
uvar[1 : meta.nvar] .= meta.uvar
uvar[meta.nvar + 1 : end] .= T(Inf)
meta = NLPModelMeta{T, S}(
nvar,
x0 = [meta.x0; zeros(nequ)],
lvar = [meta.lvar; fill(-Inf, nequ)],
uvar = [meta.uvar; fill(Inf, nequ)],
x0 = x0,
lvar = lvar,
uvar = uvar,
ncon = ncon,
lcon = meta.lcon,
ucon = meta.ucon,
Expand All @@ -20,9 +32,9 @@ function FeasibilityFormNLS(nls::FeasibilityResidual; name = "$(nls.meta.name)-f
nnzh = nnzh,
name = name,
)
nls_meta = NLSMeta(nequ, nvar, x0 = [meta.x0; zeros(nequ)], nnzj = nequ, nnzh = 0)
nls_meta = NLSMeta{T, S}(nequ, nvar, x0 = x0, nnzj = nequ, nnzh = 0)

nlp = FeasibilityFormNLS{FeasibilityResidual}(meta, nls_meta, nls, NLSCounters())
nlp = FeasibilityFormNLS{T, S, FeasibilityResidual{T, S}}(meta, nls_meta, nls, NLSCounters())
finalizer(nlp -> finalize(nlp.internal), nlp)

return nlp
Expand Down
14 changes: 7 additions & 7 deletions src/quasi-newton.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
export QuasiNewtonModel, LBFGSModel, LSR1Model

abstract type QuasiNewtonModel <: AbstractNLPModel end
abstract type QuasiNewtonModel{T, S} <: AbstractNLPModel{T, S} end

mutable struct LBFGSModel <: QuasiNewtonModel
meta::NLPModelMeta
model::AbstractNLPModel
mutable struct LBFGSModel{T, S} <: QuasiNewtonModel{T, S}
meta::NLPModelMeta{T, S}
model::AbstractNLPModel{T, S}
op::LBFGSOperator
end

mutable struct LSR1Model <: QuasiNewtonModel
meta::NLPModelMeta
model::AbstractNLPModel
mutable struct LSR1Model{T, S} <: QuasiNewtonModel{T, S}
meta::NLPModelMeta{T, S}
model::AbstractNLPModel{T, S}
op::LSR1Operator
end

Expand Down
40 changes: 25 additions & 15 deletions src/slack-model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ The slack variables are implicitly ordered as `[s(low), s(upp), s(rng)]`, where
``c_L ≤ c(x) < ∞``, ``-∞ < c(x) ≤ c_U`` and
``c_L ≤ c(x) ≤ c_U``, respectively.
"""
mutable struct SlackModel <: AbstractNLPModel
meta::NLPModelMeta
model::AbstractNLPModel
mutable struct SlackModel{T, S} <: AbstractNLPModel{T, S}
meta::NLPModelMeta{T, S}
model::AbstractNLPModel{T, S}
end

NLPModels.show_header(io::IO, nlp::SlackModel) =
Expand All @@ -60,10 +60,10 @@ end

"""Like `SlackModel`, this model converts inequalities into equalities and bounds.
"""
mutable struct SlackNLSModel <: AbstractNLSModel
meta::NLPModelMeta
nls_meta::NLSMeta
model::AbstractNLPModel
mutable struct SlackNLSModel{T, S} <: AbstractNLSModel{T, S}
meta::NLPModelMeta{T, S}
nls_meta::NLSMeta{T, S}
model::AbstractNLPModel{T, S}
end

NLPModels.show_header(io::IO, nls::SlackNLSModel) =
Expand All @@ -75,24 +75,28 @@ function Base.show(io::IO, nls::SlackNLSModel)
show(io, nls.model.counters)
end

function slack_meta(meta::NLPModelMeta; name = meta.name * "-slack")
function slack_meta(meta::NLPModelMeta{T, S}; name = meta.name * "-slack") where {T, S}
ns = meta.ncon - length(meta.jfix)
jlow = meta.jlow
jupp = meta.jupp
jrng = meta.jrng
T = eltype(meta.x0)

# Don't introduce slacks for equality constraints!
lvar = [meta.lvar; meta.lcon[[jlow; jupp; jrng]]] # l ≤ x and cₗ ≤ s
uvar = [meta.uvar; meta.ucon[[jlow; jupp; jrng]]] # x ≤ u and s ≤ cᵤ
lcon = zeros(T, meta.ncon)
lcon = similar(meta.x0, meta.ncon)
lcon[setdiff(1:meta.ncon, meta.jfix)] .= zero(T)
lcon[meta.jfix] = meta.lcon[meta.jfix]
ucon = zeros(T, meta.ncon)
ucon = similar(meta.x0, meta.ncon)
ucon[setdiff(1:meta.ncon, meta.jfix)] .= zero(T)
ucon[meta.jfix] = meta.ucon[meta.jfix]

x0 = similar(meta.x0, meta.nvar + ns)
x0[1 : meta.nvar] .= meta.x0
x0[meta.nvar : end] .= zero(T)
return NLPModelMeta(
meta.nvar + ns,
x0 = [meta.x0; zeros(T, ns)],
x0 = x0,
lvar = lvar,
uvar = uvar,
ncon = meta.ncon,
Expand All @@ -119,15 +123,21 @@ function SlackModel(model::AbstractNLPModel; name = model.meta.name * "-slack")
return snlp
end

function SlackNLSModel(model::AbstractNLSModel; name = model.meta.name * "-slack")
function SlackNLSModel(
model::AbstractNLSModel{T, S};
name = model.meta.name * "-slack",
) where {T, S}
ns = model.meta.ncon - length(model.meta.jfix)
ns == 0 && return model

meta = slack_meta(model.meta, name = name)
nls_meta = NLSMeta(
x0 = similar(model.meta.x0, model.meta.nvar + ns)
x0[model.meta.nvar : end] .= zero(T)
x0[1 : model.meta.nvar] .= model.meta.x0
nls_meta = NLSMeta{T, S}(
model.nls_meta.nequ,
model.meta.nvar + ns,
x0 = [model.meta.x0; zeros(eltype(model.meta.x0), ns)],
x0 = x0,
nnzj = model.nls_meta.nnzj,
nnzh = model.nls_meta.nnzh,
lin = model.nls_meta.lin,
Expand Down
11 changes: 7 additions & 4 deletions test/nlp/quasi-newton.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
c(x) = [x[1] - 2x[2] + 1; -x[1]^2 / 4 - x[2]^2 + 1]
J(x) = [1.0 -2.0; -0.5x[1] -2.0x[2]]

for (QNM, QNO) in [(LSR1Model, LSR1Operator), (LBFGSModel, LBFGSOperator)]
nlp = QNM(SimpleNLPModel())
for (QNM, QNO) in [(LSR1Model, LSR1Operator), (LBFGSModel, LBFGSOperator)],
T in [Float64, Float32]
nlp = QNM(SimpleNLPModel(T))
n = nlp.meta.nvar
m = nlp.meta.ncon

Expand Down Expand Up @@ -77,7 +78,8 @@
io = IOBuffer()
show(io, nlp)
showed = String(take!(io))
expected = """LSR1Model - A QuasiNewtonModel
storage_type = typeof(nlp)
expected = """$storage_type - A QuasiNewtonModel
Problem name: Simple NLP Model
All variables: ████████████████████ 2 All constraints: ████████████████████ 2
free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
Expand All @@ -102,7 +104,8 @@
io = IOBuffer()
show(io, nlp)
showed = String(take!(io))
expected = """LBFGSModel - A QuasiNewtonModel
storage_type = typeof(nlp)
expected = """$storage_type - A QuasiNewtonModel
Problem name: Simple NLP Model
All variables: ████████████████████ 2 All constraints: ████████████████████ 2
free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0 free: ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ 0
Expand Down
19 changes: 10 additions & 9 deletions test/nlp/simple-model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,27 @@ Modified problem 14 in the Hock-Schittkowski suite

x₀ = [2.0, 2.0].
"""
mutable struct SimpleNLPModel <: AbstractNLPModel
meta::NLPModelMeta
mutable struct SimpleNLPModel{T, S} <: AbstractNLPModel{T, S}
meta::NLPModelMeta{T, S}
counters::Counters
end

function SimpleNLPModel()
meta = NLPModelMeta(
function SimpleNLPModel(::Type{T}) where {T}
meta = NLPModelMeta{T, Vector{T}}(
2,
nnzh = 2,
ncon = 2,
lvar = zeros(2),
uvar = ones(2),
x0 = [2.0; 2.0],
lcon = [0.0; 0.0],
ucon = [0.0; Inf],
lvar = zeros(T, 2),
uvar = ones(T, 2),
x0 = T[2; 2],
lcon = T[0; 0],
ucon = T[0; Inf],
name = "Simple NLP Model",
)

return SimpleNLPModel(meta, Counters())
end
SimpleNLPModel() = SimpleNLPModel(Float64)

function NLPModels.obj(nlp::SimpleNLPModel, x::AbstractVector)
@lencheck 2 x
Expand Down
5 changes: 3 additions & 2 deletions test/nlp/slack-model.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
@testset "SlackModel NLP tests" begin
@testset "API" begin
@testset "API" for T in [Float64, Float32]
f(x) = (x[1] - 2)^2 + (x[2] - 1)^2
∇f(x) = [2 * (x[1] - 2); 2 * (x[2] - 1); 0]
H(x) = [2.0 0 0; 0 2.0 0; 0 0 0]
c(x) = [x[1] - 2x[2] + 1; -x[1]^2 / 4 - x[2]^2 + 1 - x[3]]
J(x) = [1.0 -2.0 0; -0.5x[1] -2.0x[2] -1]
H(x, y) = H(x) + y[2] * [-0.5 0 0; 0 -2.0 0; 0 0 0]

nlp = SlackModel(SimpleNLPModel())

nlp = SlackModel(SimpleNLPModel(T))
n = nlp.meta.nvar
m = nlp.meta.ncon

Expand Down
Loading