diff --git a/Project.toml b/Project.toml index 0feab9d2..af33f4fc 100644 --- a/Project.toml +++ b/Project.toml @@ -7,17 +7,23 @@ version = "0.16.2" [deps] Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] +Downloads = "<0.0.1, 1" +Libdl = "<0.0.1, 1.6" +LinearAlgebra = "<0.0.1, 1.6" MathOptInterface = "1" +Random = "<0.0.1, 1.6" +SparseArrays = "<0.0.1, 1.6" +Test = "<0.0.1, 1.6" julia = "1.6" [extras] +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "Random"] +test = ["LinearAlgebra", "Random", "Test"] diff --git a/src/MOI/MOI_wrapper.jl b/src/MOI/MOI_wrapper.jl index 513a7ead..7a079325 100644 --- a/src/MOI/MOI_wrapper.jl +++ b/src/MOI/MOI_wrapper.jl @@ -3,15 +3,6 @@ # 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. -@static if v"1.2" > VERSION >= v"1.1" - # see: https://github.com/jump-dev/Xpress.jl/pull/44#issuecomment-585882858 - error("Versions 1.1.x of julia are not supported. The current verions is $(VERSION)") -end - -import MathOptInterface -using SparseArrays - -const MOI = MathOptInterface const CleverDicts = MOI.Utilities.CleverDicts @enum( @@ -161,7 +152,7 @@ mutable struct BasisStatus var_status::Vector{Cint} end -mutable struct SensitivityCache +mutable struct SensitivityCache input::Vector{Float64} output::Vector{Float64} is_updated::Bool @@ -240,7 +231,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer # TODO: add functionality to the lower-level API to support querying single # elements of the solution. - + cached_solution::Union{Nothing, CachedSolution} basis_status::Union{Nothing, BasisStatus} conflict::Union{Nothing, IISData} @@ -391,7 +382,7 @@ function MOI.is_empty(model::Optimizer) model.termination_status != MOI.OPTIMIZE_NOT_CALLED && return false model.primal_status != MOI.NO_SOLUTION && return false model.dual_status != MOI.NO_SOLUTION && return false - + model.callback_cached_solution !== nothing && return false # model.cb_cut_data !== nothing && return false model.callback_state != CB_NONE && return false @@ -816,7 +807,7 @@ function MOI.add_variable(model::Optimizer) 0,#length(_dmatval)::Int, Ref(0.0),#_dobj::Vector{Float64}, C_NULL,#Cint.(_mrwind::Vector{Int}), - C_NULL,#Cint.(_mrstart::Vector{Int}), + C_NULL,#Cint.(_mrstart::Vector{Int}), C_NULL,#_dmatval::Vector{Float64}, Ref(-Inf),#_dbdl::Vector{Float64}, Ref(Inf),#_dbdu::Vector{Float64} @@ -831,7 +822,7 @@ function MOI.add_variables(model::Optimizer, N::Int) 0,#length(_dmatval)::Int, zeros(N),# _dobj::Vector{Float64}, C_NULL,#Cint.(_mrwind::Vector{Int}), - C_NULL,#Cint.(_mrstart::Vector{Int}), + C_NULL,#Cint.(_mrstart::Vector{Int}), C_NULL,# _dmatval::Vector{Float64}, fill(-Inf, N),# _dbdl::Vector{Float64}, fill(Inf, N),# _dbdu::Vector{Float64} @@ -1005,7 +996,7 @@ function MOI.set( rows = @_invoke Lib.XPRSgetintattrib(model.inner, Lib.XPRS_ROWS, _)::Int cols = @_invoke Lib.XPRSgetintattrib(model.inner, Lib.XPRS_COLS, _)::Int if model.forward_sensitivity_cache === nothing - model.forward_sensitivity_cache = + model.forward_sensitivity_cache = SensitivityCache( zeros(rows), zeros(cols), @@ -1023,10 +1014,10 @@ function MOI.get(model::Optimizer, ::ForwardSensitivityOutputVariable, vi::MOI.V if is_mip(model) && model.moi_warnings @warn "The problem is a MIP, it might fail to get correct sensitivities." end - if MOI.get(model, MOI.TerminationStatus()) != MOI.OPTIMAL + if MOI.get(model, MOI.TerminationStatus()) != MOI.OPTIMAL error("Model not optimized. Cannot get sensitivities.") end - if model.forward_sensitivity_cache === nothing + if model.forward_sensitivity_cache === nothing error("Forward sensitivity cache not initiliazed correctly.") end if model.forward_sensitivity_cache.is_updated != true @@ -1042,7 +1033,7 @@ function MOI.set( rows = @_invoke Lib.XPRSgetintattrib(model.inner, Lib.XPRS_ROWS, _)::Int cols = @_invoke Lib.XPRSgetintattrib(model.inner, Lib.XPRS_COLS, _)::Int if model.backward_sensitivity_cache === nothing - model.backward_sensitivity_cache = + model.backward_sensitivity_cache = SensitivityCache( zeros(cols), zeros(rows), @@ -1060,10 +1051,10 @@ function MOI.get(model::Optimizer, ::BackwardSensitivityOutputConstraint, ci::MO if is_mip(model) && model.moi_warnings @warn "The problem is a MIP, it might fail to get correct sensitivities." end - if MOI.get(model, MOI.TerminationStatus()) != MOI.OPTIMAL + if MOI.get(model, MOI.TerminationStatus()) != MOI.OPTIMAL error("Model not optimized. Cannot get sensitivities.") end - if model.backward_sensitivity_cache === nothing + if model.backward_sensitivity_cache === nothing error("Backward sensitivity cache not initiliazed correctly.") end if model.backward_sensitivity_cache.is_updated != true @@ -1623,7 +1614,7 @@ or semi-integer lower bound and to change the lower bound. function _set_variable_semi_lower_bound(model, info, value) info.semi_lower_bound = value _set_variable_lower_bound(model, info, 0.0) - @checked Lib.XPRSchgglblimit(model.inner, Cint(1), Ref(Cint(info.column-1)), + @checked Lib.XPRSchgglblimit(model.inner, Cint(1), Ref(Cint(info.column-1)), Ref(Cdouble(value))) end @@ -2045,8 +2036,8 @@ function MOI.add_constraint( Ref{UInt8}(sense),#_srowtype, Ref(rhs),#_drhs, C_NULL,#_drng, - Ref{Cint}(0),#Cint.(_mrstart::Vector{Int}), - indices.-= 1,#Cint.(_mclind::Vector{Int}), + Ref{Cint}(0),#Cint.(_mrstart::Vector{Int}), + indices.-= 1,#Cint.(_mclind::Vector{Int}), coefficients#_dmatval ) return MOI.ConstraintIndex{typeof(f), typeof(s)}(model.last_constraint_index) @@ -2090,7 +2081,7 @@ function MOI.add_constraints( model.affine_constraint_info[model.last_constraint_index] = ConstraintInfo(length(model.affine_constraint_info) + 1, si, AFFINE) end - pop!(row_starts) + pop!(row_starts) @checked Lib.XPRSaddrows( model.inner, length(rhss),#length(_drhs), @@ -2098,8 +2089,8 @@ function MOI.add_constraints( senses,#_srowtype, rhss,#_drhs, C_NULL,#_drng, - row_starts.-= 1,#Cint.(_mrstart::Vector{Int}), - columns.-= 1,#Cint.(_mclind::Vector{Int}), + row_starts.-= 1,#Cint.(_mrstart::Vector{Int}), + columns.-= 1,#Cint.(_mclind::Vector{Int}), coefficients#_dmatval ) return indices @@ -2147,7 +2138,7 @@ function MOI.set( @checked Lib.XPRSchgrhs( model.inner, Cint(1), - Ref(Cint(_info(model, c).row - 1)), + Ref(Cint(_info(model, c).row - 1)), Ref(MOI.constant(s))) return end @@ -2157,7 +2148,7 @@ function _get_affine_terms(model::Optimizer, c::MOI.ConstraintIndex) nzcnt_max = Xpress.n_non_zero_elements(model.inner) _nzcnt = Ref(Cint(0)) - @checked Lib.XPRSgetrows(model.inner, C_NULL, C_NULL, C_NULL, 0, _nzcnt, + @checked Lib.XPRSgetrows(model.inner, C_NULL, C_NULL, C_NULL, 0, _nzcnt, Cint(row-1), Cint(row-1)) nzcnt = _nzcnt[] @@ -2392,8 +2383,8 @@ function MOI.add_constraint( Ref{UInt8}(sense),#_srowtype, Ref(rhs-cte),#_drhs, C_NULL,#_drng, - Ref{Cint}(0),#Cint.(_mrstart::Vector{Int}), - indices.-= 1,#Cint.(_mrwind::Vector{Int}), + Ref{Cint}(0),#Cint.(_mrstart::Vector{Int}), + indices.-= 1,#Cint.(_mrwind::Vector{Int}), coefficients#_dmatval ) @checked Lib.XPRSsetindicators(model.inner, @@ -2494,7 +2485,7 @@ function MOI.get( @checked Lib.XPRSgetqrowqmatrixtriplets(model.inner, _info(model, c).row-1, nqelem, mqcol1, mqcol2, dqe) mqcol1 .+= 1 mqcol2 .+= 1 - + quadratic_terms = MOI.ScalarQuadraticTerm{Float64}[] for (i, j, coef) in zip(mqcol1, mqcol2, dqe) push!( @@ -2617,7 +2608,7 @@ function _get_sparse_sos(model) V[j] = setvals[j] end end - return sparse(I, J, V, nsets[], n) + return SparseArrays.sparse(I, J, V, nsets[], n) end function MOI.get( @@ -2799,7 +2790,7 @@ function MOI.optimize!(model::Optimizer) has_Ray = Int64[0] @checked Lib.XPRSgetprimalray(model.inner, model.cached_solution.variable_primal, has_Ray) model.cached_solution.has_primal_certificate = _has_primal_ray(model) - elseif status == MOI.FEASIBLE_POINT + elseif status == MOI.FEASIBLE_POINT model.cached_solution.has_feasible_point = true end status = MOI.get(model, MOI.DualStatus()) @@ -3021,7 +3012,7 @@ function _cache_dual_status(model) end end return MOI.NO_SOLUTION - + end function MOI.get(model::Optimizer, attr::MOI.DualStatus) @@ -3554,7 +3545,7 @@ function _replace_with_matching_sparsity!( for term in replacement.terms col = _info(model, term.variable).column @checked Lib.XPRSchgcoef( - model.inner, Cint(row-1), Cint(col-1), + model.inner, Cint(row-1), Cint(col-1), MOI.coefficient(term) ) end @@ -3587,14 +3578,14 @@ function _replace_with_different_sparsity!( # First, zero out the old constraint function terms. for term in previous.terms col = _info(model, term.variable).column - @checked Lib.XPRSchgcoef(model.inner, Cint(row - 1), Cint(col - 1), + @checked Lib.XPRSchgcoef(model.inner, Cint(row - 1), Cint(col - 1), 0.0) end # Next, set the new constraint function terms. for term in previous.terms col = _info(model, term.variable).column - @checked Lib.XPRSchgcoef(model.inner, Cint(row - 1), Cint(col - 1), + @checked Lib.XPRSchgcoef(model.inner, Cint(row - 1), Cint(col - 1), MOI.coefficient(term)) end return @@ -4432,4 +4423,4 @@ function MOI.get( MOI.check_result_index_bounds(model, attr) column = _info(model, vi).column return model.cached_solution.variable_dual[column] -end \ No newline at end of file +end diff --git a/src/Xpress.jl b/src/Xpress.jl index 0f152b6d..35f32f61 100644 --- a/src/Xpress.jl +++ b/src/Xpress.jl @@ -3,71 +3,64 @@ # 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. -__precompile__() - module Xpress - using Libdl - - # Load in `deps.jl`, complaining if it does not exist - const depsjl_path = joinpath(@__DIR__, "..", "deps", "deps.jl") - - if isfile(depsjl_path) - include(depsjl_path) - elseif !haskey(ENV, "XPRESS_JL_NO_DEPS_ERROR") - error("XPRESS cannot be loaded. Please run Pkg.build(\"Xpress\").") - else - const xpressdlpath = "" - end - - const libxprs = joinpath(xpressdlpath, string(Sys.iswindows() ? "" : "lib", "xprs", ".", Libdl.dlext)) - - ### imports - - import Base.show, Base.copy +import Libdl +import MathOptInterface as MOI +import SparseArrays - # ### exports +# Load in `deps.jl`, complaining if it does not exist +const depsjl_path = joinpath(@__DIR__, "..", "deps", "deps.jl") - ### include source files - module Lib - import ..Xpress - const libxprs = Xpress.libxprs - include("ctypes.jl") - include("common.jl") - include("lib.jl") - end +if isfile(depsjl_path) + include(depsjl_path) +elseif !haskey(ENV, "XPRESS_JL_NO_DEPS_ERROR") + error("XPRESS cannot be loaded. Please run Pkg.build(\"Xpress\").") +else + const xpressdlpath = "" +end - include("utils.jl") - include("helper.jl") - include("attributes_controls.jl") - include("api.jl") - include("xprs_callbacks.jl") - include("license.jl") +const libxprs = joinpath(xpressdlpath, string(Sys.iswindows() ? "" : "lib", "xprs", ".", Libdl.dlext)) - const XPRS_ATTRIBUTES = Dict{String, Any}( - replace(string(name), "XPRS_"=>"") => getfield(Lib, name) - for name in names(Lib; all = true) - if startswith(string(name), "XPRS_") && all(isuppercase(c) || isdigit(c) for c in string(name) if c != '_') - ) +module Lib + import ..Xpress + const libxprs = Xpress.libxprs + include("ctypes.jl") + include("common.jl") + include("lib.jl") +end - function initialize() - Libdl.dlopen(libxprs) - userlic() - init() # Call XPRSinit for initialization - # free is not strictly necessary since destroyprob is called - # inthe finalizer. - # the user can call free if needed. - # leaving it uncommented results in many print errors becaus - # it is called prior to finalizers. - # atexit(free) # Call free when process terminates - end +include("utils.jl") +include("helper.jl") +include("attributes_controls.jl") +include("api.jl") +include("xprs_callbacks.jl") +include("license.jl") + +const XPRS_ATTRIBUTES = Dict{String, Any}( + replace(string(name), "XPRS_"=>"") => getfield(Lib, name) + for name in names(Lib; all = true) + if startswith(string(name), "XPRS_") && all(isuppercase(c) || isdigit(c) for c in string(name) if c != '_') +) + +function initialize() + Libdl.dlopen(libxprs) + userlic() + init() # Call XPRSinit for initialization + # free is not strictly necessary since destroyprob is called + # inthe finalizer. + # the user can call free if needed. + # leaving it uncommented results in many print errors becaus + # it is called prior to finalizers. + # atexit(free) # Call free when process terminates +end - include("MOI/MOI_wrapper.jl") +include("MOI/MOI_wrapper.jl") - function __init__() - if !haskey(ENV, "XPRESS_JL_NO_AUTO_INIT") && get(ENV, "JULIA_REGISTRYCI_AUTOMERGE", "false") != "true" - Xpress.initialize() - end +function __init__() + if !haskey(ENV, "XPRESS_JL_NO_AUTO_INIT") && get(ENV, "JULIA_REGISTRYCI_AUTOMERGE", "false") != "true" + Xpress.initialize() end - end + +end # Xpress diff --git a/test/MathOptInterface/MOI_wrapper.jl b/test/MathOptInterface/MOI_wrapper.jl index 78ba3e87..f2e2fc6a 100644 --- a/test/MathOptInterface/MOI_wrapper.jl +++ b/test/MathOptInterface/MOI_wrapper.jl @@ -492,7 +492,7 @@ function test_Delete_equality_constraint_in_binary_variable() @test MOI.get(model, MOI.ObjectiveBound()) >= 19.4 - atol z_value = MOI.get(model, MOI.VariablePrimal(), v)[3] - + # Relax binary bounds of z variable MOI.delete(model, vc4) @@ -795,7 +795,7 @@ function test_name_constraints() # create problem variables, constraints, optimizer = infeasible_problem(); - + # name variables and constraints MOI.set(optimizer, MOI.VariableName(), variables, ["x1", "x2"]); MOI.set(optimizer, MOI.ConstraintName(), constraints[1], "constraint1"); @@ -825,7 +825,7 @@ function test_name_constraints_with_the_same_name() # create problem variables, constraints, optimizer = infeasible_problem(); - + # name variables and constraints MOI.set(optimizer, MOI.VariableName(), variables, ["x1", "x2"]); MOI.set(optimizer, MOI.ConstraintName(), constraints[1], "same"); @@ -852,7 +852,7 @@ function test_name_variables_with_the_same_name() # create problem variables, constraints, optimizer = infeasible_problem(); - + # name variables and constraints MOI.set(optimizer, MOI.VariableName(), variables, ["x1", "x1"]); MOI.set(optimizer, MOI.ConstraintName(), constraints[1], "constraint1"); @@ -956,13 +956,13 @@ function test_dummy_nlp() djs = Array{Float64}(undef, 2) ret = Xpress.Lib.XPRSgetnlpsol(model.inner, xx, slack, duals, djs) @test ret == 0 - @test xx == [3.14, 10] + @test xx == [3.14, 10] @test slack == [0, 0] ret = Xpress.Lib.XPRSnlpchgformulastring(model.inner, Cint(0), "- 0.5 * x1 - 3") @test ret == 0 - # to optimize NLPs we need: XPRSoptimize + # to optimize NLPs we need: XPRSoptimize solvestatus = Ref{Cint}(0) solstatus = Ref{Cint}(0) ret = Xpress.Lib.XPRSoptimize(model.inner, "", solvestatus, solstatus) @@ -975,13 +975,13 @@ function test_dummy_nlp() djs = Array{Float64}(undef, 2) ret = Xpress.Lib.XPRSgetnlpsol(model.inner, xx, slack, duals, djs) @test ret == 0 - @test xx == [6, 10] + @test xx == [6, 10] @test slack == [0, 0] return nothing end -function test_multiple_modifications() +function test_multiple_modifications2() model = Xpress.Optimizer(OUTPUTLOG = 0) x = MOI.add_variable(model)