diff --git a/Project.toml b/Project.toml index 239ccec..518aa7f 100644 --- a/Project.toml +++ b/Project.toml @@ -9,7 +9,7 @@ TypeUtils = "c3b1956e-8857-4d84-9b79-890df85b1e67" [compat] julia = "1.6" -PRIMA_jll = "0.7.0" +PRIMA_jll = "0.7.1" TypeUtils = "0.3" [extras] diff --git a/gen/Project.toml b/gen/Project.toml new file mode 100644 index 0000000..a194c01 --- /dev/null +++ b/gen/Project.toml @@ -0,0 +1,7 @@ +[deps] +Clang = "40e3b903-d033-50b4-a0cc-940c62c95e31" +JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" +PRIMA_jll = "eead6e0c-2d5b-5641-a95c-b722de96d551" + +[compat] +julia = "1.6" diff --git a/gen/README.md b/gen/README.md new file mode 100644 index 0000000..0ddb82f --- /dev/null +++ b/gen/README.md @@ -0,0 +1,9 @@ +# Wrapping headers + +This directory contains a script that can be used to automatically generate wrappers from the C headers provided by PRIMA. +This is done using Clang.jl. + +# Usage + +Either run `julia wrapper.jl` directly, or include it and call the `main()` function. +Be sure to activate the project environment in this folder (`julia --project`), which will install `Clang.jl` and `JuliaFormatter.jl`. diff --git a/gen/prima.toml b/gen/prima.toml new file mode 100644 index 0000000..7d147e5 --- /dev/null +++ b/gen/prima.toml @@ -0,0 +1,13 @@ +[general] +use_julia_native_enum_type = true +print_using_CEnum = false +library_name = "libprimac" + +[codegen] +use_julia_bool = true +always_NUL_terminated_string = true +use_ccall_macro = true + +[codegen.macro] +macro_mode = "basic" +add_comment_for_skipped_macro = true diff --git a/gen/wrapper.jl b/gen/wrapper.jl new file mode 100644 index 0000000..2609c9c --- /dev/null +++ b/gen/wrapper.jl @@ -0,0 +1,30 @@ +# Script to parse PRIMA headers and generate Julia wrappers. +using PRIMA_jll +using Clang +using Clang.Generators +using JuliaFormatter + +function main() + + cd(@__DIR__) + include = joinpath(PRIMA_jll.artifact_dir, "include", "prima") + headers = [joinpath(include, "prima.h")] + + options = load_options(joinpath(@__DIR__, "prima.toml")) + options["general"]["output_file_path"] = joinpath("..", "src", "wrappers.jl") + + args = get_default_args() + push!(args, "-I$include") + + ctx = create_context(headers, args, options) + build!(ctx) + + path = options["general"]["output_file_path"] + format_file(path, YASStyle()) + return nothing +end + +# If we want to use the file as a script with `julia wrapper.jl` +if abspath(PROGRAM_FILE) == @__FILE__ + main() +end diff --git a/src/PRIMA.jl b/src/PRIMA.jl index 1482a76..002a9e3 100644 --- a/src/PRIMA.jl +++ b/src/PRIMA.jl @@ -1,41 +1,16 @@ module PRIMA +using PRIMA_jll +const libprimac = PRIMA_jll.libprimac +include("wrappers.jl") + export bobyqa, cobyla, lincoa, newuoa, uobyqa using TypeUtils -using PRIMA_jll -const libprimac = PRIMA_jll.libprimac #------------------------------------------------------------------------------ # PUBLIC INTERFACE -# Verbosity level -@enum Message::Cint begin - MSG_NONE = 0 # No messages - MSG_EXIT = 1 # Exit reasons - MSG_RHO = 2 # Rho changes - MSG_FEVL = 3 # The object/constraint functions get evaluated -end - -# Possible return values -@enum Status::Cint begin - SMALL_TR_RADIUS = 0 - FTARGET_ACHIEVED = 1 - TRSUBP_FAILED = 2 - MAXFUN_REACHED = 3 - MAXTR_REACHED = 20 - NAN_INF_X = -1 - NAN_INF_F = -2 - NAN_INF_MODEL = -3 - NO_SPACE_BETWEEN_BOUNDS = 6 - DAMAGING_ROUNDING = 7 - ZERO_LINEAR_CONSTRAINT = 8 - INVALID_INPUT = 100 - ASSERTION_FAILS = 101 - VALIDATION_FAILS = 102 - MEMORY_ALLOCATION_FAILS = 103 -end - """ PRIMA.reason(rc) -> str @@ -43,8 +18,7 @@ yields a textual message explaining `rc`, the code returned by one of the PRIMA optimizers. """ -reason(status::Union{Integer,Status}) = - unsafe_string(@ccall libprimac.prima_get_rc_string(Integer(status)::Cint)::Cstring) +reason(status::Union{Integer,Status}) = unsafe_string(prima_get_rc_string(status)) # The high level wrappers. for func in (:bobyqa, :newuoa, :uobyqa, :lincoa, :cobyla) @@ -107,20 +81,7 @@ function bobyqa!(f, x::DenseVector{Cdouble}; fp = _push_wrapper(fw) # pointer to C-callable function try # Call low-level optimizer. - rc = @ccall libprimac.prima_bobyqa( - fp ::Ptr{Cvoid}, # C-type: prima_obj - n ::Cint, - x ::Ptr{Cdouble}, # n elements - fx ::Ref{Cdouble}, - xl ::Ptr{Cdouble}, # const, n elements - xu ::Ptr{Cdouble}, # const, n elements - nf ::Ref{Cint}, - rhobeg ::Cdouble, - rhoend ::Cdouble, - ftarget ::Cdouble, - maxfun ::Cint, # maxfun - npt ::Cint, - Integer(iprint) ::Cint)::Cint + rc = prima_bobyqa(calfun, n, x, f, xl, xu, nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint) return (fx[], Int(nf[]), rc) finally _pop_wrapper(fw) @@ -159,18 +120,7 @@ function newuoa!(f, x::DenseVector{Cdouble}; fp = _push_wrapper(fw) # pointer to C-callable function try # Call low-level optimizer. - rc = @ccall libprimac.prima_newuoa( - fp ::Ptr{Cvoid}, # C-type: prima_obj - n ::Cint, - x ::Ptr{Cdouble}, # n elements - fx ::Ref{Cdouble}, - nf ::Ref{Cint}, - rhobeg ::Cdouble, - rhoend ::Cdouble, - ftarget ::Cdouble, - maxfun ::Cint, - npt ::Cint, - Integer(iprint) ::Cint)::Cint + rc = prima_newuoa(calfun, n, x, f, nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint) return (fx[], Int(nf[]), rc) finally _pop_wrapper(fw) @@ -207,17 +157,7 @@ function uobyqa!(f, x::DenseVector{Cdouble}; fp = _push_wrapper(fw) # pointer to C-callable function try # Call low-level optimizer. - rc = @ccall libprimac.prima_uobyqa( - fp ::Ptr{Cvoid}, # C-type: prima_obj - n ::Cint, - x ::Ptr{Cdouble}, # n elements - fx ::Ref{Cdouble}, - nf ::Ref{Cint}, - rhobeg ::Cdouble, - rhoend ::Cdouble, - ftarget ::Cdouble, - maxfun ::Cint, - Integer(iprint) ::Cint)::Cint + rc = prima_uobyqa(calfun, n, x, f, nf, rhobeg, rhoend, ftarget, maxfun, iprint) return (fx[], Int(nf[]), rc) finally _pop_wrapper(fw) @@ -255,28 +195,9 @@ function cobyla!(f, x::DenseVector{Cdouble}; try # Call low-level optimizer. - rc = GC.@preserve nlconstr ineqconstr eqconstr @ccall libprimac.prima_cobyla( - m_nlcon ::Cint, - fp ::Ptr{Cvoid}, # C-type: prima_objcon - n ::Cint, - x ::Ptr{Cdouble}, # n elements - fx ::Ref{Cdouble}, - cstrv ::Ref{Cdouble}, - nlcon_ptr ::Ptr{Cdouble}, # m_nlcon elements - m_ineq ::Cint, - A_ineq ::Ptr{Cdouble}, # const, m_ineq*n elements - b_ineq ::Ptr{Cdouble}, # const, m_ineq elements - m_eq ::Cint, - A_eq ::Ptr{Cdouble}, # const, m_eq*n elements - b_eq ::Ptr{Cdouble}, # const, m_eq elements - xl ::Ptr{Cdouble}, # const, n elements - xu ::Ptr{Cdouble}, # const, n elements - nf ::Ref{Cint}, - rhobeg ::Cdouble, - rhoend ::Cdouble, - ftarget ::Cdouble, - maxfun ::Cint, - Integer(iprint) ::Cint)::Cint + rc = GC.@preserve nlconstr ineqconstr eqconstr prima_cobyla(m_nlcon, calcfc, + n, x, f, cstrv, nlconstr, m_ineq, Aineq, bineq, m_eq, Aeq, beq, xl, xu, + nf, rhobeg, rhoend, ftarget, maxfun, iprint) return (fx[], Int(nf[]), rc, cstrv[]) finally _pop_wrapper(fw) @@ -311,27 +232,9 @@ function lincoa!(f, x::DenseVector{Cdouble}; fp = _push_wrapper(fw) # pointer to C-callable function try # Call low-level optimizer. - rc = GC.@preserve ineqconstr eqconstr @ccall libprimac.prima_lincoa( - fp ::Ptr{Cvoid}, # C-type: prima_objcon - n ::Cint, - x ::Ptr{Cdouble}, # n elements - fx ::Ref{Cdouble}, - cstrv ::Ref{Cdouble}, - m_ineq ::Cint, - A_ineq ::Ptr{Cdouble}, # const, m_ineq*n elements - b_ineq ::Ptr{Cdouble}, # const, m_ineq elements - m_eq ::Cint, - A_eq ::Ptr{Cdouble}, # const, m_eq*n elements - b_eq ::Ptr{Cdouble}, # const, m_eq elements - xl ::Ptr{Cdouble}, # const, n elements - xu ::Ptr{Cdouble}, # const, n elements - nf ::Ref{Cint}, - rhobeg ::Cdouble, - rhoend ::Cdouble, - ftarget ::Cdouble, - maxfun ::Cint, - npt ::Cint, - Integer(iprint) ::Cint)::Cint + rc = GC.@preserve ineqconstr eqconstr prima_lincoa(calfun, n, x, f, + cstrv, m_ineq, Aineq, bineq, m_eq, Aeq, beq, xl, xu, nf, + rhobeg, rhoend, ftarget, maxfun, npt, iprint) return (fx[], Int(nf[]), rc, cstrv[]) finally _pop_wrapper(fw) diff --git a/src/wrappers.jl b/src/wrappers.jl new file mode 100644 index 0000000..d9e2ace --- /dev/null +++ b/src/wrappers.jl @@ -0,0 +1,73 @@ +@enum prima_message::UInt32 begin + PRIMA_MSG_NONE = 0 + PRIMA_MSG_EXIT = 1 + PRIMA_MSG_RHO = 2 + PRIMA_MSG_FEVL = 3 +end + +@enum prima_rc::Int32 begin + PRIMA_SMALL_TR_RADIUS = 0 + PRIMA_FTARGET_ACHIEVED = 1 + PRIMA_TRSUBP_FAILED = 2 + PRIMA_MAXFUN_REACHED = 3 + PRIMA_MAXTR_REACHED = 20 + PRIMA_NAN_INF_X = -1 + PRIMA_NAN_INF_F = -2 + PRIMA_NAN_INF_MODEL = -3 + PRIMA_NO_SPACE_BETWEEN_BOUNDS = 6 + PRIMA_DAMAGING_ROUNDING = 7 + PRIMA_ZERO_LINEAR_CONSTRAINT = 8 + PRIMA_INVALID_INPUT = 100 + PRIMA_ASSERTION_FAILS = 101 + PRIMA_VALIDATION_FAILS = 102 + PRIMA_MEMORY_ALLOCATION_FAILS = 103 +end + +function prima_get_rc_string(rc) + @ccall libprimac.prima_get_rc_string(rc::Cint)::Cstring +end + +function prima_bobyqa(calfun, n, x, f, xl, xu, nf, rhobeg, rhoend, ftarget, maxfun, npt, + iprint) + @ccall libprimac.prima_bobyqa(calfun::Ptr{Cvoid}, n::Cint, x::Ptr{Cdouble}, + f::Ptr{Cdouble}, xl::Ptr{Cdouble}, xu::Ptr{Cdouble}, + nf::Ptr{Cint}, rhobeg::Cdouble, rhoend::Cdouble, + ftarget::Cdouble, maxfun::Cint, npt::Cint, + iprint::Cint)::Cint +end + +function prima_newuoa(calfun, n, x, f, nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint) + @ccall libprimac.prima_newuoa(calfun::Ptr{Cvoid}, n::Cint, x::Ptr{Cdouble}, + f::Ptr{Cdouble}, nf::Ptr{Cint}, rhobeg::Cdouble, + rhoend::Cdouble, ftarget::Cdouble, maxfun::Cint, + npt::Cint, iprint::Cint)::Cint +end + +function prima_uobyqa(calfun, n, x, f, nf, rhobeg, rhoend, ftarget, maxfun, iprint) + @ccall libprimac.prima_uobyqa(calfun::Ptr{Cvoid}, n::Cint, x::Ptr{Cdouble}, + f::Ptr{Cdouble}, nf::Ptr{Cint}, rhobeg::Cdouble, + rhoend::Cdouble, ftarget::Cdouble, maxfun::Cint, + iprint::Cint)::Cint +end + +function prima_cobyla(m_nlcon, calcfc, n, x, f, cstrv, nlconstr, m_ineq, Aineq, bineq, m_eq, + Aeq, beq, xl, xu, nf, rhobeg, rhoend, ftarget, maxfun, iprint) + @ccall libprimac.prima_cobyla(m_nlcon::Cint, calcfc::Ptr{Cvoid}, n::Cint, + x::Ptr{Cdouble}, f::Ptr{Cdouble}, cstrv::Ptr{Cdouble}, + nlconstr::Ptr{Cdouble}, m_ineq::Cint, Aineq::Ptr{Cdouble}, + bineq::Ptr{Cdouble}, m_eq::Cint, Aeq::Ptr{Cdouble}, + beq::Ptr{Cdouble}, xl::Ptr{Cdouble}, xu::Ptr{Cdouble}, + nf::Ptr{Cint}, rhobeg::Cdouble, rhoend::Cdouble, + ftarget::Cdouble, maxfun::Cint, iprint::Cint)::Cint +end + +function prima_lincoa(calfun, n, x, f, cstrv, m_ineq, Aineq, bineq, m_eq, Aeq, beq, xl, xu, + nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint) + @ccall libprimac.prima_lincoa(calfun::Ptr{Cvoid}, n::Cint, x::Ptr{Cdouble}, + f::Ptr{Cdouble}, cstrv::Ptr{Cdouble}, m_ineq::Cint, + Aineq::Ptr{Cdouble}, bineq::Ptr{Cdouble}, m_eq::Cint, + Aeq::Ptr{Cdouble}, beq::Ptr{Cdouble}, xl::Ptr{Cdouble}, + xu::Ptr{Cdouble}, nf::Ptr{Cint}, rhobeg::Cdouble, + rhoend::Cdouble, ftarget::Cdouble, maxfun::Cint, + npt::Cint, iprint::Cint)::Cint +end