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 test/nlp_consistency.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
for problem in nlp_problems
for problem in Symbol.(lowercase.(nlp_problems ∪ extra_nlp_oracle_problems))
@testset "Problem $problem" begin
nlp_manual = eval(Symbol(problem))()
problem_f = eval(Symbol(lowercase(problem)))
Expand Down
47 changes: 47 additions & 0 deletions test/nlp_problems/hs10.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,50 @@ function hs10()

return nlp
end

"Problem 10 in the Hock-Schittkowski suite, but the nonlinear constraint is a VectorNonlinearOracle"
function hs10_oracle()
model = Model()

@variable(model, x[1:2])
set_start_value(x[1], -10)
set_start_value(x[2], 10)

@objective(model, Min, x[1] - x[2])

# g(x) = -3 x1^2 + 2 x1 x2 - x2^2 + 1 ≥ 0
# Bounds: 0 ≤ g(x) ≤ +∞
set = MOI.VectorNonlinearOracle(;
dimension = 2, # number of input variables (x1, x2)
l = [-1.0], # lower bound on g(x)
u = [Inf], # upper bound on g(x)
eval_f = (ret, xv) -> begin
# ret[1] = g(x)
ret[1] = -3 * xv[1]^2 + 2 * xv[1] * xv[2] - xv[2]^2
end,
# Jacobian of g(x): ∇g = [∂g/∂x1, ∂g/∂x2]
# ∂g/∂x1 = -6 x1 + 2 x2
# ∂g/∂x2 = 2 x1 - 2 x2
jacobian_structure = [(1, 1), (1, 2)], # (row, col) for each entry of ret
eval_jacobian = (ret, xv) -> begin
ret[1] = -6 * xv[1] + 2 * xv[2] # d g / d x1
ret[2] = 2 * xv[1] - 2 * xv[2] # d g / d x2
end,
# Hessian of g(x) (constant):
# ∂²g/∂x1² = -6
# ∂²g/∂x1∂x2 = ∂²g/∂x2∂x1 = 2
# ∂²g/∂x2² = -2
hessian_lagrangian_structure = [(1, 1), (1, 2), (2, 2)],
eval_hessian_lagrangian = (ret, xv, μ) -> begin
# Hessian of μ[1] * g(x)
ret[1] = μ[1] * (-6.0) # (1,1)
ret[2] = μ[1] * ( 2.0) # (1,2)
ret[3] = μ[1] * (-2.0) # (2,2)
end,
)

# Same constraint, but expressed as a VectorNonlinearOracle on [x[1], x[2]]
@constraint(model, c, [x[1], x[2]] in set)

return model
end
53 changes: 53 additions & 0 deletions test/nlp_problems/hs14.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,56 @@ function hs14()

return nlp
end

"Problem 14 in the Hock–Schittkowski suite, with inequality as a VectorNonlinearOracle"
function hs14_oracle()
model = Model()

@variable(model, x[1:2])
set_start_value(x[1], 2)
set_start_value(x[2], 2)

@objective(model, Min, (x[1] - 2)^2 + (x[2] - 1)^2)

# Inequality: -x1^2/4 - x2^2 + 1 ≥ 0
# g(x) = -x1^2/4 - x2^2 + 1, 0 ≤ g(x) ≤ +∞
set = MOI.VectorNonlinearOracle(;
dimension = 2, # 2 inputs: x1, x2
l = [-1.0], # lower bound on g(x)
u = [Inf], # upper bound on g(x)
eval_f = (ret, xv) -> begin
# ret[1] = g(x)
ret[1] = -0.25 * xv[1]^2 - xv[2]^2
end,
# Jacobian of g(x): ∇g = [∂g/∂x1, ∂g/∂x2]
# ∂g/∂x1 = -0.5 x1
# ∂g/∂x2 = -2 x2
jacobian_structure = [(1, 1), (1, 2)],
eval_jacobian = (ret, xv) -> begin
ret[1] = -0.5 * xv[1] # d g / d x1
ret[2] = -2.0 * xv[2] # d g / d x2
end,
# Hessian of g(x):
# ∂²g/∂x1² = -0.5
# ∂²g/∂x1∂x2 = 0
# ∂²g/∂x2² = -2
#
# We store only upper-triangular entries: (1,1), (1,2), (2,2)
hessian_lagrangian_structure = [(1, 1), (1, 2), (2, 2)],
eval_hessian_lagrangian = (ret, xv, μ) -> begin
# Hessian of μ[1] * g(x)
ret[1] = μ[1] * (-0.5) # (1,1)
ret[2] = μ[1] * 0.0 # (1,2)
ret[3] = μ[1] * (-2.0) # (2,2)
end,
)

# Inequality as oracle
@constraint(model, [x[1], x[2]] in set)

# Equality stays as a standard nonlinear constraint:
# x1 - 2 x2 + 1 == 0
@constraint(model, x[1] - 2 * x[2] + 1 == 0)

return model
end
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ nls_problems = NLPModelsTest.nls_problems
extra_nlp_problems = ["nohesspb", "hs61", "hs100", "hs219", "quadcon", "operatorspb", "nf"]
extra_nls_problems = ["nlsnohesspb", "HS30", "HS43", "MGH07", "nlsqc"]

extra_nlp_oracle_problems = ["hs10_oracle", "hs14_oracle"]

for problem in lowercase.(nlp_problems ∪ extra_nlp_problems)
include(joinpath("nlp_problems", "$problem.jl"))
end
Expand All @@ -19,6 +21,8 @@ end
include("test_moi_nlp_model.jl")
include("test_moi_nls_model.jl")

include("test_moi_nlp_oracle.jl")

include("nlp_consistency.jl")
include("nls_consistency.jl")

Expand Down
2 changes: 1 addition & 1 deletion test/test_moi_nlp_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ println("Testing MathOptNLPModel")
"‖c(x₀)‖"
)
# Test that every problem can be instantiated.
for prob in Symbol.(lowercase.(nlp_problems ∪ extra_nlp_problems))
for prob in Symbol.(lowercase.(nlp_problems ∪ extra_nlp_problems ∪ extra_nlp_oracle_problems))
prob_fn = eval(prob)
nlp = MathOptNLPModel(prob_fn(), hessian = (prob != :nohesspb))
n = nlp.meta.nvar
Expand Down
21 changes: 21 additions & 0 deletions test/test_moi_nlp_oracle.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@testset "Testing Oracle vs Non-Oracle NLP Models: $prob" for prob in extra_nlp_oracle_problems
prob_no_oracle = replace(prob, "_oracle" => "")
prob_fn_no_oracle = eval(Symbol(prob_no_oracle))
prob_fn = eval(Symbol(prob))
nlp_no_oracle = MathOptNLPModel(prob_fn_no_oracle(), hessian = true)
nlp_with_oracle = MathOptNLPModel(prob_fn(), hessian = true)
n = nlp_no_oracle.meta.nvar
m = nlp_no_oracle.meta.ncon
x = nlp_no_oracle.meta.x0
fx_no_oracle = obj(nlp_no_oracle, x)
fx_with_oracle = obj(nlp_with_oracle, x)
@test isapprox(fx_no_oracle, fx_with_oracle; atol = 1e-8, rtol = 1e-8)
ngx_no_oracle = grad(nlp_no_oracle, x)
ngx_with_oracle = grad(nlp_with_oracle, x)
@test isapprox(ngx_no_oracle, ngx_with_oracle; atol = 1e-8, rtol = 1e-8)
if m > 0
ncx_no_oracle = cons(nlp_no_oracle, x)
ncx_with_oracle = cons(nlp_with_oracle, x)
@test isapprox(ncx_no_oracle, ncx_with_oracle; atol = 1e-8, rtol = 1e-8)
end
end
Loading