Skip to content

Commit

Permalink
MadNLPMOI extension added
Browse files Browse the repository at this point in the history
  • Loading branch information
sshin23 committed Mar 6, 2024
1 parent 8b704e7 commit b69867e
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 62 deletions.
10 changes: 7 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ uuid = "2621e9c9-9eb4-46b1-8089-e8c72242dfb6"
version = "0.7.0"

[deps]
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Expand All @@ -22,11 +20,17 @@ NLPModels = "~0.17.2, 0.18, 0.19, 0.20"
SolverCore = "~0.3"
julia = "1.9"

[weakdeps]
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"

[extensions]
MadNLPMOI = "MathOptInterface"

[extras]
MINLPTests = "ee0a3090-8ee9-5cdb-b8cb-8eeba3165522"
MadNLPTests = "b52a2a03-04ab-4a5f-9698-6a2deff93217"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "MadNLPTests", "MINLPTests", "Random"]
test = ["Test", "MadNLPTests", "MINLPTests", "Random", "MathOptInterface"]
102 changes: 54 additions & 48 deletions src/Interfaces/MOI_interface.jl → ext/MadNLPMOI/MadNLPMOI.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# MadNLP.jl
# Modified from Ipopt.jl (https://github.com/jump-dev/Ipopt.jl)
module MadNLPMOI

import MadNLP, MathOptInterface, NLPModels

const MOI = MathOptInterface
const MOIU = MathOptInterface.Utilities

include("utils.jl")

Expand All @@ -15,9 +19,9 @@ _is_parameter(term::MOI.ScalarAffineTerm) = _is_parameter(term.variable)
Create a new MadNLP optimizer.
"""
mutable struct Optimizer <: MOI.AbstractOptimizer
solver::Union{Nothing,MadNLPSolver}
nlp::Union{Nothing,AbstractNLPModel}
result::Union{Nothing,MadNLPExecutionStats{Float64}}
solver::Union{Nothing,MadNLP.MadNLPSolver}
nlp::Union{Nothing,NLPModels.AbstractNLPModel}
result::Union{Nothing,MadNLP.MadNLPExecutionStats{Float64}}

name::String
invalid_model::Bool
Expand All @@ -41,7 +45,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
nlp_model::Union{Nothing,MOI.Nonlinear.Model}
end

function Optimizer(; kwargs...)
function MadNLP.Optimizer(; kwargs...)
option_dict = Dict{Symbol, Any}()
for (name, value) in kwargs
option_dict[name] = value
Expand Down Expand Up @@ -739,31 +743,31 @@ function MOI.eval_hessian_lagrangian(model::Optimizer, H, x, σ, μ)
end

### NLPModels wrapper
struct MOIModel{T} <: AbstractNLPModel{T,Vector{T}}
meta::NLPModelMeta{T, Vector{T}}
struct MOIModel{T} <: NLPModels.AbstractNLPModel{T,Vector{T}}
meta::NLPModels.NLPModelMeta{T, Vector{T}}
model::Optimizer
counters::NLPModels.Counters
end

obj(nlp::MOIModel, x::AbstractVector{Float64}) = MOI.eval_objective(nlp.model,x)
NLPModels.obj(nlp::MOIModel, x::AbstractVector{Float64}) = MOI.eval_objective(nlp.model,x)

function grad!(nlp::MOIModel, x::AbstractVector{Float64}, g::AbstractVector{Float64})
function NLPModels. grad!(nlp::MOIModel, x::AbstractVector{Float64}, g::AbstractVector{Float64})
MOI.eval_objective_gradient(nlp.model, g, x)
end

function cons!(nlp::MOIModel, x::AbstractVector{Float64}, c::AbstractVector{Float64})
function NLPModels. cons!(nlp::MOIModel, x::AbstractVector{Float64}, c::AbstractVector{Float64})
MOI.eval_constraint(nlp.model, c, x)
end

function jac_coord!(nlp::MOIModel, x::AbstractVector{Float64}, jac::AbstractVector{Float64})
function NLPModels. jac_coord!(nlp::MOIModel, x::AbstractVector{Float64}, jac::AbstractVector{Float64})
MOI.eval_constraint_jacobian(nlp.model, jac, x)
end

function hess_coord!(nlp::MOIModel, x::AbstractVector{Float64}, l::AbstractVector{Float64}, hess::AbstractVector{Float64}; obj_weight::Float64=1.0)
function NLPModels. hess_coord!(nlp::MOIModel, x::AbstractVector{Float64}, l::AbstractVector{Float64}, hess::AbstractVector{Float64}; obj_weight::Float64=1.0)
MOI.eval_hessian_lagrangian(nlp.model, hess, x, obj_weight, l)
end

function hess_structure!(nlp::MOIModel, I::AbstractVector{T}, J::AbstractVector{T}) where T
function NLPModels. hess_structure!(nlp::MOIModel, I::AbstractVector{T}, J::AbstractVector{T}) where T
@assert length(I) == length(J) == length(MOI.hessian_lagrangian_structure(nlp.model))
cnt = 1
for (row, col) in MOI.hessian_lagrangian_structure(nlp.model)
Expand All @@ -772,7 +776,7 @@ function hess_structure!(nlp::MOIModel, I::AbstractVector{T}, J::AbstractVector{
end
end

function jac_structure!(nlp::MOIModel, I::AbstractVector{T}, J::AbstractVector{T}) where T
function NLPModels.jac_structure!(nlp::MOIModel, I::AbstractVector{T}, J::AbstractVector{T}) where T
@assert length(I) == length(J) == length(MOI.jacobian_structure(nlp.model))
cnt = 1
for (row, col) in MOI.jacobian_structure(nlp.model)
Expand Down Expand Up @@ -856,7 +860,7 @@ function MOIModel(model::Optimizer)
end

return MOIModel(
NLPModelMeta(
NLPModels.NLPModelMeta(
nvar,
x0 = x0,
lvar = model.variables.lower,
Expand Down Expand Up @@ -894,37 +898,34 @@ function MOI.optimize!(model::Optimizer)
if model.silent
model.options[:print_level] = MadNLP.ERROR
end
model.solver = MadNLPSolver(model.nlp; model.options...)
model.result = solve!(model.solver)
model.solver = MadNLP.MadNLPSolver(model.nlp; model.options...)
model.result = MadNLP.solve!(model.solver)
model.solve_time = model.solver.cnt.total_time
model.solve_iterations = model.solver.cnt.k
return
end

# From Ipopt/src/Interfaces/IpReturnCodes_inc.h
const _STATUS_CODES = Dict{Status,MOI.TerminationStatusCode}(
SOLVE_SUCCEEDED => MOI.LOCALLY_SOLVED,
SOLVED_TO_ACCEPTABLE_LEVEL => MOI.ALMOST_LOCALLY_SOLVED,
SEARCH_DIRECTION_BECOMES_TOO_SMALL => MOI.SLOW_PROGRESS,
DIVERGING_ITERATES => MOI.INFEASIBLE_OR_UNBOUNDED,
INFEASIBLE_PROBLEM_DETECTED => MOI.LOCALLY_INFEASIBLE,
MAXIMUM_ITERATIONS_EXCEEDED => MOI.ITERATION_LIMIT,
MAXIMUM_WALLTIME_EXCEEDED => MOI.TIME_LIMIT,
INITIAL => MOI.OPTIMIZE_NOT_CALLED,
# REGULAR
# RESTORE
# ROBUST
RESTORATION_FAILED => MOI.NUMERICAL_ERROR,
INVALID_NUMBER_DETECTED => MOI.INVALID_MODEL,
ERROR_IN_STEP_COMPUTATION => MOI.NUMERICAL_ERROR,
NOT_ENOUGH_DEGREES_OF_FREEDOM => MOI.INVALID_MODEL,
USER_REQUESTED_STOP => MOI.INTERRUPTED,
INTERNAL_ERROR => MOI.OTHER_ERROR,
INVALID_NUMBER_OBJECTIVE => MOI.INVALID_MODEL,
INVALID_NUMBER_GRADIENT => MOI.INVALID_MODEL,
INVALID_NUMBER_CONSTRAINTS => MOI.INVALID_MODEL,
INVALID_NUMBER_JACOBIAN => MOI.INVALID_MODEL,
INVALID_NUMBER_HESSIAN_LAGRANGIAN => MOI.INVALID_MODEL,
const _STATUS_CODES = Dict{MadNLP.Status,MOI.TerminationStatusCode}(
MadNLP.SOLVE_SUCCEEDED => MOI.LOCALLY_SOLVED,
MadNLP.SOLVED_TO_ACCEPTABLE_LEVEL => MOI.ALMOST_LOCALLY_SOLVED,
MadNLP.SEARCH_DIRECTION_BECOMES_TOO_SMALL => MOI.SLOW_PROGRESS,
MadNLP.DIVERGING_ITERATES => MOI.INFEASIBLE_OR_UNBOUNDED,
MadNLP.INFEASIBLE_PROBLEM_DETECTED => MOI.LOCALLY_INFEASIBLE,
MadNLP.MAXIMUM_ITERATIONS_EXCEEDED => MOI.ITERATION_LIMIT,
MadNLP.MAXIMUM_WALLTIME_EXCEEDED => MOI.TIME_LIMIT,
MadNLP.INITIAL => MOI.OPTIMIZE_NOT_CALLED,
MadNLP.RESTORATION_FAILED => MOI.NUMERICAL_ERROR,
MadNLP.INVALID_NUMBER_DETECTED => MOI.INVALID_MODEL,
MadNLP.ERROR_IN_STEP_COMPUTATION => MOI.NUMERICAL_ERROR,
MadNLP.NOT_ENOUGH_DEGREES_OF_FREEDOM => MOI.INVALID_MODEL,
MadNLP.USER_REQUESTED_STOP => MOI.INTERRUPTED,
MadNLP.INTERNAL_ERROR => MOI.OTHER_ERROR,
MadNLP.INVALID_NUMBER_OBJECTIVE => MOI.INVALID_MODEL,
MadNLP.INVALID_NUMBER_GRADIENT => MOI.INVALID_MODEL,
MadNLP.INVALID_NUMBER_CONSTRAINTS => MOI.INVALID_MODEL,
MadNLP.INVALID_NUMBER_JACOBIAN => MOI.INVALID_MODEL,
MadNLP.INVALID_NUMBER_HESSIAN_LAGRANGIAN => MOI.INVALID_MODEL,
)

### MOI.ResultCount
Expand Down Expand Up @@ -953,7 +954,11 @@ function MOI.get(model::Optimizer, ::MOI.RawStatusString)
elseif model.solver === nothing
return "Optimize not called"
end
return get_status_output(model.result.status, model.result.options)
return get(
MadNLP.STATUS_OUTPUT_DICT,
model.result.status,
"Unknown result status: $(model.result.status)",
)
end


Expand All @@ -964,11 +969,11 @@ function MOI.get(model::Optimizer, attr::MOI.PrimalStatus)
return MOI.NO_SOLUTION
end
status = model.result.status
if status == SOLVE_SUCCEEDED
if status == MadNLP.SOLVE_SUCCEEDED
return MOI.FEASIBLE_POINT
elseif status == SOLVED_TO_ACCEPTABLE_LEVEL
elseif status == MadNLP.SOLVED_TO_ACCEPTABLE_LEVEL
return MOI.NEARLY_FEASIBLE_POINT
elseif status == INFEASIBLE_PROBLEM_DETECTED
elseif status == MadNLP.INFEASIBLE_PROBLEM_DETECTED
return MOI.INFEASIBLE_POINT
else
return MOI.UNKNOWN_RESULT_STATUS
Expand All @@ -982,11 +987,11 @@ function MOI.get(model::Optimizer, attr::MOI.DualStatus)
return MOI.NO_SOLUTION
end
status = model.result.status
if status == SOLVE_SUCCEEDED
if status == MadNLP.SOLVE_SUCCEEDED
return MOI.FEASIBLE_POINT
elseif status == SOLVED_TO_ACCEPTABLE_LEVEL
elseif status == MadNLP.SOLVED_TO_ACCEPTABLE_LEVEL
return MOI.NEARLY_FEASIBLE_POINT
elseif status == INFEASIBLE_PROBLEM_DETECTED
elseif status == MadNLP.INFEASIBLE_PROBLEM_DETECTED
return MOI.INFEASIBLE_POINT
else
return MOI.UNKNOWN_RESULT_STATUS
Expand Down Expand Up @@ -1122,3 +1127,4 @@ end
### MOI.BarrierIterations
MOI.get(model::Optimizer,::MOI.BarrierIterations) = model.solve_iterations

end # module
File renamed without changes.
1 change: 0 additions & 1 deletion src/Interfaces/interfaces.jl

This file was deleted.

1 change: 0 additions & 1 deletion src/LinearSolvers/linearsolvers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,3 @@ include("lapack.jl")
include("umfpack.jl")
include("cholmod.jl")
include("ldl.jl")

10 changes: 2 additions & 8 deletions src/MadNLP.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
module MadNLP

import Pkg.TOML: parsefile
import MathOptInterface
import Libdl: dlopen, dlext, RTLD_DEEPBIND, RTLD_GLOBAL
import Printf: @sprintf
import LinearAlgebra: BLAS, Adjoint, Symmetric, mul!, ldiv!, norm, dot, diagind, normInf, transpose!, issuccess
import LinearAlgebra: cholesky, qr, lu, cholesky!, axpy!
Expand All @@ -11,12 +9,8 @@ import SparseArrays: SparseArrays, AbstractSparseMatrix, SparseMatrixCSC, sparse
import Base: string, show, print, size, getindex, copyto!, @kwdef
import SuiteSparse: UMFPACK, CHOLMOD
import NLPModels
import NLPModels: finalize, AbstractNLPModel, obj, grad!, cons!, jac_coord!, hess_coord!, hess_structure!, jac_structure!, NLPModelMeta, get_nvar, get_ncon, get_minimize, get_x0, get_y0, get_nnzj, get_nnzh, get_lvar, get_uvar, get_lcon, get_ucon, Counters as _Counters # get_zl,get_zu
import NLPModels: finalize, AbstractNLPModel, obj, grad!, cons!, jac_coord!, hess_coord!, hess_structure!, jac_structure!, NLPModelMeta, get_nvar, get_ncon, get_minimize, get_x0, get_y0, get_nnzj, get_nnzh, get_lvar, get_uvar, get_lcon, get_ucon
import SolverCore: solve!, getStatus, AbstractOptimizationSolver, AbstractExecutionStats

const MOI = MathOptInterface
const MOIU = MathOptInterface.Utilities

export MadNLPSolver, MadNLPOptions, UmfpackSolver, LapackCPUSolver, madnlp, solve!

# Version info
Expand All @@ -32,6 +26,6 @@ include(joinpath("KKT", "KKTsystem.jl"))
include(joinpath("LinearSolvers","linearsolvers.jl"))
include("options.jl")
include(joinpath("IPM", "IPM.jl"))
include(joinpath("Interfaces","interfaces.jl"))
include("ext.jl")

end # end module
6 changes: 6 additions & 0 deletions src/ext.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
Optimizer()
Create a new MadNLP optimizer.
"""
function Optimizer end
3 changes: 2 additions & 1 deletion test/MOI_interface_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module TestMOIWrapper
using MadNLP
using Test

const MOI = MadNLP.MathOptInterface
using MathOptInterface
const MOI = MathOptInterface

function runtests()
for name in names(@__MODULE__; all = true)
Expand Down

0 comments on commit b69867e

Please sign in to comment.