Skip to content

Commit

Permalink
Merge pull request #20 from emmt/CUTEst
Browse files Browse the repository at this point in the history
Interface to CUTEst
  • Loading branch information
emmt authored Jan 29, 2024
2 parents 46b84ee + 1ba519d commit 6c30115
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,17 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
PRIMA_jll = "eead6e0c-2d5b-5641-a95c-b722de96d551"
TypeUtils = "c3b1956e-8857-4d84-9b79-890df85b1e67"

[weakdeps]
CUTEst = "1b53aba6-35b6-5f92-a507-53c67d53f819"
NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6"

[extensions]
PRIMACUTEstExt = "CUTEst"
PRIMANLPModelsExt = "NLPModels"

[compat]
CUTEst = "0.13"
NLPModels = "0.20"
PRIMA_jll = "0.7.1"
TypeUtils = "0.3"
julia = "1.6"
Expand Down
24 changes: 24 additions & 0 deletions ext/PRIMACUTEstExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module PRIMACUTEstExt

if isdefined(Base, :get_extension)
using NLPModels
using CUTEst
using PRIMA
else
using ..NLPModels
using ..CUTEst
using ..PRIMA
end

for func in (:uobyqa, :newuoa, :bobyqa, :lincoa, :cobyla, :prima)
@eval function PRIMA.$(Symbol(func,"_CUTEst"))(name::AbstractString; kwds...)
nlp = CUTEstModel(name)
try
return $func(nlp; kwds...)
finally
finalize(nlp)
end
end
end

end # module
82 changes: 82 additions & 0 deletions ext/PRIMANLPModelsExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
module PRIMANLPModelsExt

if isdefined(Base, :get_extension)
using NLPModels
using PRIMA
else
using ..NLPModels
using ..PRIMA
end

# This structure is to wrap a non-linear problem model into a callable object.
struct ObjectiveFunction{F<:AbstractNLPModel} <: Function
nlp::F
end
(f::ObjectiveFunction)(x::AbstractVector) = obj(f.nlp, x)

function check_variables(nlp::AbstractNLPModel, x0::AbstractVector)
length(x0) == get_nvar(nlp) || error(
"initial variables must have $(get_nvar(nlp)) elements")
return x0
end

const Variables = AbstractVector{<:Real}

for func in (:uobyqa, :newuoa)
@eval function PRIMA.$func(nlp::AbstractNLPModel, x0::Variables = get_x0(nlp); kwds...)
has_bounds(nlp) && error("`$($func)` cannot solve problems with bound constraints")
has_equalities(nlp) && error("`$($func)` cannot solve problems with equality constraints")
has_inequalities(nlp) && error("`$($func)` cannot solve problems with inequality constraints")
return uobyqa(ObjectiveFunction(nlp), check_variables(nlp, x0); kwds...)
end
end

function PRIMA.bobyqa(nlp::AbstractNLPModel, x0::Variables = get_x0(nlp); kwds...)
has_equalities(nlp) && error("`bobyqa` cannot solve problems with equality constraints")
has_inequalities(nlp) && error("`bobyqa` cannot solve problems with inequality constraints")
return bobyqa(ObjectiveFunction(nlp), check_variables(nlp, x0); kwds...,
xl = get_lvar(nlp), xu = get_uvar(nlp))
end

function PRIMA.lincoa(nlp::AbstractNLPModel, x0::Variables = get_x0(nlp); kwds...)
nlin = get_nlin(nlp) # number of linear constraints
nnln = get_nnln(nlp) # number of non-linear constraints
nlin == 0 || error("linear constraints not yet implemented for NLPModels in `lincoa`")
nnln == 0 || error("`lincoa` cannot solve problems with non-linear constraints")
return lincoa(ObjectiveFunction(nlp), check_variables(nlp, x0); kwds...,
xl = get_lvar(nlp), xu = get_uvar(nlp),
linear_eq = nothing, linear_ineq = nothing)
end

function PRIMA.cobyla(nlp::AbstractNLPModel, x0::Variables = get_x0(nlp); kwds...)
nlin = get_nlin(nlp) # number of linear constraints
nnln = get_nnln(nlp) # number of non-linear constraints
nlin == 0 || error("linear constraints not yet implemented for NLPModels in `cobyla`")
nnln == 0 || error("non-linear constraints not yet implemented for NLPModels in `cobyla`")
return cobyla(ObjectiveFunction(nlp), check_variables(nlp, x0); kwds...,
xl = get_lvar(nlp), xu = get_uvar(nlp),
linear_eq = nothing, linear_ineq = nothing,
nonlinear_eq = nothing, nonlinear_ineq = nothing)
end

function PRIMA.prima(nlp::AbstractNLPModel, x0::Variables = get_x0(nlp); kwds...)
nlin = get_nlin(nlp) # number of linear constraints
nnln = get_nnln(nlp) # number of non-linear constraints
if nnln > 0
# Only COBYLA can deal with non-linear constraints.
error("solving problem with non-linear constraints by COBYLA not yet implemented")
return cobyla(nlp, x0; kwds...)
elseif nlin > 0
# LINCOA can deal with bounds and linear constraints.
error("solving problem with linear constraints by LINCOA not yet implemented")
return lincoa(nlp, x0; kwds...)
elseif has_bounds(nlp)
# BOBYQA can deal with bounds.
return bobyqa(nlp, x0; kwds...)
else
# Use NEWUOA for unconstrained problems.
return newuoa(nlp, x0; kwds...)
end
end

end # module
17 changes: 17 additions & 0 deletions src/PRIMA.jl
Original file line number Diff line number Diff line change
Expand Up @@ -985,4 +985,21 @@ function _get_scaling(scl::AbstractVector{<:Real}, n::Int)
return convert(Vector{Cdouble}, scl)
end

for func in (:uobyqa, :newuoa, :bobyqa, :lincoa, :cobyla, :prima)
@eval $(Symbol(func,"_CUTEst"))(args...; kwds...) =
error("invalid arguments or `CUTEst` package not yet loaded")
end

@static if !isdefined(Base, :get_extension)
using Requires
function __init__()
if !isdefined(Base, :get_extension)
@require CUTEst = "1b53aba6-35b6-5f92-a507-53c67d53f819" include(
"../ext/PRIMACUTEstExt.jl")
@require NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6" include(
"../ext/PRIMANLPModelsExt.jl")
end
end
end

end # module

1 comment on commit 6c30115

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log or 📝 job summary for details.

Unrecognized words (8)

nlin
nlp
nnln
nvar
PRIMACUT
PRIMANLP
uvar
weakdeps

To accept these unrecognized words as correct, you could run the following commands

... in a clone of the git@github.com:libprima/PRIMA.jl.git repository
on the main branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/main/apply.pl' |
perl - 'https://github.com/libprima/PRIMA.jl/actions/runs/7693173034/attempts/1'
Errors (1)

See the 📜action log or 📝 job summary for details.

❌ Errors Count
❌ check-file-path 2

See ❌ Event descriptions for more information.

If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.