diff --git a/REQUIRE b/REQUIRE index 86a3cb02..7ce4d6d9 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,4 +1,4 @@ julia 0.6 MathProgBase 0.5 0.8 Compat 0.18 -LinQuadOptInterface 0.4 0.5 +LinQuadOptInterface 0.6 0.7 diff --git a/src/CPLEX.jl b/src/CPLEX.jl index c4ee742d..038ede29 100644 --- a/src/CPLEX.jl +++ b/src/CPLEX.jl @@ -116,5 +116,5 @@ module CPLEX include("cpx_highlevel.jl") include("CplexSolverInterface.jl") - include("MOIWrapper.jl") + include("MOI_wrapper.jl") end diff --git a/src/MOIWrapper.jl b/src/MOIWrapper.jl deleted file mode 100644 index a58f062b..00000000 --- a/src/MOIWrapper.jl +++ /dev/null @@ -1,270 +0,0 @@ - -using LinQuadOptInterface -const LQOI = LinQuadOptInterface -const MOI = LQOI.MOI - -const SUPPORTED_OBJECTIVES = [ - LQOI.Linear - LQOI.SinVar -] - -const SUPPORTED_CONSTRAINTS = [ - (LQOI.Linear, LQOI.EQ), - (LQOI.Linear, LQOI.LE), - (LQOI.Linear, LQOI.GE), - (LQOI.SinVar, LQOI.EQ), - (LQOI.SinVar, LQOI.LE), - (LQOI.SinVar, LQOI.GE), - (LQOI.SinVar, LQOI.IV), - (LQOI.SinVar, MOI.ZeroOne), - (LQOI.SinVar, MOI.Integer), - (LQOI.VecVar, MOI.Nonnegatives), - (LQOI.VecVar, MOI.Nonpositives), - (LQOI.VecVar, MOI.Zeros), - (LQOI.VecLin, MOI.Nonnegatives), - (LQOI.VecLin, MOI.Nonpositives), - (LQOI.VecLin, MOI.Zeros) -] - -mutable struct Optimizer <: LQOI.LinQuadOptimizer - LQOI.@LinQuadOptimizerBase - env::Env # Cplex environment - Optimizer(::Nothing) = new() -end - -function LQOI.LinearQuadraticModel(::Type{Optimizer},env) - env = Env() - return Model(env::Env) -end - -function Optimizer(;kwargs...) - env = Env() - for (name,value) in kwargs - set_param!(env, string(name), value) - end - model = Optimizer(nothing) - model.env = env - MOI.empty!(model) - return model -end - -LQOI.supported_constraints(::Optimizer) = SUPPORTED_CONSTRAINTS -LQOI.supported_objectives(::Optimizer) = SUPPORTED_OBJECTIVES - -LQOI.backend_type(model::Optimizer, ::MOI.EqualTo{Float64}) = Cchar('E') -LQOI.backend_type(model::Optimizer, ::MOI.LessThan{Float64}) = Cchar('L') -LQOI.backend_type(model::Optimizer, ::MOI.GreaterThan{Float64}) = Cchar('G') - -LQOI.backend_type(model::Optimizer, ::MOI.Zeros) = Cchar('B') -LQOI.backend_type(model::Optimizer, ::MOI.Nonpositives) = Cchar('L') -LQOI.backend_type(model::Optimizer, ::MOI.Nonnegatives) = Cchar('E') - -# TODO - improve single type -function LQOI.change_variable_bounds!(model::Optimizer, - columns::Vector{Int}, values::Vector{Float64}, senses::Vector{Cchar}) - - c_api_chgbds(model.inner, ivec(columns), senses, values) -end - -function LQOI.get_variable_lowerbound(model::Optimizer, column::Int) - c_api_getlb(model.inner, Cint(column), Cint(column))[1] -end - -function LQOI.get_variable_upperbound(model::Optimizer, column::Int) - c_api_getub(model.inner, Cint(column), Cint(column))[1] -end - -function LQOI.get_number_linear_constraints(model::Optimizer) - c_api_getnumrows(model.inner) -end - -function LQOI.add_linear_constraints!(model::Optimizer, - A::LQOI.CSRMatrix{Float64}, sense::Vector{Cchar}, rhs::Vector{Float64}) - - c_api_addrows(model.inner, ivec(A.row_pointers), ivec(A.columns), - A.coefficients, sense, rhs) -end - -function LQOI.get_rhs(model::Optimizer, row::Int) - rhs = Vector{Cdouble}(undef, 1) - c_api_getrhs(model.inner, rhs, Cint(row), Cint(row)) - return rhs[1] -end - -function LQOI.get_linear_constraint(model::Optimizer, row::Int) - (nzcnt, rmatbeg, rmatind, rmatval) = - c_api_getrows(model.inner, Cint(row), Cint(row)) - return rmatind[1:nzcnt], rmatval[1:nzcnt] -end - -function LQOI.change_matrix_coefficient!(model::Optimizer, - row::Int, col::Int, coef::Float64) - - c_api_chgcoef(model.inner, Cint(row), Cint(col), coef) -end - -function LQOI.change_objective_coefficient!(model::Optimizer, col::Int, - coef::Float64) - - c_api_chgobj(model.inner, [Cint(col)], [coef]) -end - -function LQOI.change_rhs_coefficient!(model::Optimizer, row::Int, - coef::Float64) - - c_api_chgrhs(model.inner, [Cint(row)], [coef]) -end - -function LQOI.delete_linear_constraints!(model::Optimizer, - first_row::Int, last_row::Int) - - c_api_delrows(model.inner, Cint(first_row), Cint(last_row)) -end - -function LQOI.change_variable_types!(model::Optimizer, - columns::Vector{Int}, vtypes::Vector{Cchar}) - - c_api_chgctype(model.inner, ivec(columns), vtypes) -end - -function LQOI.change_linear_constraint_sense!(model::Optimizer, - rows::Vector{Int}, senses::Vector{Cchar}) - - c_api_chgsense(model.inner, ivec(rows), senses) -end - -function LQOI.set_linear_objective!(model::Optimizer, - columns::Vector{Int}, coefficients::Vector{Float64}) - - n = num_var(model.inner) - all_coefs = zeros(Float64, n) - for (col, coef) in zip(columns, coefficients) - all_coefs[col] += coef - end - c_api_chgobj(model.inner, Cint[1:n;], all_coefs) -end - -function LQOI.change_objective_sense!(model::Optimizer, symbol) - if symbol == :min - c_api_chgobjsen(model.inner, Cint(1)) - else - c_api_chgobjsen(model.inner, Cint(-1)) - end -end - -function LQOI.get_linear_objective!(model::Optimizer, x) - c_api_getobj(model.inner, x, Cint(1), c_api_getnumcols(model.inner)) -end - -function LQOI.get_objectivesense(model::Optimizer) - s = c_api_getobjsen(model.inner) - if s == 1 - return MOI.MinSense - else - return MOI.MaxSense - end -end - -function LQOI.get_number_variables(model::Optimizer) - return c_api_getnumcols(model.inner) -end - -function LQOI.add_variables!(model::Optimizer, N::Int) - add_vars!(model.inner, zeros(Float64, N), fill(-Inf, N), fill(Inf, N)) -end - -function LQOI.delete_variables!(model::Optimizer, - first_col::Int, last_col::Int) - - c_api_delcols(model.inner, Cint(first_col), Cint(last_col)) -end - -function LQOI.solve_mip_problem!(model::Optimizer) - model.inner.has_int = true - LQOI.solve_linear_problem!(model) -end - -function LQOI.solve_linear_problem!(model::Optimizer) - optimize!(model.inner) -end - -function LQOI.get_termination_status(model::Optimizer) - stat = c_api_getstat(model.inner) - - if stat == 1 # CPX_STAT_OPTIMAL - return MOI.Success - elseif stat == 3 # CPX_STAT_INFEASIBLE - return MOI.InfeasibleNoResult - elseif stat == 4 # CPX_STAT_INForUNBD - return MOI.InfeasibleOrUnbounded - elseif stat == 2 # CPX_STAT_UNBOUNDED - return MOI.UnboundedNoResult - elseif stat in [12, 21, 22, 36] # CPX_STAT_*ABORT*_OBJ_LIM - return MOI.ObjectiveLimit - elseif stat in [10,34] # CPX_STAT_*ABORT_IT_LIM - return MOI.IterationLimit - elseif stat == 53 # CPX_STAT_CONFLICT_ABORT_NODE_LIM - return MOI.NodeLimit - elseif stat in [11, 25, 33, 39] # CPX_STAT_*ABORT*TIME_LIM - return MOI.TimeLimit - elseif stat == 5 # CPX_STAT_OPTIMAL_INFEAS - return MOI.NumericalError - - # MIP STATUS - elseif stat in [101, 102] # CPXMIP_OPTIMAL, CPXMIP_OPTIMAL_TOL - return MOI.Success - elseif stat == 103 # CPXMIP_INFEASIBLE - return MOI.InfeasibleNoResult - elseif stat == 119 # CPXMIP_INForUNBD - return MOI.InfeasibleOrUnbounded - elseif stat == 118 # CPXMIP_UNBOUNDED - return MOI.UnboundedNoResult - elseif stat == [105, 106] # CPXMIP_NODE_LIM* - return MOI.NodeLimit - elseif stat in [107, 108, 131, 132] # CPXMIP_*TIME_LIM* - return MOI.TimeLimit - else - return MOI.OtherError - end -end - -function LQOI.get_primal_status(model::Optimizer) - stat = c_api_solninfo(model.inner)[3] - if stat == 1 - return MOI.FeasiblePoint - else - return MOI.UnknownResultStatus - end -end - -function LQOI.get_dual_status(model::Optimizer) - if model.inner.has_int - return MOI.UnknownResultStatus - end - stat = c_api_solninfo(model.inner)[4] - if stat == 1 - return MOI.FeasiblePoint - else - return MOI.UnknownResultStatus - end -end - -function LQOI.get_variable_primal_solution!(model::Optimizer, result) - return c_api_getx(model.inner, result) -end - -function LQOI.get_linear_primal_solution!(model::Optimizer, result) - return c_api_getax(model.inner, result) -end - -function LQOI.get_variable_dual_solution!(model::Optimizer, place) - return c_api_getdj(model.inner, place) -end - -function LQOI.get_linear_dual_solution!(model::Optimizer, place) - return c_api_getpi(model.inner, place) -end - -function LQOI.get_objective_value(model::Optimizer) - return c_api_getobjval(model.inner) -end \ No newline at end of file diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl new file mode 100644 index 00000000..7a70579b --- /dev/null +++ b/src/MOI_wrapper.jl @@ -0,0 +1,316 @@ +using LinQuadOptInterface + +const LQOI = LinQuadOptInterface +const MOI = LQOI.MathOptInterface + +const SUPPORTED_OBJECTIVES = [ + LQOI.Linear + LQOI.SinVar +] + +const SUPPORTED_CONSTRAINTS = [ + (LQOI.Linear, LQOI.EQ), + (LQOI.Linear, LQOI.LE), + (LQOI.Linear, LQOI.GE), + (LQOI.SinVar, LQOI.EQ), + (LQOI.SinVar, LQOI.LE), + (LQOI.SinVar, LQOI.GE), + (LQOI.SinVar, LQOI.IV), + (LQOI.SinVar, MOI.ZeroOne), + (LQOI.SinVar, MOI.Integer), + (LQOI.VecVar, MOI.Nonnegatives), + (LQOI.VecVar, MOI.Nonpositives), + (LQOI.VecVar, MOI.Zeros), + (LQOI.VecVar, MOI.SOS1{Float64}), + (LQOI.VecVar, MOI.SOS2{Float64}), + (LQOI.VecLin, MOI.Nonnegatives), + (LQOI.VecLin, MOI.Nonpositives), + (LQOI.VecLin, MOI.Zeros) +] + +mutable struct Optimizer <: LQOI.LinQuadOptimizer + LQOI.@LinQuadOptimizerBase + env::Env # Cplex environment + Optimizer(::Nothing) = new() +end + +MOI.get(::Optimizer, ::MOI.SolverName) = "CPLEX" + +function LQOI.LinearQuadraticModel(::Type{Optimizer}, env) + env = Env() + return Model(env::Env) +end + +function Optimizer(;kwargs...) + env = Env() + for (name, value) in kwargs + set_param!(env, string(name), value) + end + model = Optimizer(nothing) + model.env = env + MOI.empty!(model) + return model +end + +LQOI.supported_constraints(::Optimizer) = SUPPORTED_CONSTRAINTS +LQOI.supported_objectives(::Optimizer) = SUPPORTED_OBJECTIVES + +LQOI.backend_type(model::Optimizer, ::MOI.EqualTo{Float64}) = Cchar('E') +LQOI.backend_type(model::Optimizer, ::MOI.LessThan{Float64}) = Cchar('L') +LQOI.backend_type(model::Optimizer, ::MOI.GreaterThan{Float64}) = Cchar('G') + +LQOI.backend_type(model::Optimizer, ::MOI.Zeros) = Cchar('B') +LQOI.backend_type(model::Optimizer, ::MOI.Nonpositives) = Cchar('L') +LQOI.backend_type(model::Optimizer, ::MOI.Nonnegatives) = Cchar('E') + +function LQOI.change_variable_bounds!(model::Optimizer, + columns::Vector{Int}, values::Vector{Float64}, senses::Vector{Cchar}) + c_api_chgbds(model.inner, ivec(columns), senses, values) + return +end + +function LQOI.get_variable_lowerbound(model::Optimizer, column::Int) + return c_api_getlb(model.inner, Cint(column), Cint(column))[1] +end + +function LQOI.get_variable_upperbound(model::Optimizer, column::Int) + return c_api_getub(model.inner, Cint(column), Cint(column))[1] +end + +function LQOI.get_number_linear_constraints(model::Optimizer) + return c_api_getnumrows(model.inner) +end + +function LQOI.add_linear_constraints!(model::Optimizer, + A::LQOI.CSRMatrix{Float64}, sense::Vector{Cchar}, rhs::Vector{Float64}) + c_api_addrows(model.inner, ivec(A.row_pointers), ivec(A.columns), + A.coefficients, sense, rhs) + return +end + +function LQOI.get_rhs(model::Optimizer, row::Int) + rhs = Vector{Cdouble}(undef, 1) + c_api_getrhs(model.inner, rhs, Cint(row), Cint(row)) + return rhs[1] +end + +function LQOI.get_linear_constraint(model::Optimizer, row::Int) + (nzcnt, rmatbeg, rmatind, rmatval) = + c_api_getrows(model.inner, Cint(row), Cint(row)) + return rmatind[1:nzcnt], rmatval[1:nzcnt] +end + +function LQOI.change_matrix_coefficient!( + model::Optimizer, row::Int, col::Int, coef::Float64) + c_api_chgcoef(model.inner, Cint(row), Cint(col), coef) + return +end + +function LQOI.change_objective_coefficient!( + model::Optimizer, col::Int, coef::Float64) + c_api_chgobj(model.inner, [Cint(col)], [coef]) + return +end + +function LQOI.change_rhs_coefficient!( + model::Optimizer, row::Int, coef::Float64) + c_api_chgrhs(model.inner, [Cint(row)], [coef]) + return +end + +function LQOI.delete_linear_constraints!( + model::Optimizer, first_row::Int, last_row::Int) + c_api_delrows(model.inner, Cint(first_row), Cint(last_row)) + return +end + +function LQOI.change_variable_types!( + model::Optimizer, columns::Vector{Int}, vtypes::Vector{Cchar}) + c_api_chgctype(model.inner, ivec(columns), vtypes) + return +end + +function LQOI.change_linear_constraint_sense!( + model::Optimizer, rows::Vector{Int}, senses::Vector{Cchar}) + c_api_chgsense(model.inner, ivec(rows), senses) + return +end + +function LQOI.set_linear_objective!( + model::Optimizer, columns::Vector{Int}, coefficients::Vector{Float64}) + n = num_var(model.inner) + all_coefs = zeros(Float64, n) + for (col, coef) in zip(columns, coefficients) + all_coefs[col] += coef + end + c_api_chgobj(model.inner, Cint[1:n;], all_coefs) + return +end + +function LQOI.change_objective_sense!(model::Optimizer, symbol) + if symbol == :min + c_api_chgobjsen(model.inner, Cint(1)) + else + c_api_chgobjsen(model.inner, Cint(-1)) + end + return +end + +function LQOI.get_linear_objective!(model::Optimizer, dest) + c_api_getobj(model.inner, dest, Cint(1), c_api_getnumcols(model.inner)) + return +end + +function LQOI.get_objectivesense(model::Optimizer) + s = c_api_getobjsen(model.inner) + if s == 1 + return MOI.MIN_SENSE + else + return MOI.MAX_SENSE + end +end + +function LQOI.get_number_variables(model::Optimizer) + return c_api_getnumcols(model.inner) +end + +function LQOI.add_variables!(model::Optimizer, N::Int) + add_vars!(model.inner, zeros(Float64, N), fill(-Inf, N), fill(Inf, N)) + return +end + +function LQOI.delete_variables!(model::Optimizer, first_col::Int, last_col::Int) + c_api_delcols(model.inner, Cint(first_col), Cint(last_col)) + return +end + +function LQOI.solve_mip_problem!(model::Optimizer) + model.inner.has_int = true + LQOI.solve_linear_problem!(model) + return +end + +function LQOI.solve_linear_problem!(model::Optimizer) + optimize!(model.inner) + return +end + +function LQOI.get_termination_status(model::Optimizer) + stat = c_api_getstat(model.inner) + + if stat == 1 # CPX_STAT_OPTIMAL + return MOI.OPTIMAL + elseif stat == 3 # CPX_STAT_INFEASIBLE + return MOI.INFEASIBLE + elseif stat == 4 # CPX_STAT_INForUNBD + return MOI.INFEASIBLE_OR_UNBOUNDED + elseif stat == 2 # CPX_STAT_UNBOUNDED + return MOI.DUAL_INFEASIBLE + elseif stat in (12, 21, 22, 36) # CPX_STAT_*ABORT*_OBJ_LIM + return MOI.OBJECTIVE_LIMIT + elseif stat in (10, 34) # CPX_STAT_*ABORT_IT_LIM + return MOI.ITERATION_LIMIT + elseif stat == 53 # CPX_STAT_CONFLICT_ABORT_NODE_LIM + return MOI.NODE_LIMIT + elseif stat in (11, 25, 33, 39) # CPX_STAT_*ABORT*TIME_LIM + return MOI.TIME_LIMIT + elseif stat == 5 # CPX_STAT_OPTIMAL_INFEAS + return MOI.NUMERICAL_ERROR + # MIP STATUS + elseif stat in (101, 102) # CPXMIP_OPTIMAL, CPXMIP_OPTIMAL_TOL + return MOI.OPTIMAL + elseif stat == 103 # CPXMIP_INFEASIBLE + return MOI.INFEASIBLE + elseif stat == 119 # CPXMIP_INForUNBD + return MOI.INFEASIBLE_OR_UNBOUNDED + elseif stat == 118 # CPXMIP_UNBOUNDED + return MOI.DUAL_INFEASIBLE + elseif stat in (105, 106) # CPXMIP_NODE_LIM* + return MOI.NODE_LIMIT + elseif stat in (107, 108, 131, 132) # CPXMIP_*TIME_LIM* + return MOI.TIME_LIMIT + else + return MOI.OTHER_ERROR + end +end + +function LQOI.get_primal_status(model::Optimizer) + soln_method, soln_type, primal_stat, dual_stat = c_api_solninfo(model.inner) + if primal_stat == 1 + return MOI.FEASIBLE_POINT + else + return MOI.NO_SOLUTION + end +end + +function LQOI.get_dual_status(model::Optimizer) + if model.inner.has_int + return MOI.NO_SOLUTION + end + soln_method, soln_type, primal_stat, dual_stat = c_api_solninfo(model.inner) + if dual_stat == 1 + return MOI.FEASIBLE_POINT + else + return MOI.NO_SOLUTION + end +end + +function LQOI.get_variable_primal_solution!(model::Optimizer, dest) + c_api_getx(model.inner, dest) + return +end + +function LQOI.get_linear_primal_solution!(model::Optimizer, dest) + c_api_getax(model.inner, dest) + return +end + +function LQOI.get_variable_dual_solution!(model::Optimizer, dest) + c_api_getdj(model.inner, dest) + return +end + +function LQOI.get_linear_dual_solution!(model::Optimizer, dest) + c_api_getpi(model.inner, dest) + return +end + +function LQOI.get_objective_value(model::Optimizer) + return c_api_getobjval(model.inner) +end + +function LQOI.get_objective_bound(model::Optimizer) + return get_best_bound(model.inner) +end + +function LQOI.get_farkas_dual!(model::Optimizer, dest) + copy!(dest, get_infeasibility_ray(model.inner)) + return +end + +function LQOI.get_unbounded_ray!(model::Optimizer, dest) + copy!(dest, get_unbounded_ray(model.inner)) + return +end + +function LQOI.add_sos_constraint!(model::Optimizer, columns::Vector{Int}, + weights::Vector{Float64}, sos_type::Symbol) + add_sos!(model.inner, sos_type, columns, weights) + return +end + +function LQOI.get_sos_constraint(model::Optimizer, index::Int) + (cols, weights, sos_type) = c_api_getsos(model.inner, index - 1) + if sos_type == Cchar('1') + return (cols .+ 1, weights, :SOS1) + else + @assert sos_type == Cchar('2') + return (cols .+ 1, weights, :SOS2) + end + return +end + +function LQOI.delete_sos!(model::Optimizer, begin_index::Int, end_index::Int) + c_api_delsos(model.inner, begin_index - 1, end_index - 1) + return +end diff --git a/src/cpx_constrs.jl b/src/cpx_constrs.jl index 9642a3c6..1412935d 100644 --- a/src/cpx_constrs.jl +++ b/src/cpx_constrs.jl @@ -1,6 +1,6 @@ -function c_api_addrows(model::Model, cbegins::IVec, inds::IVec, coeffs::FVec, +function c_api_addrows(model::Model, cbegins::IVec, inds::IVec, coeffs::FVec, rel::CVec, rhs::FVec) - + nnz = length(inds) ncons = length(rhs) (nnz == length(coeffs)) || error("Incompatible constraint arg dimensions.") @@ -20,8 +20,8 @@ function c_api_addrows(model::Model, cbegins::IVec, inds::IVec, coeffs::FVec, Ptr{Ptr{Cchar}}, # col names Ptr{Ptr{Cchar}} # row names ), - model.env.ptr, model.lp, 0, ncons, nnz, rhs, rel, - cbegins .- Cint(1), inds .- Cint(1), coeffs, + model.env.ptr, model.lp, 0, ncons, nnz, rhs, rel, + cbegins .- Cint(1), inds .- Cint(1), coeffs, C_NULL, C_NULL) if stat != 0 @@ -31,7 +31,7 @@ function c_api_addrows(model::Model, cbegins::IVec, inds::IVec, coeffs::FVec, end # same as add_rows but does conversion of sense chars -function add_constrs!(model::Model, cbegins::IVec, inds::IVec, coeffs::FVec, +function add_constrs!(model::Model, cbegins::IVec, inds::IVec, coeffs::FVec, rel::CVec, rhs::FVec) for k in 1:length(rel) @@ -43,7 +43,7 @@ function add_constrs!(model::Model, cbegins::IVec, inds::IVec, coeffs::FVec, rel[k] = convert(Cchar, 'E') end end - + c_api_addrows(model, cbegins, inds, coeffs, rel, rhs) end @@ -150,7 +150,7 @@ function c_api_getnumrows(model::Model) return ncons end # Needed by MathProgBase Interface -num_constr(model::Model) = c_api_getnumrows(model) +num_constr(model::Model) = c_api_getnumrows(model) function get_constr_senses(model::Model) ncons = num_constr(model) @@ -178,7 +178,7 @@ function c_api_chgsense(model::Model, indices::IVec, senses::CVec) Ptr{Cint}, Ptr{Cchar} ), - model.env.ptr, model.lp, ncons, + model.env.ptr, model.lp, ncons, indices .- Cint(1), senses) if stat != 0 throw(CplexError(model.env, stat)) @@ -194,16 +194,16 @@ function set_constr_senses!(model::Model, senses::Vector) Ptr{Cint}, Ptr{Cchar} ), - model.env.ptr, model.lp, ncons, Cint[0:ncons-1;], + model.env.ptr, model.lp, ncons, Cint[0:ncons-1;], convert(Vector{Cchar},senses)) if stat != 0 throw(CplexError(model.env, stat)) end end -function c_api_getrhs(model::Model, rhs::Vector{Cdouble}, +function c_api_getrhs(model::Model, rhs::Vector{Cdouble}, row_start::Cint, row_end::Cint) - + stat = @cpx_ccall(getrhs, Cint, ( Ptr{Cvoid}, Ptr{Cvoid}, @@ -211,13 +211,13 @@ function c_api_getrhs(model::Model, rhs::Vector{Cdouble}, Cint, Cint ), - model.env.ptr, model.lp, rhs, + model.env.ptr, model.lp, rhs, row_start - Cint(1), row_end - Cint(1)) if stat != 0 throw(CplexError(model.env, stat)) end end - + function get_rhs(model::Model) ncons = num_constr(model) rhs = Vector{Cdouble}(undef, ncons) @@ -359,8 +359,8 @@ end function c_api_getrows(model::Model, mbegin::Cint, mend::Cint) nzcnt_p = Vector{Cint}(undef, 1) - m = mend - mbegin + 1 - nnz = get_nnz(model) + m = mend - mbegin + 1 + nnz = get_nnz(model) rmatbeg = Vector{Cint}(undef, m+1) rmatind = Vector{Cint}(undef, nnz) rmatval = Vector{Cdouble}(undef, nnz) @@ -377,8 +377,8 @@ function c_api_getrows(model::Model, mbegin::Cint, mend::Cint) Cint, Cint ), - model.env.ptr, model.lp, nzcnt_p, - rmatbeg, rmatind, rmatval, + model.env.ptr, model.lp, nzcnt_p, + rmatbeg, rmatind, rmatval, nnz, surplus_p, mbegin-Cint(1), mend-Cint(1)) if stat != 0 || surplus_p[1] < 0 throw(CplexError(model.env, stat)) @@ -442,6 +442,44 @@ function add_sos!(model::Model, sostype::Symbol, idx::Vector{Int}, weight::Vecto return nothing end +# int CPXgetsos( CPXCENVptr env, CPXCLPptr lp, int * numsosnz_p, char * sostype, +# int * sosbeg, int * sosind, double * soswt, int sosspace, int * surplus_p, int +# begin, int end ) +function c_api_getsos(model::Model, index::Int) + numsosnz_p = Ref{Cint}() + surplus_p = Ref{Cint}() + sos_type = Ref{Cchar}() + sos_beg = Ref{Cint}() + stat = @cpx_ccall(getsos, Cint, ( + Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cint}, Ptr{Cchar}, Ptr{Cint}, Ptr{Cint}, + Ptr{Cdouble}, Cint, Ptr{Cint}, Cint, Cint), + model.env.ptr, model.lp, numsosnz_p, sos_type, sos_beg, C_NULL, C_NULL, + 0, surplus_p, index, index) + + num_terms = -surplus_p[] + columns = fill(Cint(0), num_terms) + weights = fill(Cdouble(0.0), num_terms) + stat = @cpx_ccall(getsos, Cint, ( + Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cint}, Ptr{Cchar}, Ptr{Cint}, Ptr{Cint}, + Ptr{Cdouble}, Cint, Ptr{Cint}, Cint, Cint), + model.env.ptr, model.lp, numsosnz_p, sos_type, sos_beg, columns, weights, + num_terms, surplus_p, index, index) + if stat != 0 + throw(CplexError(model.env, stat)) + end + return (columns, weights, sos_type[]) +end + +# int CPXdelsos( CPXCENVptr env, CPXLPptr lp, int begin, int end ) +function c_api_delsos(model::Model, begin_index::Int, end_index::Int) + stat = @cpx_ccall(delsos, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cint, Cint), + model.env.ptr, model.lp, begin_index, end_index) + if stat != 0 + throw(CplexError(model.env, stat)) + end + return +end + add_indicator_constraint(model::Model, idx, coeff, sense, rhs, indicator) = add_indicator_constraint(model::Model, idx, coeff, sense, rhs, indicator, 0) add_indicator_constraint(model::Model, idx, coeff, sense, rhs, indicator, comp) = @@ -474,9 +512,9 @@ function c_api_chgcoef(model::Model, row::Cint, col::Cint, coef::Cdouble) Ptr{Cvoid}, # problem Cint, # row Cint, # col - Cdouble, # coef + Cdouble, # coef ), - model.env.ptr, model.lp, + model.env.ptr, model.lp, row - Cint(1), col - Cint(1), coef) if stat != 0 diff --git a/test/MOIWrapper.jl b/test/MOI_wrapper.jl similarity index 68% rename from test/MOIWrapper.jl rename to test/MOI_wrapper.jl index 7908e2b3..4c4b67c2 100644 --- a/test/MOIWrapper.jl +++ b/test/MOI_wrapper.jl @@ -1,4 +1,4 @@ -using MathOptInterface +using MathOptInterface, CPLEX, Test const MOI = MathOptInterface const MOIT = MathOptInterface.Test @@ -9,10 +9,10 @@ const MOIB = MathOptInterface.Bridges solver = CPLEX.Optimizer() MOIT.unittest(solver, config, [ - "solve_affine_interval", # not implemented - "solve_qp_edge_cases", # not implemented - "solve_qcp_edge_cases", # not implemented - "solve_objbound_edge_cases", # Requires MOI.ObjectiveBound() + "solve_affine_interval", # not implemented + "solve_qp_edge_cases", # not implemented + "solve_qcp_edge_cases", # not implemented + "solve_objbound_edge_cases" ]) @testset "solve_affine_interval" begin MOIT.solve_affine_interval( @@ -28,8 +28,11 @@ end linconfig = MOIT.TestConfig() @testset "Default Solver" begin solver = CPLEX.Optimizer() - MOIT.contlineartest(solver, linconfig, ["linear10","linear11", - "linear12", "linear8a","linear8b","linear8c"]) + MOIT.contlineartest(solver, linconfig, [ + "linear10", # Requires interval + # Requires infeasiblity certificates + "linear8a", "linear8b", "linear8c", "linear11", "linear12" + ]) end @testset "linear10" begin MOIT.linear10test( @@ -47,8 +50,9 @@ end @testset "Integer Linear tests" begin intconfig = MOIT.TestConfig() solver = CPLEX.Optimizer() - MOIT.intlineartest(solver, intconfig, ["int1", # requires objbound impl - "int2", "int3"]) + MOIT.intlineartest(solver, intconfig, [ + "int3" # Requires Interval + ]) # 3 is ranged, 2 has sos @testset "int3" begin MOIT.int3test( @@ -57,8 +61,16 @@ end ) end end + @testset "ModelLike tests" begin solver = CPLEX.Optimizer() + @test MOI.get(solver, MOI.SolverName()) == "CPLEX" + @testset "default_objective_test" begin + MOIT.default_objective_test(solver) + end + @testset "default_status_test" begin + MOIT.default_status_test(solver) + end @testset "nametest" begin MOIT.nametest(solver) end diff --git a/test/runtests.jl b/test/runtests.jl index 4503ea97..f4f8579a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,25 +4,30 @@ import Compat.Pkg using MathProgBase using CPLEX -include("constants.jl") -include("low_level_api.jl") -@testset "LP" begin - include("lp_01.jl") - include("lp_02.jl") - include("lp_03.jl") +@testset "C API" begin + include("constants.jl") + include("low_level_api.jl") + @testset "LP" begin + include("lp_01.jl") + include("lp_02.jl") + include("lp_03.jl") + end + include("mip_01.jl") + @testset "QP" begin + include("qp_01.jl") + include("qp_02.jl") + end + include("qcqp_01.jl") + include("env.jl") + include("sos.jl") + include("problemtype.jl") + include("miqcp.jl") end -include("mip_01.jl") -@testset "QP" begin - include("qp_01.jl") - include("qp_02.jl") -end -include("qcqp_01.jl") -include("env.jl") -include("sos.jl") -include("problemtype.jl") -include("miqcp.jl") + @testset "MathProgBase" begin include("mathprog.jl") end -include("MOIWrapper.jl") +@testset "MathOptInterface" begin + include("MOI_Wrapper.jl") +end