├── test ├── REQUIRE └── runtests.jl ├── README.md ├── REQUIRE ├── LICENSE └── src └── MathOptInterfaceXpress.jl /test/REQUIRE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MathOptInterfaceXpress.jl -------------------------------------------------------------------------------- /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 0.6 2 | Xpress 3 | LinQuadOptInterface 4 | MathOptInterface 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Joaquim Dias Garcia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | #push!(Base.LOAD_PATH,joinpath(dirname(@__FILE__),"..","..")) 2 | 3 | using Xpress, Base.Test, MathOptInterface, MathOptInterface.Test, MathOptInterfaceXpress 4 | 5 | const MOI = MathOptInterface 6 | const MOIT = MathOptInterface.Test 7 | const MOIXPR = MathOptInterfaceXpress 8 | 9 | @testset "MathOptInterfaceXpress" begin 10 | @testset "Unit Tests" begin 11 | config = MOIT.TestConfig() 12 | solver = XpressOptimizer() 13 | 14 | MOIT.basic_constraint_tests(solver, config; 15 | exclude = [ 16 | ] 17 | ) 18 | 19 | MOIT.unittest(solver, config, [ 20 | "solve_affine_interval" 21 | ]) 22 | end 23 | @testset "Linear tests" begin 24 | linconfig = MOIT.TestConfig() 25 | solver = XpressOptimizer() 26 | MOIT.contlineartest(solver , linconfig, ["linear10","linear12","linear8a","linear8b","linear8c"]) 27 | 28 | solver_nopresolve = XpressOptimizer(PRESOLVE = 0) 29 | MOIT.contlineartest(solver_nopresolve , linconfig, ["linear10","linear12"]) 30 | 31 | linconfig_nocertificate = MOIT.TestConfig(infeas_certificates=false) 32 | MOIT.linear12test(solver, linconfig_nocertificate) 33 | 34 | # Intervals 35 | linconfig_noquery = MOIT.TestConfig(query=false) 36 | MOIT.linear10test(solver, linconfig_noquery) 37 | end 38 | 39 | @testset "Quadratic tests" begin 40 | quadconfig = MOIT.TestConfig(atol=1e-5, rtol=1e-5, duals=false, query=false) 41 | solver = XpressOptimizer() 42 | MOIT.contquadratictest(solver, quadconfig) 43 | end 44 | 45 | @testset "Linear Conic tests" begin 46 | linconfig = MOIT.TestConfig() 47 | solver = XpressOptimizer() 48 | MOIT.lintest(solver, linconfig, ["lin3","lin4"]) 49 | 50 | solver_nopresolve = XpressOptimizer(PRESOLVE=0) 51 | MOIT.lintest(solver_nopresolve, linconfig) 52 | end 53 | 54 | @testset "Integer Linear tests" begin 55 | intconfig = MOIT.TestConfig() 56 | solver = XpressOptimizer() 57 | MOIT.intlineartest(solver, intconfig) 58 | end 59 | 60 | @testset "ModelLike tests" begin 61 | intconfig = MOIT.TestConfig() 62 | solver = XpressOptimizer() 63 | MOIT.nametest(solver) 64 | @testset "validtest" begin 65 | MOIT.validtest(solver) 66 | end 67 | @testset "emptytest" begin 68 | MOIT.emptytest(solver) 69 | end 70 | @testset "orderedindicestest" begin 71 | MOIT.orderedindicestest(solver) 72 | end 73 | @testset "canaddconstrainttest" begin 74 | MOIT.canaddconstrainttest(solver, Float64, Complex{Float64}) 75 | end 76 | @testset "copytest" begin 77 | solver2 = XpressOptimizer() 78 | MOIT.copytest(solver,solver2) 79 | end 80 | end 81 | end 82 | ; 83 | -------------------------------------------------------------------------------- /src/MathOptInterfaceXpress.jl: -------------------------------------------------------------------------------- 1 | __precompile__() 2 | module MathOptInterfaceXpress 3 | 4 | export XpressOptimizer 5 | 6 | using Xpress 7 | const XPR = Xpress 8 | using MathOptInterface 9 | const MOI = MathOptInterface 10 | using LinQuadOptInterface 11 | const LQOI = LinQuadOptInterface 12 | 13 | const SUPPORTED_OBJECTIVES = [ 14 | LQOI.Linear, 15 | LQOI.Quad 16 | ] 17 | 18 | const SUPPORTED_CONSTRAINTS = [ 19 | (LQOI.Linear, LQOI.EQ), 20 | (LQOI.Linear, LQOI.LE), 21 | (LQOI.Linear, LQOI.GE), 22 | (LQOI.Linear, LQOI.IV), 23 | (LQOI.Quad, LQOI.EQ), 24 | (LQOI.Quad, LQOI.LE), 25 | (LQOI.Quad, LQOI.GE), 26 | (LQOI.SinVar, LQOI.EQ), 27 | (LQOI.SinVar, LQOI.LE), 28 | (LQOI.SinVar, LQOI.GE), 29 | (LQOI.SinVar, LQOI.IV), 30 | (LQOI.SinVar, MOI.ZeroOne), 31 | (LQOI.SinVar, MOI.Integer), 32 | (LQOI.VecVar, LQOI.SOS1), 33 | (LQOI.VecVar, LQOI.SOS2), 34 | (LQOI.VecVar, MOI.Nonnegatives), 35 | (LQOI.VecVar, MOI.Nonpositives), 36 | (LQOI.VecVar, MOI.Zeros), 37 | (LQOI.VecLin, MOI.Nonnegatives), 38 | (LQOI.VecLin, MOI.Nonpositives), 39 | (LQOI.VecLin, MOI.Zeros) 40 | ] 41 | 42 | mutable struct XpressOptimizer <: LQOI.LinQuadOptimizer 43 | LQOI.@LinQuadOptimizerBase 44 | params::Dict{Any,Any} 45 | XpressOptimizer(::Void) = new() 46 | end 47 | 48 | LQOI.LinearQuadraticModel(::Type{XpressOptimizer}, env) = XPR.Model() 49 | 50 | function XpressOptimizer(; kwargs...) 51 | 52 | env = nothing 53 | m = XpressOptimizer(nothing) 54 | m.params = Dict{Any,Any}() 55 | MOI.empty!(m) 56 | for (name,value) in kwargs 57 | m.params[name] = value 58 | XPR.setparam!(m.inner, XPR.XPRS_CONTROLS_DICT[name], value) 59 | end 60 | return m 61 | end 62 | 63 | function MOI.empty!(m::XpressOptimizer) 64 | MOI.empty!(m,nothing) 65 | for (name,value) in m.params 66 | XPR.setparam!(m.inner, XPR.XPRS_CONTROLS_DICT[name], value) 67 | end 68 | end 69 | 70 | LQOI.supported_constraints(s::XpressOptimizer) = SUPPORTED_CONSTRAINTS 71 | LQOI.supported_objectives(s::XpressOptimizer) = SUPPORTED_OBJECTIVES 72 | 73 | backend_type(m::XpressOptimizer, ::MOI.GreaterThan{T}) where T = Cchar('G') 74 | backend_type(m::XpressOptimizer, ::MOI.LessThan{T}) where T = Cchar('L') 75 | backend_type(m::XpressOptimizer, ::MOI.EqualTo{T}) where T = Cchar('E') 76 | # Implemented separately 77 | # backend_type(m::XpressOptimizer, ::MOI.Interval{T}) where T = Cchar('R') 78 | 79 | backend_type(m::XpressOptimizer, ::MOI.Zeros) = Cchar('E') 80 | backend_type(m::XpressOptimizer, ::MOI.Nonpositives) = Cchar('L') 81 | backend_type(m::XpressOptimizer, ::MOI.Nonnegatives) = Cchar('G') 82 | 83 | 84 | 85 | #= 86 | not in LinQuad 87 | =# 88 | 89 | setparam!(instance::XpressOptimizer, name, val) = XPR.setparam!(instance.inner, XPR.XPRS_CONTROLS_DICT[name], val) 90 | 91 | setlogfile!(instance::XpressOptimizer, path) = XPR.setlogfile(instance.inner, path::String) 92 | 93 | cintvec(v::Vector) = convert(Vector{Int32}, v) 94 | 95 | #= 96 | inner wrapper 97 | =# 98 | 99 | #= 100 | Constraints 101 | =# 102 | 103 | MOI.canset(::XpressOptimizer, ::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}) = true 104 | MOI.canset(::XpressOptimizer, ::MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}}) = true 105 | MOI.canget(::XpressOptimizer, ::MOI.ConstraintSet, ::Type{LQOI.LCI{LQOI.IV}}) = true 106 | 107 | LQOI.change_variable_bounds!(instance::XpressOptimizer, colvec, valvec, sensevec) = XPR.chgbounds!(instance.inner, cintvec(colvec), sensevec, valvec) 108 | 109 | LQOI.get_variable_lowerbound(instance::XpressOptimizer, col) = XPR.get_lb(instance.inner, col, col)[1] 110 | LQOI.get_variable_upperbound(instance::XpressOptimizer, col) = XPR.get_ub(instance.inner, col, col)[1] 111 | 112 | LQOI.get_number_linear_constraints(instance::XpressOptimizer) = XPR.num_linconstrs(instance.inner) 113 | 114 | LQOI.add_linear_constraints!(instance::XpressOptimizer, A::LQOI.CSRMatrix{Float64}, sensevec, rhsvec) = XPR.add_constrs!(instance.inner, A.row_pointers, A.columns, A.coefficients, sensevec, rhsvec) 115 | 116 | function LQOI.add_ranged_constraints!(instance::XpressOptimizer, A::LQOI.CSRMatrix{Float64}, lowerbound, upperbound) 117 | newrows = length(lowerbound) 118 | rows = XPR.num_linconstrs(instance.inner) 119 | addedrows = collect((rows+1):(rows+newrows)) 120 | 121 | sensevec = fill(Cchar('E'),newrows) 122 | XPR.add_constrs!(instance.inner, A.row_pointers, A.columns, A.coefficients, sensevec, upperbound) 123 | 124 | XPR.chg_rhsrange!(instance.inner, cintvec(addedrows), +upperbound-lowerbound) 125 | end 126 | 127 | function LQOI.modify_ranged_constraints!(instance::XpressOptimizer, rows::Vector{Int}, lowerbound::Vector{Float64}, upperbound::Vector{Float64}) 128 | XPR.set_rhs!(instance.inner, rows, upperbound) 129 | XPR.chg_rhsrange!(instance.inner, cintvec(rows), +upperbound-lowerbound) 130 | end 131 | 132 | LQOI.get_rhs(instance::XpressOptimizer, row) = XPR.get_rhs(instance.inner, row, row)[1] 133 | 134 | function LQOI.get_range(instance::XpressOptimizer, row) 135 | ub = XPR.get_rhs(instance.inner, row, row)[1] 136 | r = XPR.get_rhsrange(instance.inner, row, row)[1] 137 | return ub-r, ub 138 | end 139 | 140 | # TODO improve 141 | function LQOI.get_linear_constraint(instance::XpressOptimizer, idx) 142 | A = XPR.get_rows(instance.inner, idx, idx)' 143 | return A.rowval-1, A.nzval 144 | end 145 | 146 | function LQOI.get_quadratic_constraint(instance::XpressOptimizer, idx) 147 | A = XPR.get_rows(instance.inner, idx, idx)' 148 | 149 | Q = XPR.get_qrowmatrix_triplets(instance.inner, idx) 150 | 151 | return A.rowval-1, A.nzval, Q 152 | end 153 | 154 | # notin LQOI 155 | # TODO improve 156 | function getcoef(instance::XpressOptimizer, row, col) 157 | A = XPR.get_rows(instance.inner, row, row)' 158 | cols = A.rowval 159 | vals = A.nzval 160 | 161 | pos = findfirst(cols, col) 162 | if pos > 0 163 | return vals[pos] 164 | else 165 | return 0.0 166 | end 167 | end 168 | 169 | LQOI.change_matrix_coefficient!(instance::XpressOptimizer, row, col, coef) = XPR.chg_coeffs!(instance.inner, row, col, coef) 170 | 171 | LQOI.change_objective_coefficient!(instance::XpressOptimizer, col, coef) = XPR.set_objcoeffs!(instance.inner, Int32[col], Float64[coef]) 172 | 173 | LQOI.change_rhs_coefficient!(instance::XpressOptimizer, row, coef) = XPR.set_rhs!(instance.inner, Int32[row], Float64[coef]) 174 | 175 | LQOI.delete_linear_constraints!(instance::XpressOptimizer, rowbeg, rowend) = XPR.del_constrs!(instance.inner, cintvec(collect(rowbeg:rowend))) 176 | 177 | LQOI.delete_quadratic_constraints!(instance::XpressOptimizer, rowbeg, rowend) = XPR.del_constrs!(instance.inner, cintvec(collect(rowbeg:rowend))) 178 | 179 | LQOI.change_variable_types!(instance::XpressOptimizer, colvec, typevec) = XPR.chgcoltypes!(instance.inner, colvec, typevec) 180 | 181 | LQOI.change_linear_constraint_sense!(instance::XpressOptimizer, rowvec, sensevec) = XPR.set_rowtype!(instance.inner, rowvec, sensevec) 182 | 183 | LQOI.add_sos_constraint!(instance::XpressOptimizer, colvec, valvec, typ) = XPR.add_sos!(instance.inner, typ, colvec, valvec) 184 | 185 | LQOI.delete_sos!(instance::XpressOptimizer, idx1, idx2) = XPR.del_sos!(instance.inner, cintvec(collect(idx1:idx2))) 186 | 187 | # TODO improve getting processes 188 | function LQOI.get_sos_constraint(instance::XpressOptimizer, idx) 189 | A, types = XPR.get_sos_matrix(instance.inner) 190 | line = A[idx,:] #sparse vec 191 | cols = line.nzind 192 | vals = line.nzval 193 | typ = types[idx] == Cchar('1') ? :SOS1 : :SOS2 194 | return cols, vals, typ 195 | end 196 | 197 | LQOI.get_number_quadratic_constraints(instance::XpressOptimizer) = XPR.num_qconstrs(instance.inner) 198 | 199 | 200 | function scalediagonal!(V, I, J, scale) 201 | # LQOI assumes 0.5 x' Q x, but Gurobi requires the list of terms, e.g., 202 | # 2x^2 + xy + y^2, so we multiply the diagonal of V by 0.5. We don't 203 | # multiply the off-diagonal terms since we assume they are symmetric and we 204 | # only need to give one. 205 | # 206 | # We also need to make sure that after adding the constraint we un-scale 207 | # the vector because we can't modify user-data. 208 | for i in 1:length(I) 209 | if I[i] == J[i] 210 | V[i] *= scale 211 | end 212 | end 213 | end 214 | 215 | function LQOI.add_quadratic_constraint!(instance::XpressOptimizer, cols, coefs, rhs, sense, I, J, V) 216 | @assert length(I) == length(J) == length(V) 217 | scalediagonal!(V, I, J, 0.5) 218 | XPR.add_qconstr!(instance.inner, cols, coefs, I, J, V, sense, rhs) 219 | scalediagonal!(V, I, J, 2.0) 220 | end 221 | #= 222 | Objective 223 | =# 224 | 225 | function LQOI.set_quadratic_objective!(instance::XpressOptimizer, I, J, V) 226 | @assert length(I) == length(J) == length(V) 227 | XPR.delq!(instance.inner) 228 | # scalediagonal!(V, I, J, 0.5) 229 | XPR.add_qpterms!(instance.inner, I, J, V) 230 | # scalediagonal!(V, I, J, 2.0) 231 | return nothing 232 | end 233 | 234 | function LQOI.set_linear_objective!(instance::XpressOptimizer, colvec, coefvec) 235 | nvars = XPR.num_vars(instance.inner) 236 | obj = zeros(Float64, nvars) 237 | 238 | for i in eachindex(colvec) 239 | obj[colvec[i]] = coefvec[i] 240 | end 241 | 242 | XPR.set_obj!(instance.inner, obj) 243 | nothing 244 | end 245 | 246 | function LQOI.change_objective_sense!(instance::XpressOptimizer, symbol) 247 | if symbol == :min 248 | XPR.set_sense!(instance.inner, :minimize) 249 | else 250 | XPR.set_sense!(instance.inner, :maximize) 251 | end 252 | end 253 | 254 | function LQOI.get_linear_objective!(instance::XpressOptimizer, x::Vector{Float64}) 255 | obj = XPR.get_obj(instance.inner) 256 | @assert length(x) == length(obj) 257 | for i in 1:length(obj) 258 | x[i] = obj[i] 259 | end 260 | end 261 | 262 | function LQOI.get_quadratic_terms_objective(instance::XpressOptimizer) 263 | I, J, V = XPR.getq(instance.inner) 264 | return I, J, V 265 | end 266 | 267 | function LQOI.get_objectivesense(instance::XpressOptimizer) 268 | s = XPR.model_sense(instance.inner) 269 | if s == :maximize 270 | return MOI.MaxSense 271 | else 272 | return MOI.MinSense 273 | end 274 | end 275 | 276 | #= 277 | Variables 278 | =# 279 | 280 | LQOI.get_number_variables(instance::XpressOptimizer) = XPR.num_vars(instance.inner) 281 | 282 | LQOI.add_variables!(instance::XpressOptimizer, int) = XPR.add_cvars!(instance.inner, zeros(int)) 283 | 284 | LQOI.delete_variables!(instance::XpressOptimizer, col, col2) = XPR.del_vars!(instance.inner, col) 285 | 286 | # LQOI.lqs_addmipstarts(m, colvec, valvec) 287 | function LQOI.add_mip_starts!(instance::XpressOptimizer, colvec, valvec) 288 | x = zeros(XPR.num_vars(instance.inner)) 289 | for i in eachindex(colvec) 290 | x[colvec[i]] = valvec[i] 291 | end 292 | XPR.loadbasis(instance.inner, x) 293 | end 294 | 295 | #= 296 | Solve 297 | =# 298 | 299 | LQOI.solve_mip_problem!(instance::XpressOptimizer) = XPR.mipoptimize(instance.inner) 300 | 301 | LQOI.solve_quadratic_problem!(instance::XpressOptimizer) = ( writeproblem(instance, "db", "l");LQOI.solve_linear_problem!(instance) ) 302 | 303 | LQOI.solve_linear_problem!(instance::XpressOptimizer) = XPR.lpoptimize(instance.inner) 304 | 305 | function LQOI.get_termination_status(instance::XpressOptimizer) 306 | stat_lp = XPR.get_lp_status2(instance.inner) 307 | if XPR.is_mip(instance.inner) 308 | stat_mip = XPR.get_mip_status2(instance.inner) 309 | if stat_mip == XPR.MIP_NotLoaded 310 | return MOI.OtherError 311 | elseif stat_mip == XPR.MIP_LPNotOptimal 312 | # MIP search incomplete but there is no linear sol 313 | # return MOI.OtherError 314 | return MOI.InfeasibleOrUnbounded 315 | elseif stat_mip == XPR.MIP_NoSolFound 316 | # MIP search incomplete but there is no integer sol 317 | other = xprsmoi_stopstatus(instance.inner) 318 | if other == MOI.OtherError 319 | return MOI.SlowProgress#OtherLimit 320 | else 321 | return other 322 | end 323 | 324 | elseif stat_mip == XPR.MIP_Solution 325 | # MIP search incomplete but there is a solution 326 | other = xprsmoi_stopstatus(instance.inner) 327 | if other == MOI.OtherError 328 | return MOI.OtherLimit 329 | else 330 | return other 331 | end 332 | 333 | elseif stat_mip == XPR.MIP_Infeasible 334 | if XPR.hasdualray(instance.inner) 335 | return MOI.Success 336 | else 337 | return MOI.InfeasibleNoResult 338 | end 339 | elseif stat_mip == XPR.MIP_Optimal 340 | return MOI.Success 341 | elseif stat_mip == XPR.MIP_Unbounded 342 | if XPR.hasprimalray(instance.inner) 343 | return MOI.Success 344 | else 345 | return MOI.UnboundedNoResult 346 | end 347 | end 348 | return MOI.OtherError 349 | else 350 | if stat_lp == XPR.LP_Unstarted 351 | return MOI.OtherError 352 | elseif stat_lp == XPR.LP_Optimal 353 | return MOI.Success 354 | elseif stat_lp == XPR.LP_Infeasible 355 | if XPR.hasdualray(instance.inner) 356 | return MOI.Success 357 | else 358 | return MOI.InfeasibleNoResult 359 | end 360 | elseif stat_lp == XPR.LP_CutOff 361 | return MOI.ObjectiveLimit 362 | elseif stat_lp == XPR.LP_Unfinished 363 | return xprsmoi_stopstatus(instance.inner) 364 | elseif stat_lp == XPR.LP_Unbounded 365 | if XPR.hasprimalray(instance.inner) 366 | return MOI.Success 367 | else 368 | return MOI.UnboundedNoResult 369 | end 370 | elseif stat_lp == XPR.LP_CutOffInDual 371 | return MOI.ObjectiveLimit 372 | elseif stat_lp == XPR.LP_Unsolved 373 | return MOI.OtherError 374 | elseif stat_lp == XPR.LP_NonConvex 375 | return MOI.InvalidModel 376 | end 377 | return MOI.OtherError 378 | end 379 | end 380 | 381 | function xprsmoi_stopstatus(instance::XpressOptimizer) 382 | ss = XPR.get_stopstatus(instance.inner) 383 | if ss == XPR.StopTimeLimit 384 | return MOI.TimeLimit 385 | elseif ss == XPR.StopControlC 386 | return MOI.Interrupted 387 | elseif ss == XPR.StopNodeLimit 388 | # should not be here 389 | warn("should not be here") 390 | return MOI.NodeLimit 391 | elseif ss == XPR.StopIterLimit 392 | return MOI.IterationLimit 393 | elseif ss == XPR.StopMIPGap 394 | return MOI.ObjectiveLimit 395 | elseif ss == XPR.StopSolLimit 396 | return MOI.SolutionLimit 397 | elseif ss == XPR.StopUser 398 | return MOI.Interrupted 399 | end 400 | return MOI.OtherError 401 | end 402 | 403 | function LQOI.get_primal_status(instance::XpressOptimizer) 404 | if XPR.is_mip(instance.inner) 405 | stat_mip = XPR.get_mip_status2(instance.inner) 406 | if stat_mip in [XPR.MIP_Solution, XPR.MIP_Optimal] 407 | return MOI.FeasiblePoint 408 | elseif XPR.MIP_Infeasible && XPR.hasdualray(instance.inner) 409 | return MOI.InfeasibilityCertificate 410 | elseif XPR.MIP_Unbounded && XPR.hasprimalray(instance.inner) 411 | return MOI.InfeasibilityCertificate 412 | elseif stat_mip in [XPR.MIP_LPOptimal, XPR.MIP_NoSolFound] 413 | return MOI.InfeasiblePoint 414 | end 415 | return MOI.UnknownResultStatus 416 | else 417 | stat_lp = XPR.get_lp_status2(instance.inner) 418 | if stat_lp == XPR.LP_Optimal 419 | return MOI.FeasiblePoint 420 | elseif stat_lp == XPR.LP_Unbounded && XPR.hasprimalray(instance.inner) 421 | return MOI.InfeasibilityCertificate 422 | # elseif stat_lp == LP_Infeasible 423 | # return MOI.InfeasiblePoint - xpress wont return 424 | # elseif cutoff//cutoffindual ??? 425 | else 426 | return MOI.UnknownResultStatus 427 | end 428 | end 429 | end 430 | 431 | function LQOI.get_dual_status(instance::XpressOptimizer) 432 | if XPR.is_mip(instance.inner) 433 | return MOI.UnknownResultStatus 434 | else 435 | stat_lp = XPR.get_lp_status2(instance.inner) 436 | if stat_lp == XPR.LP_Optimal 437 | return MOI.FeasiblePoint 438 | elseif stat_lp == XPR.LP_Infeasible && XPR.hasdualray(instance.inner) 439 | return MOI.InfeasibilityCertificate 440 | # elseif stat_lp == LP_Unbounded 441 | # return MOI.InfeasiblePoint - xpress wont return 442 | # elseif cutoff//cutoffindual ??? 443 | else 444 | return MOI.UnknownResultStatus 445 | end 446 | end 447 | end 448 | 449 | LQOI.get_variable_primal_solution!(instance::XpressOptimizer, place) = XPR.get_solution!(instance.inner, place) 450 | 451 | function LQOI.get_linear_primal_solution!(instance::XpressOptimizer, place) 452 | if num_qconstrs(instance.inner) == 0 453 | XPR.get_slack_lin!(instance.inner, place) 454 | rhs = XPR.get_rhs(instance.inner) 455 | for i in eachindex(place) 456 | place[i] = -place[i]+rhs[i] 457 | end 458 | else 459 | XPR.get_slack_lin!(instance.inner, place) 460 | rhs = XPR.get_rhs(instance.inner) 461 | lrows = XPR.get_lrows(instance.inner) 462 | for (i,v) in enumerate(lrows) 463 | place[i] = -place[i]+rhs[v] 464 | end 465 | end 466 | nothing 467 | end 468 | 469 | function moi_lrows(m::Xpress.Model) 470 | tt_rows = collect(1:num_constrs(m)) 471 | if num_qconstrs(m) == 0 472 | return tt_rows 473 | else 474 | return setdiff(tt_rows, XPR.get_qrows(m)) 475 | end 476 | end 477 | 478 | function LQOI.get_quadratic_primal_solution!(instance::XpressOptimizer, place) 479 | if num_qconstrs(instance.inner) == 0 480 | return nothing 481 | else 482 | qrows = XPR.get_qrows(instance.inner) 483 | newplace = zeros(num_constrs(instance.inner)) 484 | XPR.get_slack!(instance.inner, newplace) 485 | rhs = XPR.get_rhs(instance.inner) 486 | for (i,v) in enumerate(qrows) 487 | place[i] = -newplace[v]+rhs[v] 488 | end 489 | end 490 | nothing 491 | end 492 | 493 | LQOI.get_variable_dual_solution!(instance::XpressOptimizer, place) = XPR.get_reducedcost!(instance.inner, place) 494 | 495 | LQOI.get_linear_dual_solution!(instance::XpressOptimizer, place) = XPR.get_dual_lin!(instance.inner, place) 496 | 497 | function LQOI.get_quadratic_dual_solution!(instance::XpressOptimizer, place) 498 | if num_qconstrs(instance.inner) == 0 499 | return nothing 500 | else 501 | qrows = XPR.get_qrows(instance.inner) 502 | newplace = zeros(num_constrs(instance.inner)) 503 | XPR.get_dual!(instance.inner, newplace) 504 | for (i,v) in enumerate(qrows) 505 | place[i] = newplace[v] 506 | end 507 | end 508 | end 509 | 510 | LQOI.get_objective_value(instance::XpressOptimizer) = XPR.get_objval(instance.inner) 511 | 512 | LQOI.get_objective_bound(instance::XpressOptimizer) = XPR.get_mip_objval(instance.inner) 513 | 514 | function LQOI.get_relative_mip_gap(instance::XpressOptimizer) 515 | L = XPR.get_mip_objval(instance.inner) 516 | U = XPR.get_bestbound(instance.inner) 517 | return abs(U-L)/U 518 | end 519 | 520 | LQOI.get_iteration_count(instance::XpressOptimizer) = XPR.get_simplex_iter_count(instance.inner) 521 | 522 | LQOI.get_barrier_iterations(instance::XpressOptimizer) = XPR.get_barrier_iter_count(instance.inner) 523 | 524 | LQOI.get_node_count(instance::XpressOptimizer) = XPR.get_node_count(instance.inner) 525 | 526 | LQOI.get_farkas_dual!(instance::XpressOptimizer, place) = XPR.getdualray!(instance.inner, place) 527 | 528 | LQOI.get_unbounded_ray!(instance::XpressOptimizer, place) = XPR.getprimalray!(instance.inner, place) 529 | 530 | 531 | MOI.free!(m::XpressOptimizer) = XPR.free_model(m.onner) 532 | 533 | """ 534 | writeproblem(m: :MOI.AbstractOptimizer, filename::String) 535 | Writes the current problem data to the given file. 536 | Supported file types are solver-dependent. 537 | """ 538 | writeproblem(instance::XpressOptimizer, filename::String, flags::String="") = XPR.write_model(instance.inner, filename, flags) 539 | 540 | end # module --------------------------------------------------------------------------------