diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f106fe4..2e9da97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,10 +24,10 @@ jobs: - version: '1' os: windows-latest arch: x86 - - version: '1.3' + - version: '1.6' os: ubuntu-latest arch: x64 - - version: '1.3' + - version: '1.6' os: ubuntu-latest arch: x86 - version: 'nightly' diff --git a/Project.toml b/Project.toml index 59c46c1..c85d6b3 100644 --- a/Project.toml +++ b/Project.toml @@ -1,20 +1,25 @@ name = "NLopt" uuid = "76087f3c-5699-56af-9a33-bf431cd00edd" -version = "0.6.6" +version = "1.0.0" [deps] MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" -MathProgBase = "fdba3010-5040-5b88-9595-932c9decdf73" NLopt_jll = "079eb43e-fd8e-5478-9966-2cf3e3edb778" [compat] -MathOptInterface = "0.10.4, 1" -MathProgBase = "0.5, 0.6, 0.7, 0.8" +MathOptInterface = "1" NLopt_jll = "2.7" -julia = "1.3" +julia = "1.6" + +[extensions] +NLoptMathOptInterfaceExt = ["MathOptInterface"] [extras] +MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test"] +test = ["MathOptInterface", "Test"] + +[weakdeps] +MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" diff --git a/src/MOI_wrapper.jl b/ext/NLoptMathOptInterfaceExt.jl similarity index 99% rename from src/MOI_wrapper.jl rename to ext/NLoptMathOptInterfaceExt.jl index 0fdfd0c..6658747 100644 --- a/src/MOI_wrapper.jl +++ b/ext/NLoptMathOptInterfaceExt.jl @@ -3,10 +3,21 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. +module NLoptMathOptInterfaceExt + +using NLopt + import MathOptInterface const MOI = MathOptInterface +function __init__() + # we need to add extension types back to the toplevel module + @static if VERSION >= v"1.9" + setglobal!(NLopt, :Optimizer, Optimizer) + end +end + mutable struct _ConstraintInfo{F,S} func::F set::S @@ -1177,3 +1188,5 @@ function MOI.get( MOI.throw_if_not_valid(model, ci) return model.constraint_primal_quadratic_eq[ci.value] end + +end # module diff --git a/src/MPB_wrapper.jl b/src/MPB_wrapper.jl deleted file mode 100644 index a8e1113..0000000 --- a/src/MPB_wrapper.jl +++ /dev/null @@ -1,225 +0,0 @@ -# Copyright (c) 2013: Steven G. Johnson and contributors -# -# Use of this source code is governed by an MIT-style license that can be found -# in the LICENSE.md file or at https://opensource.org/licenses/MIT. - -export NLoptSolver -struct NLoptSolver <: SolverInterface.AbstractMathProgSolver - algorithm::Symbol - stopval::Real - ftol_rel::Real - ftol_abs::Real - xtol_rel::Real - xtol_abs - constrtol_abs::Real - maxeval::Integer - maxtime::Real - initial_step - population::Integer - seed - vector_storage::Integer -end - -function NLoptSolver(;algorithm::Symbol=:none, stopval::Real=NaN, - ftol_rel::Real=1e-7, ftol_abs::Real=NaN, xtol_rel::Real=1e-7, - xtol_abs=nothing, constrtol_abs=1e-7, - maxeval::Integer=0, maxtime::Real=0, initial_step=nothing, - population::Integer=0, seed=nothing, vector_storage::Integer=0) - if algorithm == :none - error("Must specify an algorithm for NLoptSolver") - end - return NLoptSolver(algorithm, stopval, ftol_rel, ftol_abs, xtol_rel, - xtol_abs, constrtol_abs, maxeval, maxtime, initial_step, population, seed, - vector_storage) -end - -mutable struct NLoptMathProgModel <: SolverInterface.AbstractNonlinearModel - algorithm::Symbol - opt # can't create Opt object on construction because it needs problem dimensions - x::Vector{Float64} - objval::Float64 - status::Symbol - # options... - stopval::Real - ftol_rel::Real - ftol_abs::Real - xtol_rel::Real - xtol_abs - constrtol_abs::Real - maxeval::Integer - maxtime::Real - initial_step - population::Integer - seed - vector_storage::Integer -end - -SolverInterface.NonlinearModel(s::NLoptSolver) = NLoptMathProgModel(s.algorithm, nothing, Float64[], NaN, :NotSolved, s.stopval, s.ftol_rel, s.ftol_abs, s.xtol_rel, s.xtol_abs, s.constrtol_abs, s.maxeval, s.maxtime, s.initial_step, s.population, s.seed, s.vector_storage) - -function SolverInterface.loadproblem!(m::NLoptMathProgModel, numVar::Integer, numConstr::Integer, x_l, x_u, g_lb, g_ub, sense::Symbol, d::SolverInterface.AbstractNLPEvaluator) - - (sense == :Min || sense == :Max) || error("Unrecognized sense $sense") - m.opt = Opt(m.algorithm, numVar) - - # load parameters - stopval!(m.opt, m.stopval) - if !isnan(m.ftol_rel) - ftol_rel!(m.opt, m.ftol_rel) - end - if !isnan(m.ftol_abs) - ftol_abs!(m.opt, m.ftol_abs) - end - if !isnan(m.xtol_rel) - xtol_rel!(m.opt, m.xtol_rel) - end - if m.xtol_abs != nothing - xtol_abs!(m.opt, m.xtol_abs) - end - maxeval!(m.opt, m.maxeval) - maxtime!(m.opt, m.maxtime) - if m.initial_step != nothing - initial_step!(m.opt, m.initial_step) - end - population!(m.opt, m.population) - if isa(m.seed, Integer) - NLopt.srand(m.seed) - end - vector_storage!(m.opt, m.vector_storage) - - lower_bounds!(m.opt, x_l) - upper_bounds!(m.opt, x_u) - - eqidx = findall(g_lb .== g_ub) # indices of equalities - ineqidx = findall(g_lb .!= g_ub) - - # map from eqidx/ineqidx to index in equalities/inequalities - constrmap = zeros(Int, numConstr) - for i in 1:length(eqidx) - constrmap[eqidx[i]] = i - end - ineqcounter = 1 - for i in 1:length(ineqidx) - k = ineqidx[i] - constrmap[k] = ineqcounter - if isinf(g_lb[k]) || isinf(g_ub[k]) - ineqcounter += 1 - else # constraint has bounds on both sides, keep room for it - ineqcounter += 2 - end - end - numineq = ineqcounter-1 - numeq = length(eqidx) - - isderivativefree = string(m.algorithm)[2] == 'N' - if isderivativefree - requested_features = Symbol[] - else - requested_features = numConstr > 0 ? [:Grad, :Jac] : [:Grad] - end - - SolverInterface.initialize(d, requested_features) - - function f(x::Vector, grad::Vector) - if length(grad) > 0 - SolverInterface.eval_grad_f(d, grad, x) - end - return SolverInterface.eval_f(d, x) - end - if sense == :Min - min_objective!(m.opt, f) - else - max_objective!(m.opt, f) - end - - Jac_I,Jac_J = numConstr > 0 ? SolverInterface.jac_structure(d) : (Int[], Int[]) - Jac_val = zeros(length(Jac_I)) - g_vec = zeros(numConstr) - - # somewhat inefficient because we evaluate g and the constraints - # once for equalities and once for inequalities - - function g_eq(result::Vector, x::Vector, jac::Matrix) - if length(jac) > 0 - fill!(jac, 0.0) - SolverInterface.eval_jac_g(d, Jac_val, x) - for k in 1:length(Jac_val) - row = Jac_I[k] - if g_lb[row] == g_ub[row] - jac[Jac_J[k],constrmap[row]] += Jac_val[k] - end - end - end - SolverInterface.eval_g(d, g_vec, x) - for (ctr,idx) in enumerate(eqidx) - result[ctr] = g_vec[idx] - g_ub[idx] - end - end - - equality_constraint!(m.opt, g_eq, fill(m.constrtol_abs,numeq)) - - # inequalities need to be massaged a bit - # f(x) <= u => f(x) - u <= 0 - # f(x) >= l => l - f(x) <= 0 - - function g_ineq(result::Vector, x::Vector, jac::Matrix) - if length(jac) > 0 - fill!(jac, 0.0) - SolverInterface.eval_jac_g(d, Jac_val, x) - for k in 1:length(Jac_val) - row = Jac_I[k] - g_lb[row] == g_ub[row] && continue - if isinf(g_lb[row]) # upper bound - jac[Jac_J[k],constrmap[row]] += Jac_val[k] - elseif isinf(g_ub[row]) # lower bound - jac[Jac_J[k],constrmap[row]] -= Jac_val[k] - else - # boxed - jac[Jac_J[k],constrmap[row]] += Jac_val[k] - jac[Jac_J[k],constrmap[row]+1] -= Jac_val[k] - end - end - end - SolverInterface.eval_g(d, g_vec, x) - for row in 1:numConstr - g_lb[row] == g_ub[row] && continue - if isinf(g_lb[row]) - result[constrmap[row]] = g_vec[row] - g_ub[row] - elseif isinf(g_ub[row]) - result[constrmap[row]] = g_lb[row] - g_vec[row] - else - result[constrmap[row]] = g_vec[row] - g_ub[row] - result[constrmap[row]+1] = g_lb[row] - g_vec[row] - end - end - end - - - inequality_constraint!(m.opt, g_ineq, fill(m.constrtol_abs, numineq)) -end - -function SolverInterface.setwarmstart!(m::NLoptMathProgModel,x) - m.x = copy(float(x)) -end - -function SolverInterface.optimize!(m::NLoptMathProgModel) - isa(m.opt, Opt) || error("Must load problem before solving") - (optf,optx,ret) = optimize!(m.opt, m.x) - m.objval = optf - m.status = ret -end - -function SolverInterface.status(m::NLoptMathProgModel) - if m.status == :SUCCESS || m.status == :FTOL_REACHED || m.status == :XTOL_REACHED - return :Optimal - elseif m.status == :ROUNDOFF_LIMITED - return :Suboptimal - elseif m.status in (:STOPVAL_REACHED,:MAXEVAL_REACHED,:MAXTIME_REACHED) - return :UserLimit - else - error("Unknown status $(m.status)") - end -end - -SolverInterface.getsolution(m::NLoptMathProgModel) = m.x -SolverInterface.getobjval(m::NLoptMathProgModel) = m.objval - diff --git a/src/NLopt.jl b/src/NLopt.jl index 8cc1164..df156ac 100644 --- a/src/NLopt.jl +++ b/src/NLopt.jl @@ -10,9 +10,6 @@ export Opt, NLOPT_VERSION, algorithm, algorithm_name, ForcedStop, min_objective!, max_objective!, equality_constraint!, inequality_constraint!, remove_constraints!, optimize!, optimize, Algorithm, Result -import MathProgBase.SolverInterface -import MathProgBase.SolverInterface.optimize! - using NLopt_jll ############################################################################ @@ -633,9 +630,10 @@ end optimize(o::Opt, x::AbstractVector{<:Real}) = optimize!(o, copyto!(Array{Cdouble}(undef,length(x)), x)) -############################################################################ - -include("MPB_wrapper.jl") -include("MOI_wrapper.jl") +if !isdefined(Base, :get_extension) + include("../ext/NLoptMathOptInterfaceExt.jl") + using .NLoptMathOptInterfaceExt + const Optimizer = NLoptMathOptInterfaceExt.Optimizer +end end # module diff --git a/test/MPB_wrapper.jl b/test/MPB_wrapper.jl deleted file mode 100644 index 59bd855..0000000 --- a/test/MPB_wrapper.jl +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2013: Steven G. Johnson and contributors -# -# Use of this source code is governed by an MIT-style license that can be found -# in the LICENSE.md file or at https://opensource.org/licenses/MIT. - -using NLopt - -import MathProgBase -nlp = joinpath(dirname(dirname(pathof(MathProgBase))), "test", "nlp.jl") -include(nlp) - -nlptest(NLoptSolver(algorithm=:LD_SLSQP)) -# test derivative-free -rosenbrocktest(NLoptSolver(algorithm=:LN_PRAXIS)) diff --git a/test/runtests.jl b/test/runtests.jl index 7df5c9c..0b0c614 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,7 +5,6 @@ include("tutorial.jl") include("fix133.jl") -include("MPB_wrapper.jl") include("MOI_wrapper.jl") using NLopt @@ -27,4 +26,3 @@ end ) @test_throws err opt.initial_step end -