From ad3259464c6e4e206037bcac1930e909c4c95d88 Mon Sep 17 00:00:00 2001 From: odow Date: Wed, 16 Apr 2025 16:34:30 +1200 Subject: [PATCH 1/8] Improve code coverage --- src/MOI_wrapper.jl | 255 +++++++++++++++++++------------------------- test/MOI_wrapper.jl | 116 ++++++++++++++++++++ 2 files changed, 228 insertions(+), 143 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 63cc083..0999891 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -994,12 +994,19 @@ function MOI.add_constraint( _add_complementarity_constraint!( model.complementarity_cache, indv[1:n_comp], - indv[n_comp+1:end], + indv[(n_comp+1):end], comp_type, ) return MOI.ConstraintIndex{typeof(func),typeof(set)}(n_comp_cons) end +function MOI.get( + model::Optimizer, + ::MOI.NumberOfConstraints{MOI.VectorOfVariables,MOI.Complements}, +) + return model.complementarity_cache.n +end + # MOI.NLPBlock MOI.supports(::Optimizer, ::MOI.NLPBlock) = true @@ -1089,10 +1096,6 @@ function MOI.set( func::F, ) where {F<:Union{MOI.VariableIndex,MOI.ScalarAffineFunction,MOI.ScalarQuadraticFunction}} _throw_if_solved(model, attr) - if model.nlp_data !== nothing && model.nlp_data.has_objective - @warn("Objective is already specified in NLPBlockData.") - return - end _throw_if_not_valid(model, func) model.objective = func return @@ -1266,7 +1269,7 @@ function MOI.optimize!(model::Optimizer) evalResult.hess, evalRequest.x, evalRequest.sigma, - view(evalRequest.lambda, offset+1:num_cons), + view(evalRequest.lambda, (offset+1):num_cons), ) return 0 end @@ -1295,7 +1298,7 @@ function MOI.optimize!(model::Optimizer) evalRequest.x, evalRequest.vec, evalRequest.sigma, - view(evalRequest.lambda, offset+1:num_cons), + view(evalRequest.lambda, (offset+1):num_cons), ) return 0 end @@ -1321,145 +1324,135 @@ function MOI.get(model::Optimizer, ::MOI.RawStatusString) return string(statusP[]) end +function _interpret_return_code(code) + if code == KN_RC_OPTIMAL_OR_SATISFACTORY + return MOI.LOCALLY_SOLVED, MOI.FEASIBLE_POINT, MOI.FEASIBLE_POINT + elseif code == KN_RC_NEAR_OPT + return MOI.ALMOST_OPTIMAL, MOI.FEASIBLE_POINT, MOI.FEASIBLE_POINT + elseif -199 <= code <= -101 + return MOI.SLOW_PROGRESS, MOI.FEASIBLE_POINT, MOI.FEASIBLE_POINT + elseif -299 <= code <= -200 + return MOI.LOCALLY_INFEASIBLE, MOI.INFEASIBLE_POINT, MOI.UNKNOWN_RESULT_STATUS + elseif code == KN_RC_UNBOUNDED + elseif code == KN_RC_UNBOUNDED_OR_INFEAS + elseif code == KN_RC_ITER_LIMIT_FEAS + return MOI.ITERATION_LIMIT, MOI.FEASIBLE_POINT, MOI.UNKNOWN_RESULT_STATUS + elseif code == KN_RC_TIME_LIMIT_FEAS + return MOI.TIME_LIMIT, MOI.FEASIBLE_POINT, MOI.UNKNOWN_RESULT_STATUS + elseif -499 <= code <= -400 + end + return MOI.OTHER_ERROR, MOI.UNKNOWN_RESULT_STATUS, MOI.UNKNOWN_RESULT_STATUS +end + # Refer to KNITRO manual for solver status: # https://www.artelys.com/tools/knitro_doc/3_referenceManual/returnCodes.html#returncodes const _KN_TO_MOI_RETURN_STATUS = Dict{Int,MOI.TerminationStatusCode}( - 0 => MOI.LOCALLY_SOLVED, - -100 => MOI.ALMOST_OPTIMAL, - -101 => MOI.SLOW_PROGRESS, - -102 => MOI.SLOW_PROGRESS, - -103 => MOI.SLOW_PROGRESS, - -200 => MOI.LOCALLY_INFEASIBLE, - -201 => MOI.LOCALLY_INFEASIBLE, - -202 => MOI.LOCALLY_INFEASIBLE, - -203 => MOI.LOCALLY_INFEASIBLE, - -204 => MOI.LOCALLY_INFEASIBLE, - -205 => MOI.LOCALLY_INFEASIBLE, - -300 => MOI.DUAL_INFEASIBLE, - -301 => MOI.DUAL_INFEASIBLE, - -400 => MOI.ITERATION_LIMIT, - -401 => MOI.TIME_LIMIT, - -402 => MOI.OTHER_LIMIT, - -403 => MOI.OTHER_LIMIT, - -404 => MOI.OTHER_LIMIT, - -405 => MOI.OTHER_LIMIT, - -406 => MOI.NODE_LIMIT, - -410 => MOI.ITERATION_LIMIT, - -411 => MOI.TIME_LIMIT, - -412 => MOI.INFEASIBLE, - -413 => MOI.INFEASIBLE, - -414 => MOI.OTHER_LIMIT, - -415 => MOI.OTHER_LIMIT, - -416 => MOI.NODE_LIMIT, - -500 => MOI.INVALID_MODEL, - -501 => MOI.NUMERICAL_ERROR, - -502 => MOI.INVALID_MODEL, - -503 => MOI.MEMORY_LIMIT, - -504 => MOI.INTERRUPTED, - -505 => MOI.OTHER_ERROR, - -506 => MOI.OTHER_ERROR, - -507 => MOI.OTHER_ERROR, - -508 => MOI.OTHER_ERROR, - -509 => MOI.OTHER_ERROR, - -510 => MOI.OTHER_ERROR, - -511 => MOI.OTHER_ERROR, - -512 => MOI.OTHER_ERROR, - -513 => MOI.OTHER_ERROR, - -514 => MOI.OTHER_ERROR, - -515 => MOI.OTHER_ERROR, - -516 => MOI.OTHER_ERROR, - -517 => MOI.OTHER_ERROR, - -518 => MOI.OTHER_ERROR, - -519 => MOI.OTHER_ERROR, - -519 => MOI.OTHER_ERROR, - -520 => MOI.OTHER_ERROR, - -521 => MOI.OTHER_ERROR, - -522 => MOI.OTHER_ERROR, - -523 => MOI.OTHER_ERROR, - -524 => MOI.OTHER_ERROR, - -525 => MOI.OTHER_ERROR, - -526 => MOI.OTHER_ERROR, - -527 => MOI.OTHER_ERROR, - -528 => MOI.OTHER_ERROR, - -529 => MOI.OTHER_ERROR, - -530 => MOI.OTHER_ERROR, - -531 => MOI.OTHER_ERROR, - -532 => MOI.OTHER_ERROR, - -600 => MOI.OTHER_ERROR, + KN_RC_OPTIMAL_OR_SATISFACTORY => MOI.LOCALLY_SOLVED, + KN_RC_NEAR_OPT => MOI.ALMOST_OPTIMAL, + # slow progress + KN_RC_FEAS_XTOL => MOI.SLOW_PROGRESS, + KN_RC_FEAS_NO_IMPROVE => MOI.SLOW_PROGRESS, + KN_RC_FEAS_FTOL => MOI.SLOW_PROGRESS, + # infeasible + KN_RC_INFEASIBLE => MOI.LOCALLY_INFEASIBLE, + KN_RC_INFEAS_XTOL => MOI.LOCALLY_INFEASIBLE, + KN_RC_INFEAS_NO_IMPROVE => MOI.LOCALLY_INFEASIBLE, + KN_RC_INFEAS_MULTISTART => MOI.LOCALLY_INFEASIBLE, + KN_RC_INFEAS_CON_BOUNDS => MOI.LOCALLY_INFEASIBLE, + KN_RC_INFEAS_VAR_BOUNDS => MOI.LOCALLY_INFEASIBLE, + # unbounded + KN_RC_UNBOUNDED => MOI.DUAL_INFEASIBLE, + KN_RC_UNBOUNDED_OR_INFEAS => MOI.DUAL_INFEASIBLE, + # feasible limits + KN_RC_ITER_LIMIT_FEAS => MOI.ITERATION_LIMIT, + KN_RC_TIME_LIMIT_FEAS => MOI.TIME_LIMIT, + KN_RC_FEVAL_LIMIT_FEAS => MOI.OTHER_LIMIT, + KN_RC_MIP_EXH_FEAS => MOI.LOCALLY_SOLVED, + KN_RC_MIP_TERM_FEAS => MOI.SOLUTION_LIMIT, + KN_RC_MIP_SOLVE_LIMIT_FEAS => MOI.OTHER_LIMIT, + KN_RC_MIP_NODE_LIMIT_FEAS => MOI.NODE_LIMIT, + # infeasible limits + KN_RC_ITER_LIMIT_INFEAS => MOI.ITERATION_LIMIT, + KN_RC_TIME_LIMIT_INFEAS => MOI.TIME_LIMIT, + KN_RC_FEVAL_LIMIT_INFEAS => MOI.OTHER_LIMIT, + KN_RC_MIP_EXH_INFEAS => MOI.OTHER_LIMIT, + KN_RC_MIP_SOLVE_LIMIT_INFEAS => MOI.OTHER_LIMIT, + KN_RC_MIP_NODE_LIMIT_INFEAS => MOI.OTHER_LIMIT, + # errors + KN_RC_CALLBACK_ERR => MOI.INVALID_MODEL, + KN_RC_LP_SOLVER_ERR => MOI.NUMERICAL_ERROR, + KN_RC_EVAL_ERR => MOI.INVALID_MODEL, + KN_RC_OUT_OF_MEMORY => MOI.MEMORY_LIMIT, + KN_RC_USER_TERMINATION => MOI.INTERRUPTED, ) function MOI.get(model::Optimizer, ::MOI.TerminationStatus) if model.number_solved == 0 return MOI.OPTIMIZE_NOT_CALLED end - status, obj = Ref{Cint}(0), Ref{Cdouble}(0.0) - @_checked KN_get_solution(model.inner, status, obj, C_NULL, C_NULL) - return get(_KN_TO_MOI_RETURN_STATUS, status[], MOI.OTHER_ERROR) + statusP, obj = Ref{Cint}(0), Ref{Cdouble}(0.0) + @_checked KN_get_solution(model.inner, statusP, obj, C_NULL, C_NULL) + return get(_KN_TO_MOI_RETURN_STATUS, statusP[], MOI.OTHER_ERROR) end function MOI.get(model::Optimizer, ::MOI.ResultCount) return model.number_solved >= 1 ? 1 : 0 end -function MOI.get(model::Optimizer, attr::MOI.PrimalStatus) - if model.number_solved == 0 || attr.result_index != 1 - return MOI.NO_SOLUTION - end - statusP, obj = Ref{Cint}(0), Ref{Cdouble}(0.0) - @_checked KN_get_solution(model.inner, statusP, obj, C_NULL, C_NULL) - status = statusP[] +function _status_to_primal_status_code(status) if status == 0 return MOI.FEASIBLE_POINT - elseif -109 <= status <= -100 + elseif -199 <= status <= -100 return MOI.FEASIBLE_POINT - elseif -209 <= status <= -200 + elseif -299 <= status <= -200 return MOI.INFEASIBLE_POINT - # TODO(odow): we don't support returning certificates yet - # elseif status == -300 - # return MOI.INFEASIBILITY_CERTIFICATE + elseif -399 <= status <= -300 + return MOI.UNKNOWN_RESULT_STATUS elseif -409 <= status <= -400 return MOI.FEASIBLE_POINT - elseif -419 <= status <= -410 - return MOI.INFEASIBLE_POINT - elseif -599 <= status <= -500 - return MOI.UNKNOWN_RESULT_STATUS - else + elseif -499 <= status <= -410 return MOI.UNKNOWN_RESULT_STATUS end + @assert -599 <= status <= -500 + return MOI.UNKNOWN_RESULT_STATUS end -function MOI.get(model::Optimizer, attr::MOI.DualStatus) +function MOI.get(model::Optimizer, attr::MOI.PrimalStatus) if model.number_solved == 0 || attr.result_index != 1 return MOI.NO_SOLUTION end statusP, obj = Ref{Cint}(0), Ref{Cdouble}(0.0) @_checked KN_get_solution(model.inner, statusP, obj, C_NULL, C_NULL) - status = statusP[] + return _status_to_primal_status_code(statusP[]) +end + +function _status_to_dual_status_code(status) if status == 0 return MOI.FEASIBLE_POINT - elseif -109 <= status <= -100 - return MOI.FEASIBLE_POINT - # elseif -209 <= status <= -200 - # return MOI.INFEASIBILITY_CERTIFICATE - elseif status == -300 - return MOI.NO_SOLUTION - elseif -409 <= status <= -400 + elseif -199 <= status <= -100 return MOI.FEASIBLE_POINT - elseif -419 <= status <= -410 - return MOI.INFEASIBLE_POINT - elseif -599 <= status <= -500 + elseif -299 <= status <= -200 return MOI.UNKNOWN_RESULT_STATUS - else + elseif -399 <= status <= -300 + return MOI.UNKNOWN_RESULT_STATUS + elseif -499 <= status <= -400 return MOI.UNKNOWN_RESULT_STATUS end + @assert -599 <= status <= -500 + return MOI.UNKNOWN_RESULT_STATUS end -function MOI.get(model::Optimizer, obj::MOI.ObjectiveValue) - if model.number_solved == 0 - error("ObjectiveValue not available.") - elseif obj.result_index != 1 - throw(MOI.ResultIndexBoundsError{MOI.ObjectiveValue}(obj, 1)) +function MOI.get(model::Optimizer, attr::MOI.DualStatus) + if model.number_solved == 0 || attr.result_index != 1 + return MOI.NO_SOLUTION end + statusP, obj = Ref{Cint}(0), Ref{Cdouble}(0.0) + @_checked KN_get_solution(model.inner, statusP, obj, C_NULL, C_NULL) + return _status_to_dual_status_code(statusP[]) +end + +function MOI.get(model::Optimizer, obj::MOI.ObjectiveValue) + MOI.check_result_index_bounds(model, attr) status, obj = Ref{Cint}(0), Ref{Cdouble}(0.0) @_checked KN_get_solution(model.inner, status, obj, C_NULL, C_NULL) return obj[] @@ -1489,32 +1482,14 @@ function _get_dual(model::Optimizer, index::Integer) end function MOI.get(model::Optimizer, v::MOI.VariablePrimal, x::MOI.VariableIndex) - if model.number_solved == 0 - error("VariablePrimal not available.") - elseif v.result_index > 1 - throw(MOI.ResultIndexBoundsError{MOI.VariablePrimal}(v, 1)) - end + MOI.check_result_index_bounds(model, attr) MOI.throw_if_not_valid(model, x) return _get_solution(model, x.value) end -function _check_cons(model, ci, cp) - if model.number_solved == 0 - error("Solve problem before accessing solution.") - elseif cp.result_index > 1 - throw(MOI.ResultIndexBoundsError{typeof(cp)}(cp, 1)) - end - p = Ref{Cint}() - @_checked KN_get_number_cons(model.inner, p) - if !(0 <= ci.value <= p[] - 1) - error("Invalid constraint index ", ci.value) - end - return -end - function MOI.get( model::Optimizer, - cp::MOI.ConstraintPrimal, + attr::MOI.ConstraintPrimal, ci::MOI.ConstraintIndex{S,T}, ) where { S<:Union{MOI.ScalarAffineFunction{Float64},MOI.ScalarQuadraticFunction{Float64}}, @@ -1525,7 +1500,8 @@ function MOI.get( MOI.Interval{Float64}, }, } - _check_cons(model, ci, cp) + MOI.check_result_index_bounds(model, attr) + MOI.throw_if_not_valid(model, ci) indexCon = model.constraint_mapping[ci] p = Ref{Cdouble}(NaN) @_checked KN_get_con_value(model.inner, indexCon, p) @@ -1542,17 +1518,13 @@ end function MOI.get( model::Optimizer, - cp::MOI.ConstraintPrimal, + attr::MOI.ConstraintPrimal, ci::MOI.ConstraintIndex{ MOI.VariableIndex, <:Union{MOI.EqualTo{Float64},MOI.GreaterThan{Float64},MOI.LessThan{Float64}}, }, ) - if model.number_solved == 0 - error("ConstraintPrimal not available.") - elseif cp.result_index != 1 - throw(MOI.ResultIndexBoundsError{MOI.ConstraintPrimal}(cp, 1)) - end + MOI.check_result_index_bounds(model, attr) x = MOI.VariableIndex(ci.value) MOI.throw_if_not_valid(model, x) return _get_solution(model, x.value) @@ -1563,7 +1535,7 @@ _sense_dual(model::Optimizer) = model.sense == MOI.MAX_SENSE ? 1.0 : -1.0 function MOI.get( model::Optimizer, - cd::MOI.ConstraintDual, + attr::MOI.ConstraintDual, ci::MOI.ConstraintIndex{S,T}, ) where { S<:Union{MOI.ScalarAffineFunction{Float64},MOI.ScalarQuadraticFunction{Float64}}, @@ -1574,7 +1546,8 @@ function MOI.get( MOI.Interval{Float64}, }, } - _check_cons(model, ci, cd) + MOI.check_result_index_bounds(model, attr) + MOI.throw_if_not_valid(model, ci) index = model.constraint_mapping[ci] + 1 return _sense_dual(model) * _get_dual(model, index) end @@ -1600,11 +1573,7 @@ function _reduced_cost( attr::MOI.ConstraintDual, ci::MOI.ConstraintIndex{MOI.VariableIndex,S}, ) where {S} - if model.number_solved == 0 - error("ConstraintDual not available.") - elseif attr.result_index != 1 - throw(MOI.ResultIndexBoundsError{MOI.ConstraintDual}(attr, 1)) - end + MOI.check_result_index_bounds(model, attr) x = MOI.VariableIndex(ci.value) MOI.throw_if_not_valid(model, x) p = Ref{Cint}() @@ -1636,9 +1605,9 @@ function MOI.get( return _reduced_cost(model, attr, ci) end -function MOI.get(model::Optimizer, ::MOI.NLPBlockDual) +function MOI.get(model::Optimizer, attr::MOI.NLPBlockDual) if model.number_solved == 0 - error("NLPBlockDual not available.") + throw(MOI.ResultIndexBoundsError(attr, 0)) end return [_sense_dual(model) * _get_dual(model, i + 1) for i in model.nlp_index_cons] end diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 5b38aa2..6e4820a 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -21,6 +21,19 @@ function runtests() return end +function test_runtests() + model = MOI.instantiate(KNITRO.Optimizer) + config = MOI.Test.Config( + atol=1e-3, + rtol=1e-3, + optimal_status=MOI.LOCALLY_SOLVED, + infeasible_status=MOI.LOCALLY_INFEASIBLE, + exclude=Any[MOI.VariableBasisStatus, MOI.ConstraintBasisStatus], + ) + MOI.Test.runtests(model, config; include=["test_basic_"]) + return +end + function test_MOI_Test_cached() second_order_exclude = [ r"^test_conic_GeometricMeanCone_VectorAffineFunction$", @@ -142,6 +155,109 @@ function test_outname() return end +function test_objective_sense() + model = KNITRO.Optimizer() + @test MOI.supports(model, MOI.ObjectiveSense()) + for sense in (MOI.MIN_SENSE, MOI.MAX_SENSE) + MOI.set(model, MOI.ObjectiveSense(), sense) + @test MOI.get(model, MOI.ObjectiveSense()) == sense + end + return +end + +function test_get_objective_function() + model = KNITRO.Optimizer() + x = MOI.add_variable(model) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + for f in ( + x, + 1.0 * x + 2.0, + 1.0 * x * x + 2.0 * x + 3.0, + MOI.ScalarNonlinearFunction(:log, Any[x]), + ) + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + @test isapprox(MOI.get(model, MOI.ObjectiveFunction{typeof(f)}()), f) + end + return +end + +function test_status_to_primal_status_code() + for (status, code) in [ + 0 => MOI.FEASIBLE_POINT, + -100 => MOI.FEASIBLE_POINT, + -199 => MOI.FEASIBLE_POINT, + -200 => MOI.INFEASIBLE_POINT, + -299 => MOI.INFEASIBLE_POINT, + -300 => MOI.UNKNOWN_RESULT_STATUS, + -301 => MOI.UNKNOWN_RESULT_STATUS, + -399 => MOI.UNKNOWN_RESULT_STATUS, + -400 => MOI.FEASIBLE_POINT, + -409 => MOI.FEASIBLE_POINT, + -410 => MOI.UNKNOWN_RESULT_STATUS, + -499 => MOI.UNKNOWN_RESULT_STATUS, + -500 => MOI.UNKNOWN_RESULT_STATUS, + -599 => MOI.UNKNOWN_RESULT_STATUS, + ] + @test KNITRO._status_to_primal_status_code(status) == code + end + for status in [1, 100, 200, 300, 400] + @test_throws AssertionError KNITRO._status_to_primal_status_code(status) + end + return +end + +function test_status_to_dual_status_code() + for (status, code) in [ + 0 => MOI.FEASIBLE_POINT, + -100 => MOI.FEASIBLE_POINT, + -199 => MOI.FEASIBLE_POINT, + -200 => MOI.UNKNOWN_RESULT_STATUS, + -299 => MOI.UNKNOWN_RESULT_STATUS, + -300 => MOI.UNKNOWN_RESULT_STATUS, + -301 => MOI.UNKNOWN_RESULT_STATUS, + -399 => MOI.UNKNOWN_RESULT_STATUS, + -400 => MOI.UNKNOWN_RESULT_STATUS, + -409 => MOI.UNKNOWN_RESULT_STATUS, + -410 => MOI.UNKNOWN_RESULT_STATUS, + -499 => MOI.UNKNOWN_RESULT_STATUS, + -500 => MOI.UNKNOWN_RESULT_STATUS, + -599 => MOI.UNKNOWN_RESULT_STATUS, + ] + @test KNITRO._status_to_dual_status_code(status) == code + end + for status in [1, 100, 200, 300, 400] + @test_throws AssertionError KNITRO._status_to_dual_status_code(status) + end + return +end + +function test_NLPBlockDual_error() + model = KNITRO.Optimizer() + @test_throws( + MOI.ResultIndexBoundsError(MOI.NLPBlockDual(), 0), + MOI.get(model, MOI.NLPBlockDual()), + ) + return +end + +function test_NodeCount() + model = KNITRO.Optimizer() + @test MOI.get(model, MOI.NodeCount()) === Int64(0) + return +end + +function test_BarrierIterations() + model = KNITRO.Optimizer() + @test MOI.get(model, MOI.BarrierIterations()) === Int64(0) + return +end + +function test_RelativeGap() + model = KNITRO.Optimizer() + @test MOI.get(model, MOI.RelativeGap()) === 0.0 + return +end + end TestMOIWrapper.runtests() From f2aa4c1e6c462a4ced734939fa96fe616a653be1 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 17 Apr 2025 15:31:24 +1200 Subject: [PATCH 2/8] Fix --- src/MOI_wrapper.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 0999891..29cea8e 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -1451,7 +1451,7 @@ function MOI.get(model::Optimizer, attr::MOI.DualStatus) return _status_to_dual_status_code(statusP[]) end -function MOI.get(model::Optimizer, obj::MOI.ObjectiveValue) +function MOI.get(model::Optimizer, attr::MOI.ObjectiveValue) MOI.check_result_index_bounds(model, attr) status, obj = Ref{Cint}(0), Ref{Cdouble}(0.0) @_checked KN_get_solution(model.inner, status, obj, C_NULL, C_NULL) From c9db2a31b43a1d24c1b9f2c4b47b3c4c412af6b6 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 17 Apr 2025 15:32:44 +1200 Subject: [PATCH 3/8] Update --- src/MOI_wrapper.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 29cea8e..e9f9c13 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -1481,7 +1481,7 @@ function _get_dual(model::Optimizer, index::Integer) return model.lambda[index] end -function MOI.get(model::Optimizer, v::MOI.VariablePrimal, x::MOI.VariableIndex) +function MOI.get(model::Optimizer, attr::MOI.VariablePrimal, x::MOI.VariableIndex) MOI.check_result_index_bounds(model, attr) MOI.throw_if_not_valid(model, x) return _get_solution(model, x.value) From 84d617f1385af991cce4e99e489525233af6ceec Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 17 Apr 2025 16:06:01 +1200 Subject: [PATCH 4/8] Update --- src/MOI_wrapper.jl | 85 +++++++++++++++++++++++++++++---------------- test/MOI_wrapper.jl | 2 +- 2 files changed, 56 insertions(+), 31 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index e9f9c13..f71d2ad 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -121,9 +121,6 @@ mutable struct Optimizer <: MOI.AbstractOptimizer MOI.ScalarNonlinearFunction, Nothing, } - # Constraint counters. - number_zeroone_constraints::Int - number_integer_constraints::Int # Complementarity cache complementarity_cache::_ComplementarityCache # Constraint mappings. @@ -155,8 +152,6 @@ function Optimizer(; license_manager::Union{LMcontext,Nothing}=nothing, kwargs.. Cint[], MOI.FEASIBILITY_SENSE, nothing, - 0, - 0, _ComplementarityCache(), Dict{MOI.ConstraintIndex,Union{Cint,Vector{Cint}}}(), license_manager, @@ -194,8 +189,6 @@ function MOI.empty!(model::Optimizer) MOI.empty!(model.nlp_model) model.sense = MOI.FEASIBILITY_SENSE model.objective = nothing - model.number_zeroone_constraints = 0 - model.number_integer_constraints = 0 model.complementarity_cache = _ComplementarityCache() model.constraint_mapping = Dict() model.license_manager = model.license_manager @@ -215,8 +208,6 @@ function MOI.is_empty(model::Optimizer) model.sense == MOI.FEASIBILITY_SENSE && model.number_solved == 0 && isa(model.objective, Nothing) && - model.number_zeroone_constraints == 0 && - model.number_integer_constraints == 0 && !_has_complementarity(model.complementarity_cache) && !model.nlp_loaded end @@ -346,6 +337,8 @@ function MOI.set(model::Optimizer, attr::MOI.UserDefinedFunction, args) return end +# Variables + MOI.get(model::Optimizer, ::MOI.NumberOfVariables) = length(model.variable_info) function MOI.get(model::Optimizer, ::MOI.ListOfVariableIndices) @@ -447,12 +440,28 @@ end # MOI.NumberOfConstraints -function MOI.get(model::Optimizer, ::MOI.NumberOfConstraints{MOI.VariableIndex,MOI.ZeroOne}) - return model.number_zeroone_constraints +_get_F_S(::MOI.ConstraintIndex{F,S}) where {F,S} = (F, S) + +function MOI.get(model::Optimizer, ::MOI.ListOfConstraintTypesPresent) + ret = Tuple{Type,Type}[] + for k in keys(model.constraint_mapping) + F, S = _get_F_S(k) + if !((F, S) in ret) + push!(ret, (F, S)) + end + end + return ret end -function MOI.get(model::Optimizer, ::MOI.NumberOfConstraints{MOI.VariableIndex,MOI.Integer}) - return model.number_integer_constraints +function MOI.get(model::Optimizer, ::MOI.ListOfConstraintIndices{F,S}) where {F,S} + ret = MOI.ConstraintIndex{F,S}[] + for k in keys(model.constraint_mapping) + if k isa MOI.ConstraintIndex{F,S} + push!(ret, k) + end + end + sort!(ret; by=x -> x.value) + return ret end function MOI.get(model::Optimizer, ::MOI.NumberOfConstraints{F,S}) where {F,S} @@ -460,6 +469,10 @@ function MOI.get(model::Optimizer, ::MOI.NumberOfConstraints{F,S}) where {F,S} return count(f, keys(model.constraint_mapping); init=0) end +function MOI.is_valid(model::Optimizer, ci::MOI.ConstraintIndex) + return haskey(model.constraint_mapping, ci) +end + ### ### MOI.VariableIndex -in- LessThan ### @@ -468,7 +481,11 @@ function MOI.is_valid( model::Optimizer, ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}, ) - return model.variable_info[ci.value].has_upper_bound + info = get(model.variable_info, ci.value, nothing) + if info === nothing + return false + end + return info.has_upper_bound end function MOI.add_constraint( @@ -503,7 +520,11 @@ function MOI.is_valid( model::Optimizer, ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}, ) - return model.variable_info[ci.value].has_lower_bound + info = get(model.variable_info, ci.value, nothing) + if info === nothing + return false + end + return info.has_lower_bound end function MOI.add_constraint( @@ -538,8 +559,11 @@ function MOI.is_valid( model::Optimizer, ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{Float64}}, ) - return model.variable_info[ci.value].has_lower_bound && - model.variable_info[ci.value].has_upper_bound + info = get(model.variable_info, ci.value, nothing) + if info === nothing + return false + end + return info.has_lower_bound && info.has_upper_bound end function MOI.add_constraint( @@ -577,7 +601,11 @@ function MOI.is_valid( model::Optimizer, ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{Float64}}, ) - return model.variable_info[ci.value].is_fixed + info = get(model.variable_info, ci.value, nothing) + if info === nothing + return false + end + return info.is_fixed end function MOI.add_constraint( @@ -642,7 +670,6 @@ end function MOI.add_constraint(model::Optimizer, x::MOI.VariableIndex, ::MOI.ZeroOne) MOI.throw_if_not_valid(model, x) - model.number_zeroone_constraints += 1 lb, ub = nothing, nothing p = Ref{Cdouble}(NaN) if model.variable_info[x.value].has_lower_bound @@ -662,7 +689,9 @@ function MOI.add_constraint(model::Optimizer, x::MOI.VariableIndex, ::MOI.ZeroOn if ub !== nothing @_checked KN_set_var_upbnd(model.inner, _c_column(x), ub) end - return MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}(x.value) + ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.ZeroOne}(x.value) + model.constraint_mapping[ci] = convert(Cint, x.value) + return ci end ### @@ -672,9 +701,10 @@ end function MOI.add_constraint(model::Optimizer, x::MOI.VariableIndex, set::MOI.Integer) _throw_if_solved(model, x, set) MOI.throw_if_not_valid(model, x) - model.number_integer_constraints += 1 @_checked KN_set_var_type(model.inner, _c_column(x), KN_VARTYPE_INTEGER) - return MOI.ConstraintIndex{MOI.VariableIndex,MOI.Integer}(x.value) + ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.Integer}(x.value) + model.constraint_mapping[ci] = convert(Cint, x.value) + return ci end ### @@ -997,14 +1027,9 @@ function MOI.add_constraint( indv[(n_comp+1):end], comp_type, ) - return MOI.ConstraintIndex{typeof(func),typeof(set)}(n_comp_cons) -end - -function MOI.get( - model::Optimizer, - ::MOI.NumberOfConstraints{MOI.VectorOfVariables,MOI.Complements}, -) - return model.complementarity_cache.n + ci = MOI.ConstraintIndex{typeof(func),typeof(set)}(n_comp_cons) + model.constraint_mapping[ci] = convert(Cint, n_comp_cons) + return ci end # MOI.NLPBlock diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 6e4820a..1ad4714 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -28,7 +28,7 @@ function test_runtests() rtol=1e-3, optimal_status=MOI.LOCALLY_SOLVED, infeasible_status=MOI.LOCALLY_INFEASIBLE, - exclude=Any[MOI.VariableBasisStatus, MOI.ConstraintBasisStatus], + exclude=Any[MOI.VariableBasisStatus, MOI.ConstraintBasisStatus, MOI.ConstraintName], ) MOI.Test.runtests(model, config; include=["test_basic_"]) return From 94edfcd670cf501a7e8d1684935b9ab86c85cb9b Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 17 Apr 2025 16:48:36 +1200 Subject: [PATCH 5/8] Update --- src/MOI_wrapper.jl | 101 ++++++++++++++------------------------------ test/MOI_wrapper.jl | 90 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 69 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index f71d2ad..d9de1e6 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -84,13 +84,7 @@ function _add_complementarity_constraint!( index_vars_2::Vector{Cint}, cc_types::Vector{Int}, ) - if !(length(index_vars_1) == length(index_vars_2) == length(cc_types)) - error( - "Arrays `index_vars_1`, `index_vars_2` and `cc_types` should" * - " share the same length to specify a valid complementarity " * - "constraint.", - ) - end + @assert length(index_vars_1) == length(index_vars_2) == length(cc_types) cache.n += 1 append!(cache.index_comps_1, index_vars_1) append!(cache.index_comps_2, index_vars_2) @@ -284,7 +278,7 @@ end # MOI.RawOptimizerAttribute function MOI.supports(model::Optimizer, attr::MOI.RawOptimizerAttribute) - if attr.name == "free" + if attr.name in ("option_file", "tuner_file", "free") return true end p = Ref{Cint}(0) @@ -304,10 +298,13 @@ end function MOI.set(model::Optimizer, attr::MOI.RawOptimizerAttribute, value) if attr.name == "option_file" @_checked KN_load_param_file(model.inner, value) + return elseif attr.name == "tuner_file" @_checked KN_load_tuner_file(model.inner, value) + return elseif attr.name == "free" @_checked KN_free(model.inner) + return end pId = Ref{Cint}(0) ret = KN_get_param_id(model.inner, attr.name, pId) @@ -495,9 +492,6 @@ function MOI.add_constraint( ) _throw_if_solved(model, x, set) MOI.throw_if_not_valid(model, x) - if isnan(set.upper) - error("Invalid upper bound value $(set.upper).") - end if _has_upper_bound(model, x) error("Upper bound on variable $x already exists.") end @@ -534,9 +528,6 @@ function MOI.add_constraint( ) _throw_if_solved(model, x, set) MOI.throw_if_not_valid(model, x) - if isnan(set.lower) - error("Invalid lower bound value $(set.lower).") - end if _has_lower_bound(model, x) error("Lower bound on variable $x already exists.") end @@ -573,9 +564,6 @@ function MOI.add_constraint( ) _throw_if_solved(model, x, set) MOI.throw_if_not_valid(model, x) - if isnan(set.lower) || isnan(set.upper) - error("Invalid lower bound value $(set.lower).") - end if _has_lower_bound(model, x) || _has_upper_bound(model, x) error("Bounds on variable $x already exists.") end @@ -615,9 +603,6 @@ function MOI.add_constraint( ) _throw_if_solved(model, x, set) MOI.throw_if_not_valid(model, x) - if isnan(set.value) - error("Invalid fixed value $(set.value).") - end if _has_lower_bound(model, x) error("Variable $x has a lower bound. Cannot be fixed.") end @@ -642,11 +627,8 @@ end function MOI.supports( ::Optimizer, ::MOI.ConstraintDualStart, - ::MOI.ConstraintIndex{ - MOI.VariableIndex, - <:Union{MOI.EqualTo{Float64},MOI.GreaterThan{Float64},MOI.LessThan{Float64}}, - }, -) + ::Type{MOI.ConstraintIndex{MOI.VariableIndex,S}}, +) where {S<:Union{MOI.EqualTo{Float64},MOI.GreaterThan{Float64},MOI.LessThan{Float64}}} return true end @@ -659,8 +641,9 @@ function MOI.set( }, value::Union{Real,Nothing}, ) - start = something(value, 0.0) - @_checked KN_set_var_dual_init_values(model.inner, ci.value, Cdouble(start)) + start = convert(Cdouble, something(value, 0.0)) + indexVars = [_c_column(MOI.VariableIndex(ci.value))] + @_checked KN_set_var_dual_init_values(model.inner, 1, indexVars, [start]) return end @@ -756,16 +739,15 @@ end function MOI.supports( ::Optimizer, ::MOI.ConstraintDualStart, - ::MOI.ConstraintIndex{ - MOI.ScalarAffineFunction{Float64}, - <:Union{ - MOI.EqualTo{Float64}, - MOI.GreaterThan{Float64}, - MOI.LessThan{Float64}, - MOI.Interval{Float64}, - }, + ::Type{MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},S}}, +) where { + S<:Union{ + MOI.EqualTo{Float64}, + MOI.GreaterThan{Float64}, + MOI.LessThan{Float64}, + MOI.Interval{Float64}, }, -) +} return true end @@ -783,8 +765,9 @@ function MOI.set( }, value::Union{Real,Nothing}, ) - start = something(value, 0.0) - @_checked KN_set_con_dual_init_values(model.inner, ci.value, Cdouble(start)) + start = convert(Cdouble, something(value, 0.0)) + indexCons = KNINT[ci.value] + @_checked KN_set_con_dual_init_values(model.inner, 1, indexCons, [start]) return end @@ -838,16 +821,15 @@ end function MOI.supports( ::Optimizer, ::MOI.ConstraintDualStart, - ::MOI.ConstraintIndex{ - MOI.ScalarQuadraticFunction{Float64}, - <:Union{ - MOI.EqualTo{Float64}, - MOI.GreaterThan{Float64}, - MOI.LessThan{Float64}, - MOI.Interval{Float64}, - }, + ::Type{MOI.ConstraintIndex{MOI.ScalarQuadraticFunction{Float64},S}}, +) where { + S<:Union{ + MOI.EqualTo{Float64}, + MOI.GreaterThan{Float64}, + MOI.LessThan{Float64}, + MOI.Interval{Float64}, }, -) +} return true end @@ -865,8 +847,9 @@ function MOI.set( }, value::Union{Real,Nothing}, ) - start = something(value, 0.0) - @_checked KN_set_con_dual_init_values(model.inner, ci.value, Cdouble(start)) + start = convert(Cdouble, something(value, 0.0)) + indexCons = KNINT[ci.value] + @_checked KN_set_con_dual_init_values(model.inner, 1, indexCons, [start]) return end @@ -1349,26 +1332,6 @@ function MOI.get(model::Optimizer, ::MOI.RawStatusString) return string(statusP[]) end -function _interpret_return_code(code) - if code == KN_RC_OPTIMAL_OR_SATISFACTORY - return MOI.LOCALLY_SOLVED, MOI.FEASIBLE_POINT, MOI.FEASIBLE_POINT - elseif code == KN_RC_NEAR_OPT - return MOI.ALMOST_OPTIMAL, MOI.FEASIBLE_POINT, MOI.FEASIBLE_POINT - elseif -199 <= code <= -101 - return MOI.SLOW_PROGRESS, MOI.FEASIBLE_POINT, MOI.FEASIBLE_POINT - elseif -299 <= code <= -200 - return MOI.LOCALLY_INFEASIBLE, MOI.INFEASIBLE_POINT, MOI.UNKNOWN_RESULT_STATUS - elseif code == KN_RC_UNBOUNDED - elseif code == KN_RC_UNBOUNDED_OR_INFEAS - elseif code == KN_RC_ITER_LIMIT_FEAS - return MOI.ITERATION_LIMIT, MOI.FEASIBLE_POINT, MOI.UNKNOWN_RESULT_STATUS - elseif code == KN_RC_TIME_LIMIT_FEAS - return MOI.TIME_LIMIT, MOI.FEASIBLE_POINT, MOI.UNKNOWN_RESULT_STATUS - elseif -499 <= code <= -400 - end - return MOI.OTHER_ERROR, MOI.UNKNOWN_RESULT_STATUS, MOI.UNKNOWN_RESULT_STATUS -end - # Refer to KNITRO manual for solver status: # https://www.artelys.com/tools/knitro_doc/3_referenceManual/returnCodes.html#returncodes const _KN_TO_MOI_RETURN_STATUS = Dict{Int,MOI.TerminationStatusCode}( diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 1ad4714..dcaf6e7 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -258,6 +258,96 @@ function test_RelativeGap() return end +function test_NumberOfVariales() + model = KNITRO.Optimizer() + @test MOI.get(model, MOI.NumberOfVariables()) == 0 + x = MOI.add_variable(model) + @test MOI.get(model, MOI.NumberOfVariables()) == 1 + y = MOI.add_variables(model, 2) + @test MOI.get(model, MOI.NumberOfVariables()) == 3 + return +end + +function test_RawOptimizerParameter_free() + model = KNITRO.Optimizer() + @test MOI.supports(model, MOI.RawOptimizerAttribute("free")) + @test model.inner.env.ptr_env != C_NULL + MOI.set(model, MOI.RawOptimizerAttribute("free"), true) + @test model.inner.env.ptr_env == C_NULL + return +end + +function test_RawOptimizerParameter_option_file() + model = KNITRO.Optimizer() + @test MOI.supports(model, MOI.RawOptimizerAttribute("option_file")) + dir = mktempdir() + filename = joinpath(dir, "option_file") + write(filename, "outlev 1") + MOI.set(model, MOI.RawOptimizerAttribute("option_file"), filename) + valueP = Ref{Cint}() + KN_get_int_param(model.inner, KN_PARAM_OUTLEV, valueP) + @test valueP[] == 1 + return +end + +function test_RawOptimizerParameter_tuner_file() + model = KNITRO.Optimizer() + @test MOI.supports(model, MOI.RawOptimizerAttribute("tuner_file")) + dir = mktempdir() + filename = joinpath(dir, "tuner_file") + write(filename, "algorithm") + MOI.set(model, MOI.RawOptimizerAttribute("tuner_file"), filename) + valueP = Ref{Cint}() + KN_get_int_param(model.inner, KN_PARAM_OUTLEV, valueP) + @test valueP[] == 1 + return +end + +function test_VariableName() + model = KNITRO.Optimizer() + x = MOI.add_variable(model) + @test MOI.supports(model, MOI.VariableName(), MOI.VariableIndex) + @test MOI.get(model, MOI.VariableName(), x) == "" + MOI.set(model, MOI.VariableName(), x, "x") + @test MOI.get(model, MOI.VariableName(), x) == "x" + return +end + +function test_ConstraintDualStart() + model = KNITRO.Optimizer() + x = MOI.add_variable(model) + for f in (x, 1.0 * x, 1.0 * x * x) + c = MOI.add_constraint(model, f, MOI.LessThan(1.0)) + @test MOI.supports(model, MOI.ConstraintDualStart(), typeof(c)) + # Just test that this doesn't error. + MOI.set(model, MOI.ConstraintDualStart(), c, nothing) + MOI.set(model, MOI.ConstraintDualStart(), c, 1.0) + end + return +end + +function test_error_kwargs() + @test_throws( + ErrorException( + "Unsupported keyword arguments passed to `Optimizer`. Set attributes instead", + ), + KNITRO.Optimizer(; outlev = 1), + ) + return +end + +function test_lm_context() + lm = KNITRO.LMcontext() + @test isempty(lm.linked_models) + model = KNITRO.Optimizer(; license_manager = lm) + @test length(lm.linked_models) == 1 + @test model.inner in lm.linked_models + MOI.empty!(model) + @test length(lm.linked_models) == 2 + @test model.inner in lm.linked_models + return +end + end TestMOIWrapper.runtests() From 1d677eaf5d065b734d98b990b33422799cf0b543 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 17 Apr 2025 16:52:22 +1200 Subject: [PATCH 6/8] Fix formatting --- test/MOI_wrapper.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index dcaf6e7..b6922f0 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -331,7 +331,7 @@ function test_error_kwargs() ErrorException( "Unsupported keyword arguments passed to `Optimizer`. Set attributes instead", ), - KNITRO.Optimizer(; outlev = 1), + KNITRO.Optimizer(; outlev=1), ) return end @@ -339,7 +339,7 @@ end function test_lm_context() lm = KNITRO.LMcontext() @test isempty(lm.linked_models) - model = KNITRO.Optimizer(; license_manager = lm) + model = KNITRO.Optimizer(; license_manager=lm) @test length(lm.linked_models) == 1 @test model.inner in lm.linked_models MOI.empty!(model) From 3930e2b8651b85a006d7d1c533f80b0794bbe303 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 17 Apr 2025 17:11:54 +1200 Subject: [PATCH 7/8] Update --- test/MOI_wrapper.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index b6922f0..c2aaa29 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -79,6 +79,8 @@ function test_MOI_Test_cached() r"^test_solve_ObjectiveBound_MAX_SENSE_LP$", # KNITRO doesn't support INFEASIBILITY_CERTIFICATE results. r"^test_solve_DualStatus_INFEASIBILITY_CERTIFICATE_$", + # Cannot get ConstraintDualStart + r"^test_model_ModelFilter_AbstractConstraintAttribute$", # ConstraintDual not supported for SecondOrderCone second_order_exclude..., ], @@ -285,7 +287,7 @@ function test_RawOptimizerParameter_option_file() write(filename, "outlev 1") MOI.set(model, MOI.RawOptimizerAttribute("option_file"), filename) valueP = Ref{Cint}() - KN_get_int_param(model.inner, KN_PARAM_OUTLEV, valueP) + KNITRO.KN_get_int_param(model.inner, KN_PARAM_OUTLEV, valueP) @test valueP[] == 1 return end @@ -298,7 +300,7 @@ function test_RawOptimizerParameter_tuner_file() write(filename, "algorithm") MOI.set(model, MOI.RawOptimizerAttribute("tuner_file"), filename) valueP = Ref{Cint}() - KN_get_int_param(model.inner, KN_PARAM_OUTLEV, valueP) + KNITRO.KN_get_int_param(model.inner, KN_PARAM_OUTLEV, valueP) @test valueP[] == 1 return end From c440e44974101dd273f643196f0bd6a766260b04 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 17 Apr 2025 17:31:14 +1200 Subject: [PATCH 8/8] Update --- test/MOI_wrapper.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index c2aaa29..99c1e34 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -287,7 +287,7 @@ function test_RawOptimizerParameter_option_file() write(filename, "outlev 1") MOI.set(model, MOI.RawOptimizerAttribute("option_file"), filename) valueP = Ref{Cint}() - KNITRO.KN_get_int_param(model.inner, KN_PARAM_OUTLEV, valueP) + KNITRO.KN_get_int_param(model.inner, KNITRO.KN_PARAM_OUTLEV, valueP) @test valueP[] == 1 return end @@ -299,9 +299,6 @@ function test_RawOptimizerParameter_tuner_file() filename = joinpath(dir, "tuner_file") write(filename, "algorithm") MOI.set(model, MOI.RawOptimizerAttribute("tuner_file"), filename) - valueP = Ref{Cint}() - KNITRO.KN_get_int_param(model.inner, KN_PARAM_OUTLEV, valueP) - @test valueP[] == 1 return end