Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 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
32 changes: 29 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,42 @@ permissions:
jobs:
test:
name: 'KNITRO'
runs-on: 'ubuntu-latest'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version: ['1.10', '1']
include:
# Julia v1.10, Linux, KNITRO@15
- version: '1.10'
os: 'ubuntu-latest'
arch: 'x64'
KNITRO_JL_WHL: 'true'
# Julia v1, Linux, KNITRO@15
- version: '1'
os: 'ubuntu-latest'
arch: 'x64'
KNITRO_JL_WHL: 'true'
# Julia v1, macOS-aarch64, KNITRO@15
- version: '1'
os: macos-14
arch: aarch64
KNITRO_JL_WHL: 'true'
# Julia v1, Windows, KNITRO@15
- version: '1'
os: 'windows-latest'
arch: 'x64'
KNITRO_JL_WHL: 'true'
# Julia v1, Linux, KNITRO@14
- version: '1'
os: 'ubuntu-latest'
arch: 'x64'
KNITRO_JL_WHL: 'false'
steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: 'x64'
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v1
- shell: bash
env:
Expand All @@ -30,6 +55,7 @@ jobs:
echo "$SECRET_KNITRO_LICENSE" > ~/artelys_lic.txt
- uses: julia-actions/julia-buildpkg@v1
env:
KNITRO_JL_WHL: ${{ matrix.KNITRO_JL_WHL }}
SECRET_KNITRO_URL: ${{ secrets.KNITRO_URL }}
SECRET_KNITRO_LIBIOMP5: ${{ secrets.KNITRO_LIBIOMP5 }}
- uses: julia-actions/julia-runtest@v1
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ deps/lib*
deps/artelys*
deps/build.log
test/*.mps
deps/knitro*
38 changes: 34 additions & 4 deletions deps/build.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
using Libdl, Base.Sys
# Copyright (c) 2016: Ng Yee Sian, Miles Lubin, other 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.

import Libdl

const DEPS_FILE = joinpath(dirname(@__FILE__), "deps.jl")

Expand Down Expand Up @@ -41,16 +46,41 @@ function try_local_installation()
return
end

function try_ci_installation()
const WHEELS = Dict(
"linux" => "https://files.pythonhosted.org/packages/76/6e/ffe880b013ad244f0fd91940454e4f2bf16fa01e74c469e1b0fb75eda12a/knitro-15.0.0-py3-none-manylinux1_x86_64.whl",
"macos" => "https://files.pythonhosted.org/packages/76/66/936edcd2255055cf6c8a060d1beeb8ce543ef078dceb631caad22a20002a/knitro-15.0.0-py3-none-macosx_13_0_arm64.whl",
"windows" => "https://files.pythonhosted.org/packages/13/3f/54953373ee3b631640b33b5d4bdb0217bdb1f8514b9374b08348e098ea2a/knitro-15.0.0-py3-none-win_amd64.whl",
)

function try_wheel_installation()
libname, url = if Sys.islinux()
"libknitro.so", WHEELS["linux"]
elseif Sys.iswindows()
"knitro.dll", WHEELS["windows"]
elseif Sys.isapple()
"libknitro.dylib", WHEELS["macos"]
end
if !isdir(joinpath(@__DIR__, "knitro"))
run(`curl --output knitro.whl $url`)
run(`unzip knitro.whl`)
end
filename = joinpath(@__DIR__, "knitro", "lib", libname)
write_depsfile("", filename)
return
end

function try_secret_installation()
local_filename = joinpath(@__DIR__, "libknitro.so")
download(ENV["SECRET_KNITRO_URL"], local_filename)
download(ENV["SECRET_KNITRO_LIBIOMP5"], joinpath(@__DIR__, "libiomp5.so"))
write_depsfile("", local_filename)
return
end

if get(ENV, "SECRET_KNITRO_URL", "") != ""
try_ci_installation()
if get(ENV, "KNITRO_JL_WHL", "false") == "true"
try_wheel_installation()
elseif get(ENV, "SECRET_KNITRO_URL", "") != ""
try_secret_installation()
else
try_local_installation()
end
72 changes: 39 additions & 33 deletions ext/KNITROMathOptInterfaceExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
constraint_mapping::Dict{MOI.ConstraintIndex,Union{Cint,Vector{Cint}}}
license_manager::Union{KNITRO.LMcontext,Nothing}
options::Dict{String,Any}
binary_variables::Vector{MOI.VariableIndex}
# Cache for the solution
x::Vector{Float64}
lambda::Vector{Float64}
Expand Down Expand Up @@ -160,6 +161,7 @@
Dict{MOI.ConstraintIndex,Union{Cint,Vector{Cint}}}(),
license_manager,
Dict{String,Any}(),
MOI.VariableIndex[],
Float64[],
Float64[],
nothing,
Expand Down Expand Up @@ -201,6 +203,7 @@
end
empty!(model.x)
empty!(model.lambda)
empty!(model.binary_variables)
MOI.set(model, MOI.TimeLimitSec(), model.time_limit_sec)
return
end
Expand Down Expand Up @@ -276,20 +279,28 @@
model.time_limit_sec = value
# By default, maxtime is set to 1e8 in Knitro.
limit = something(value, 1e8)
# KNITRO does not have a single option to control the global time limit, so
# we set various options.
# MAXTIME_REAL is the base option, which applies if the problem is a NLP.
KNITRO.@_checked KNITRO.KN_set_double_param(
model.inner,
KNITRO.KN_PARAM_MAXTIMEREAL,
limit,
)
# MIP_MAXTIME_REAL applies if the problem is a MINLP
KNITRO.@_checked KNITRO.KN_set_double_param(
model.inner,
KNITRO.KN_PARAM_MIP_MAXTIMEREAL,
limit,
)
if KNITRO.knitro_version() >= v"15.0"
KNITRO.@_checked KNITRO.KN_set_double_param(
model.inner,
KNITRO.KN_PARAM_MAXTIME,
limit,
)
else
# KNITRO does not have a single option to control the global time limit, so
# we set various options.
# MAXTIME_REAL is the base option, which applies if the problem is a NLP.
KNITRO.@_checked KNITRO.KN_set_double_param(

Check warning on line 292 in ext/KNITROMathOptInterfaceExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/KNITROMathOptInterfaceExt.jl#L292

Added line #L292 was not covered by tests
model.inner,
KNITRO.KN_PARAM_MAXTIMEREAL,
limit,
)
# MIP_MAXTIME_REAL applies if the problem is a MINLP
KNITRO.@_checked KNITRO.KN_set_double_param(

Check warning on line 298 in ext/KNITROMathOptInterfaceExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/KNITROMathOptInterfaceExt.jl#L298

Added line #L298 was not covered by tests
model.inner,
KNITRO.KN_PARAM_MIP_MAXTIMEREAL,
limit,
)
end
return
end

Expand Down Expand Up @@ -675,29 +686,12 @@

function MOI.add_constraint(model::Optimizer, x::MOI.VariableIndex, ::MOI.ZeroOne)
MOI.throw_if_not_valid(model, x)
lb, ub = nothing, nothing
p = Ref{Cdouble}(NaN)
if model.variable_info[x.value].has_lower_bound
KNITRO.@_checked KNITRO.KN_get_var_lobnd(model.inner, _c_column(x), p)
lb = max(0.0, p[])
end
if model.variable_info[x.value].has_upper_bound
KNITRO.@_checked KNITRO.KN_get_var_upbnd(model.inner, _c_column(x), p)
ub = min(1.0, p[])
end
KNITRO.@_checked KNITRO.KN_set_var_type(
model.inner,
_c_column(x),
KNITRO.KN_VARTYPE_BINARY,
KNITRO.KN_VARTYPE_INTEGER,
)
# Calling `set_var_type` resets variable bounds in KNITRO. To fix, we need
# to restore them after calling `set_var_type`.
if lb !== nothing
KNITRO.@_checked KNITRO.KN_set_var_lobnd(model.inner, _c_column(x), lb)
end
if ub !== nothing
KNITRO.@_checked KNITRO.KN_set_var_upbnd(model.inner, _c_column(x), ub)
end
push!(model.binary_variables, x)
ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}(x.value)
model.constraint_mapping[ci] = convert(Cint, x.value)
return ci
Expand Down Expand Up @@ -1398,7 +1392,19 @@
!isa(model.objective, MOI.ScalarNonlinearFunction)
_add_objective(model, model.objective)
end
# Update binary variable bounds
binary_cols = _c_column.(model.binary_variables)
nV = length(binary_cols)
lb, ub, tmp = zeros(Cdouble, nV), zeros(Cdouble, nV), zeros(Cdouble, nV)
KNITRO.@_checked KNITRO.KN_get_var_lobnds(model.inner, nV, binary_cols, lb)
KNITRO.@_checked KNITRO.KN_get_var_upbnds(model.inner, nV, binary_cols, ub)
tmp .= max.(lb, 0.0)
KNITRO.@_checked KNITRO.KN_set_var_lobnds(model.inner, nV, binary_cols, tmp)
tmp .= min.(ub, 1.0)
KNITRO.@_checked KNITRO.KN_set_var_upbnds(model.inner, nV, binary_cols, tmp)
KNITRO.KN_solve(model.inner)
KNITRO.@_checked KNITRO.KN_set_var_lobnds(model.inner, nV, binary_cols, lb)
KNITRO.@_checked KNITRO.KN_set_var_upbnds(model.inner, nV, binary_cols, ub)
model.number_solved += 1
return
end
Expand Down
23 changes: 12 additions & 11 deletions test/C_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,15 @@ end
_to_string(x) = GC.@preserve(x, unsafe_string(pointer(x)))
@test _to_string(tmp) == "xtol"
KN_get_param_doc(kc, KN_PARAM_XTOL, tmp, 1024)
header = KNITRO.knitro_version() >= v"15" ? "" : "# "
@test _to_string(tmp) ==
"# Step size tolerance used for terminating the optimization.\n"
"$(header)Step size tolerance used for terminating the optimization.\n"
KN_get_param_type(kc, KN_PARAM_XTOL, pCint)
@test pCint[] == KN_PARAMTYPE_FLOAT
KN_get_num_param_values(kc, KN_PARAM_XTOL, pCint)
@test pCint[] == 0
KN_get_param_value_doc(kc, KN_PARAM_GRADOPT, 1, tmp, 1024)
@test _to_string(tmp) == "exact"
@test occursin("exact", _to_string(tmp))
KN_get_param_id(kc, "xtol", pCint)
@test pCint[] == KN_PARAM_XTOL

Expand Down Expand Up @@ -276,20 +277,20 @@ end
@test pCint[] >= 0
pCdouble = Ref{Cdouble}()
KN_get_abs_feas_error(kc, pCdouble)
@test pCdouble[] < 1e-10
@test pCdouble[] < 1e-6
KN_get_rel_feas_error(kc, pCdouble)
@test pCdouble[] < 1e-10
@test pCdouble[] < 1e-6
KN_get_abs_opt_error(kc, pCdouble)
@test pCdouble[] < 1e-7
@test pCdouble[] < 1e-4
KN_get_rel_opt_error(kc, pCdouble)
@test pCdouble[] < 1e-8
@test pCdouble[] < 1e-6
KN_get_con_value(kc, 0, pCdouble)
@test pCdouble[]3.96
@test ≈(pCdouble[], 3.96; atol=1e-4)
nStatus, objSol, x, lambda_ = KN_get_solution(kc)
@test nStatus == 0
@test x ≈ [0.0, 2.0, 1.98]
@test ≈(x, [0.0, 2.0, 1.98]; atol=1e-4)

@test objSol ≈ 31.363199 atol = 1e-5
@test objSol ≈ 31.363 atol = 1e-3

# Test getters for primal and dual variables
if KNITRO.knitro_version() >= v"12.0"
Expand Down Expand Up @@ -499,7 +500,7 @@ end
KN_set_obj_name(kc, "myobj")
# Set feasibility tolerances
KN_set_var_feastols_all(kc, [0.1, 0.001, 0.1])
KN_set_con_feastols_all(kc, [0.1])
KN_set_con_feastols_all(kc, [1e-4])
KN_set_compcon_feastols_all(kc, [0.1])
# Set finite differences step size
KN_set_cb_relstepsizes_all(kc, cb, [0.1, 0.001, 0.1])
Expand Down Expand Up @@ -880,7 +881,7 @@ end
for x in xIndices
KN_set_var_primal_init_value(kc, x, 0.8)
end

KN_set_var_lobnd(kc, 2, 0.0)
# Add the constraints and set the rhs and coefficients
KN_add_cons(kc, 3, zeros(Cint, 3))
KN_set_con_eqbnds_all(kc, [1.0, 0.0, 0.0])
Expand Down
Loading
Loading