-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathhelper.jl
530 lines (489 loc) · 21.7 KB
/
helper.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
struct XpressError <: Exception
errorcode::Int
msg::String
# TODO try to append string from Xpress.getlasterror(model) here
# only possible on call that had the model as parameter
end
function Base.showerror(io::IO, err::XpressError)
print(io, "XpressError($(err.errorcode)): ")
if err.errorcode == 1
print(io, "Bad input encountered.")
elseif err.errorcode == 2
print(io, "Bad or corrupt file - unrecoverable.")
elseif err.errorcode == 4
print(io, "Memory error.")
elseif err.errorcode == 8
print(io, "Corrupt use.")
elseif err.errorcode == 16
print(io, "Program error.")
elseif err.errorcode == 32
print(io, "Subroutine not completed successfully, possibly due to invalid argument.")
elseif err.errorcode == 128
print(io, "Too many users.")
else
print(io, "Unrecoverable error.")
end
print(io, " $(err.msg)")
end
function fixinfinity(val::Float64)
if val == Inf
return Xpress.Lib.XPRS_PLUSINFINITY
elseif val == -Inf
return Xpress.Lib.XPRS_MINUSINFINITY
else
return val
end
end
function fixinfinity!(vals::Vector{Float64})
map!(fixinfinity, vals, vals)
end
"""
Xpress.CWrapper
abstract type Xpress.CWrapper
"""
abstract type CWrapper end
Base.unsafe_convert(::Type{Ptr{Cvoid}}, t::CWrapper) = (t.ptr == C_NULL) ? throw(XpressError(255, "Received null pointer in CWrapper. Something must be wrong.")) : t.ptr
mutable struct XpressProblem <: CWrapper
ptr::Lib.XPRSprob
logfile::String
function XpressProblem(ptr::Lib.XPRSprob; finalize_env::Bool = true, logfile = "")
model = new(ptr, logfile)
if finalize_env
finalizer(destroyprob, model)
end
return model
end
end
function get_xpress_error_message(prob::XpressProblem)
lstrip(Xpress.getlasterror(prob), ['?'])
end
function XpressProblem(; logfile = "")
ref = Ref{Lib.XPRSprob}()
createprob(ref)
@assert ref[] != C_NULL "Failed to create XpressProblem. Received null pointer from Xpress C interface."
p = XpressProblem(ref[], logfile = logfile)
if logfile != ""
setlogfile(p, logfile)
end
return p
end
addcolnames(prob::XpressProblem, names::Vector{String}) = addnames(prob, names, 2)
addrownames(prob::XpressProblem, names::Vector{String}) = addnames(prob, names, 1)
"""
getcontrol(prob::XpressProblem, control::Integer)
getcontrol(prob::XpressProblem, control::String)
getcontrol(prob::XpressProblem, control::Symbol)
Get parameter of any type
"""
function getcontrol(prob::XpressProblem, control::Integer)
# TODO: this function is not type stable
if convert(Int, control) in XPRS_INT_CONTROLS
return getintcontrol(prob, control)
elseif convert(Int, control) in XPRS_DBL_CONTROLS
return getdblcontrol(prob, control)
elseif convert(Int, control) in XPRS_STR_CONTROLS
return getstrcontrol(prob, control)
else
error("Unrecognized control parameter: $(control).")
end
end
getcontrol(prob::XpressProblem, control::Symbol) = getcontrol(prob, getproperty(Lib, Symbol("XPRS_$(String(control))")))
getcontrol(prob::XpressProblem, control::String) = getcontrol(prob, XPRS_ATTRIBUTES[control])
"""
setcontrol!(prob::XpressProblem, control::Symbol, val::Any)
setcontrol!(prob::XpressProblem, control::String, val::Any)
setcontrol!(prob::XpressProblem, control::Integer, val::Any)
Set parameter of any type
"""
setcontrol!(prob::XpressProblem, control::Symbol, val::Any) = setcontrol!(prob, getproperty(Lib, Symbol("XPRS_$(String(control))")), val::Any)
setcontrol!(prob::XpressProblem, control::String, val::Any) = setcontrol!(prob, XPRS_ATTRIBUTES[control], val)
setcontrol!(prob::XpressProblem, control::Integer, val::Any) = setcontrol!(prob, Cint(control), val)
function setcontrol!(prob::XpressProblem, control::Cint, val)
# TODO: dispatch on Val(control) instead?
if convert(Int, control) in XPRS_INT_CONTROLS
if isinteger(val)
setintcontrol(prob, control, convert(Int, val))
else
error("Expected and integer and got $val")
end
elseif convert(Int, control) in XPRS_DBL_CONTROLS
setdblcontrol(prob, control, val)
elseif convert(Int, control) in XPRS_STR_CONTROLS
setstrcontrol(prob, control, val)
else
error("Unrecognized control parameter: $(control).")
end
end
"""
setparams!(prob::XpressProblem;args...)
Set multiple parameters of any type
"""
function setcontrols!(prob::XpressProblem; args...)
for (control, val) in args
setcontrols!(prob, getproperty(Lib, control), val)
end
end
XPRS_STR_CONTROLS = [
Lib.XPRS_MPSRHSNAME,
Lib.XPRS_MPSOBJNAME,
Lib.XPRS_MPSRANGENAME,
Lib.XPRS_MPSBOUNDNAME,
Lib.XPRS_OUTPUTMASK,
]
XPRS_DBL_CONTROLS = [
Lib.XPRS_MATRIXTOL
Lib.XPRS_PIVOTTOL
Lib.XPRS_FEASTOL
Lib.XPRS_OUTPUTTOL
Lib.XPRS_SOSREFTOL
Lib.XPRS_OPTIMALITYTOL
Lib.XPRS_ETATOL
Lib.XPRS_RELPIVOTTOL
Lib.XPRS_MIPTOL
Lib.XPRS_MIPTOLTARGET
Lib.XPRS_MIPADDCUTOFF
Lib.XPRS_MIPABSCUTOFF
Lib.XPRS_MIPRELCUTOFF
Lib.XPRS_PSEUDOCOST
Lib.XPRS_PENALTY
Lib.XPRS_BIGM
Lib.XPRS_MIPABSSTOP
Lib.XPRS_MIPRELSTOP
Lib.XPRS_CROSSOVERACCURACYTOL
Lib.XPRS_BAROBJSCALE
Lib.XPRS_BARRHSSCALE
Lib.XPRS_CHOLESKYTOL
Lib.XPRS_BARGAPSTOP
Lib.XPRS_BARDUALSTOP
Lib.XPRS_BARPRIMALSTOP
Lib.XPRS_BARSTEPSTOP
Lib.XPRS_ELIMTOL
Lib.XPRS_PERTURB
Lib.XPRS_MARKOWITZTOL
Lib.XPRS_MIPABSGAPNOTIFY
Lib.XPRS_MIPRELGAPNOTIFY
Lib.XPRS_PPFACTOR
Lib.XPRS_REPAIRINDEFINITEQMAX
Lib.XPRS_BARGAPTARGET
Lib.XPRS_SBEFFORT
Lib.XPRS_HEURDIVERANDOMIZE
Lib.XPRS_HEURSEARCHEFFORT
Lib.XPRS_CUTFACTOR
Lib.XPRS_EIGENVALUETOL
Lib.XPRS_INDLINBIGM
Lib.XPRS_TREEMEMORYSAVINGTARGET
Lib.XPRS_GLOBALFILEBIAS
Lib.XPRS_INDPRELINBIGM
Lib.XPRS_RELAXTREEMEMORYLIMIT
Lib.XPRS_MIPABSGAPNOTIFYOBJ
Lib.XPRS_MIPABSGAPNOTIFYBOUND
Lib.XPRS_PRESOLVEMAXGROW
Lib.XPRS_HEURSEARCHTARGETSIZE
Lib.XPRS_CROSSOVERRELPIVOTTOL
Lib.XPRS_CROSSOVERRELPIVOTTOLSAFE
Lib.XPRS_DETLOGFREQ
Lib.XPRS_MAXIMPLIEDBOUND
Lib.XPRS_FEASTOLTARGET
Lib.XPRS_OPTIMALITYTOLTARGET
Lib.XPRS_PRECOMPONENTSEFFORT
]
XPRS_INT_CONTROLS = [
Lib.XPRS_EXTRAROWS
Lib.XPRS_EXTRACOLS
Lib.XPRS_LPITERLIMIT
Lib.XPRS_LPLOG
Lib.XPRS_SCALING
Lib.XPRS_PRESOLVE
Lib.XPRS_CRASH
Lib.XPRS_PRICINGALG
Lib.XPRS_INVERTFREQ
Lib.XPRS_INVERTMIN
Lib.XPRS_MAXNODE
Lib.XPRS_MAXTIME
Lib.XPRS_MAXMIPSOL
Lib.XPRS_DEFAULTALG
Lib.XPRS_VARSELECTION
Lib.XPRS_NODESELECTION
Lib.XPRS_BACKTRACK
Lib.XPRS_MIPLOG
Lib.XPRS_KEEPNROWS
Lib.XPRS_MPSECHO
Lib.XPRS_MAXPAGELINES
Lib.XPRS_OUTPUTLOG
Lib.XPRS_BARSOLUTION
Lib.XPRS_CACHESIZE
Lib.XPRS_CROSSOVER
Lib.XPRS_BARITERLIMIT
Lib.XPRS_CHOLESKYALG
Lib.XPRS_BAROUTPUT
Lib.XPRS_CSTYLE
Lib.XPRS_EXTRAMIPENTS
Lib.XPRS_REFACTOR
Lib.XPRS_BARTHREADS
Lib.XPRS_KEEPBASIS
Lib.XPRS_VERSION
Lib.XPRS_BIGMMETHOD
Lib.XPRS_MPSNAMELENGTH
Lib.XPRS_PRESOLVEOPS
Lib.XPRS_MIPPRESOLVE
Lib.XPRS_MIPTHREADS
Lib.XPRS_BARORDER
Lib.XPRS_BREADTHFIRST
Lib.XPRS_AUTOPERTURB
Lib.XPRS_DENSECOLLIMIT
Lib.XPRS_CALLBACKFROMMASTERTHREAD
Lib.XPRS_MAXMCOEFFBUFFERELEMS
Lib.XPRS_REFINEOPS
Lib.XPRS_LPREFINEITERLIMIT
Lib.XPRS_MIPREFINEITERLIMIT
Lib.XPRS_DUALIZEOPS
Lib.XPRS_PRESORT
Lib.XPRS_PREPERMUTE
Lib.XPRS_PREPERMUTESEED
Lib.XPRS_MAXMEMORY
Lib.XPRS_CUTFREQ
Lib.XPRS_SYMSELECT
Lib.XPRS_SYMMETRY
Lib.XPRS_LPTHREADS
Lib.XPRS_MIQCPALG
Lib.XPRS_QCCUTS
Lib.XPRS_QCROOTALG
Lib.XPRS_ALGAFTERNETWORK
Lib.XPRS_TRACE
Lib.XPRS_MAXIIS
Lib.XPRS_CPUTIME
Lib.XPRS_COVERCUTS
Lib.XPRS_GOMCUTS
Lib.XPRS_MPSFORMAT
Lib.XPRS_CUTSTRATEGY
Lib.XPRS_CUTDEPTH
Lib.XPRS_TREECOVERCUTS
Lib.XPRS_TREEGOMCUTS
Lib.XPRS_CUTSELECT
Lib.XPRS_TREECUTSELECT
Lib.XPRS_DUALIZE
Lib.XPRS_DUALGRADIENT
Lib.XPRS_SBITERLIMIT
Lib.XPRS_SBBEST
Lib.XPRS_MAXCUTTIME
Lib.XPRS_ACTIVESET
Lib.XPRS_BARINDEFLIMIT
Lib.XPRS_HEURSTRATEGY
Lib.XPRS_HEURFREQ
Lib.XPRS_HEURDEPTH
Lib.XPRS_HEURMAXSOL
Lib.XPRS_HEURNODES
Lib.XPRS_LNPBEST
Lib.XPRS_LNPITERLIMIT
Lib.XPRS_BRANCHCHOICE
Lib.XPRS_BARREGULARIZE
Lib.XPRS_SBSELECT
Lib.XPRS_LOCALCHOICE
Lib.XPRS_LOCALBACKTRACK
Lib.XPRS_DUALSTRATEGY
Lib.XPRS_L1CACHE
Lib.XPRS_HEURDIVESTRATEGY
Lib.XPRS_HEURSELECT
Lib.XPRS_BARSTART
Lib.XPRS_BARNUMSTABILITY
Lib.XPRS_BARORDERTHREADS
Lib.XPRS_EXTRASETS
Lib.XPRS_FEASIBILITYPUMP
Lib.XPRS_PRECOEFELIM
Lib.XPRS_PREDOMCOL
Lib.XPRS_HEURSEARCHFREQ
Lib.XPRS_HEURDIVESPEEDUP
Lib.XPRS_SBESTIMATE
Lib.XPRS_BARCORES
Lib.XPRS_MAXCHECKSONMAXTIME
Lib.XPRS_MAXCHECKSONMAXCUTTIME
Lib.XPRS_HISTORYCOSTS
Lib.XPRS_ALGAFTERCROSSOVER
Lib.XPRS_LINELENGTH
Lib.XPRS_MUTEXCALLBACKS
Lib.XPRS_BARCRASH
Lib.XPRS_HEURDIVESOFTROUNDING
Lib.XPRS_HEURSEARCHROOTSELECT
Lib.XPRS_HEURSEARCHTREESELECT
Lib.XPRS_MPS18COMPATIBLE
Lib.XPRS_ROOTPRESOLVE
Lib.XPRS_CROSSOVERDRP
Lib.XPRS_FORCEOUTPUT
Lib.XPRS_DETERMINISTIC
Lib.XPRS_PREPROBING
Lib.XPRS_EXTRAQCELEMENTS
Lib.XPRS_EXTRAQCROWS
Lib.XPRS_TREEMEMORYLIMIT
Lib.XPRS_TREECOMPRESSION
Lib.XPRS_TREEDIAGNOSTICS
Lib.XPRS_MAXGLOBALFILESIZE
Lib.XPRS_REPAIRINFEASMAXTIME
Lib.XPRS_IFCHECKCONVEXITY
Lib.XPRS_PRIMALUNSHIFT
Lib.XPRS_REPAIRINDEFINITEQ
Lib.XPRS_MAXLOCALBACKTRACK
Lib.XPRS_USERSOLHEURISTIC
Lib.XPRS_FORCEPARALLELDUAL
Lib.XPRS_BACKTRACKTIE
Lib.XPRS_BRANCHDISJ
Lib.XPRS_MIPFRACREDUCE
Lib.XPRS_CONCURRENTTHREADS
Lib.XPRS_MAXSCALEFACTOR
Lib.XPRS_HEURTHREADS
Lib.XPRS_THREADS
Lib.XPRS_HEURBEFORELP
Lib.XPRS_PREDOMROW
Lib.XPRS_BRANCHSTRUCTURAL
Lib.XPRS_QUADRATICUNSHIFT
Lib.XPRS_BARPRESOLVEOPS
Lib.XPRS_QSIMPLEXOPS
Lib.XPRS_CONFLICTCUTS
Lib.XPRS_PREPROTECTDUAL
Lib.XPRS_CORESPERCPU
Lib.XPRS_SLEEPONTHREADWAIT
Lib.XPRS_PREDUPROW
Lib.XPRS_CPUPLATFORM
Lib.XPRS_BARALG
Lib.XPRS_TREEPRESOLVE
Lib.XPRS_TREEPRESOLVE_KEEPBASIS
Lib.XPRS_TREEPRESOLVEOPS
Lib.XPRS_LPLOGSTYLE
Lib.XPRS_RANDOMSEED
Lib.XPRS_TREEQCCUTS
Lib.XPRS_PRELINDEP
Lib.XPRS_DUALTHREADS
Lib.XPRS_PREOBJCUTDETECT
Lib.XPRS_PREBNDREDQUAD
Lib.XPRS_PREBNDREDCONE
Lib.XPRS_PRECOMPONENTS
Lib.XPRS_MAXMIPTASKS
Lib.XPRS_MIPTERMINATIONMETHOD
Lib.XPRS_PRECONEDECOMP
Lib.XPRS_HEURSEARCHROOTCUTFREQ
Lib.XPRS_EXTRAELEMS
Lib.XPRS_EXTRAPRESOLVE
Lib.XPRS_EXTRASETELEMS
]
# TODO list attributes by type
# originals are more important to be used everywhere, presolved are actually
# secondary
n_variables(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALCOLS)
n_constraints(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALROWS)
n_special_ordered_sets(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALSETS)
n_quadratic_constraints(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALQCONSTRAINTS)
n_non_zero_elements(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ELEMS)
n_quadratic_elements(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALQELEMS)
n_quadratic_row_coefficients(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALQCELEMS)
n_entities(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALMIPENTS)
n_setmembers(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALSETMEMBERS)
n_original_variables(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALCOLS)
n_original_constraints(prob::XpressProblem) = getintattrib(prob, Lib.XPRS_ORIGINALROWS)
objective_sense(prob::XpressProblem) = getdblattrib(prob, Lib.XPRS_OBJSENSE) == Lib.XPRS_OBJ_MINIMIZE ? :minimize : :maximize
# derived attribute functions
"""
n_linear_constraints(prob::XpressProblem)
Return the number of purely linear contraints in the XpressProblem
"""
n_linear_constraints(prob::XpressProblem) = n_constraints(prob) - n_quadratic_constraints(prob)
"""
is_qcp(prob::XpressProblem)
Return `true` if there are quadratic constraints in the XpressProblem
"""
is_quadratic_constraints(prob::XpressProblem) = n_quadratic_constraints(prob) > 0
"""
is_mip(prob::XpressProblem)
Return `true` if there are integer entities in the XpressProblem
"""
is_mixedinteger(prob::XpressProblem) = (n_entities(prob) + n_special_ordered_sets(prob)) > 0
"""
is_quadratic_objective(prob::XpressProblem)
Return `true` if there are quadratic terms in the objective in the XpressProblem
"""
is_quadratic_objective(prob::XpressProblem) = n_quadratic_elements(prob) > 0
"""
problem_type(prob::XpressProblem)
Return a symbol enconding the type of the problem.]
Options are: `:LP`, `:QP` and `:QCP`
"""
function problem_type(prob::XpressProblem)
is_quadratic_constraints(prob) ? (:QCP) :
is_quadratic_objective(prob) ? (:QP) : (:LP)
end
"""
show(io::IO, prob::XpressProblem)
Prints a simplified problem description
"""
function Base.show(io::IO, prob::XpressProblem)
println(io, "Xpress Problem:" )
if is_mixedinteger(prob)
println(io, " type : $(problem_type(prob)) (MIP)")
else
println(io, " type : $(problem_type(prob))")
end
println(io, " sense : $(objective_sense(prob))")
println(io, " number of variables = $(n_variables(prob))")
println(io, " number of linear constraints = $(n_linear_constraints(prob))")
println(io, " number of quadratic constraints = $(n_quadratic_constraints(prob))")
println(io, " number of sos constraints = $(n_special_ordered_sets(prob))")
println(io, " number of non-zero coeffs = $(n_non_zero_elements(prob))")
println(io, " number of non-zero qp objective terms = $(n_quadratic_elements(prob))")
println(io, " number of non-zero qp constraint terms = $(n_quadratic_row_coefficients(prob))")
println(io, " number of integer entities = $(n_entities(prob))")
end
"""
delq!(model::Model)
delete all quadritic terms formthe objective
"""
function delq!(prob::XpressProblem)
n = n_variables(prob)
k = n*(n+1)÷2
qr = zeros(Cint, k)
qc = zeros(Cint, k)
qv = zeros(k)
cont = 1
for i in 1:n
for j in i:n
qr[cont] = i
qc[cont] = j
cont += 1
end
end
Xpress.chgmqobj(prob, qr, qc, qv)
end
const MIPSTATUS_STRING = Dict{Int,String}(
Xpress.Lib.XPRS_MIP_NOT_LOADED => "0 Problem has not been loaded ( XPRS_MIP_NOT_LOADED).",
Xpress.Lib.XPRS_MIP_LP_NOT_OPTIMAL => "1 Global search incomplete - the initial continuous relaxation has not been solved and no integer solution has been found ( XPRS_MIP_LP_NOT_OPTIMAL).",
Xpress.Lib.XPRS_MIP_LP_OPTIMAL => "2 Global search incomplete - the initial continuous relaxation has been solved and no integer solution has been found ( XPRS_MIP_LP_OPTIMAL).",
Xpress.Lib.XPRS_MIP_NO_SOL_FOUND => "3 Global search incomplete - no integer solution found ( XPRS_MIP_NO_SOL_FOUND).",
Xpress.Lib.XPRS_MIP_SOLUTION => "4 Global search incomplete - an integer solution has been found ( XPRS_MIP_SOLUTION).",
Xpress.Lib.XPRS_MIP_INFEAS => "5 Global search complete - no integer solution found ( XPRS_MIP_INFEAS).",
Xpress.Lib.XPRS_MIP_OPTIMAL => "6 Global search complete - integer solution found ( XPRS_MIP_OPTIMAL).",
Xpress.Lib.XPRS_MIP_UNBOUNDED => "7 Global search incomplete - the initial continuous relaxation was found to be unbounded. A solution may have been found ( XPRS_MIP_UNBOUNDED).",
)
function mip_solve_complete(stat)
stat in [Xpress.Lib.XPRS_MIP_INFEAS, Xpress.Lib.XPRS_MIP_OPTIMAL]
end
function mip_solve_stopped(stat)
stat in [Xpress.Lib.XPRS_MIP_INFEAS, Xpress.Lib.XPRS_MIP_OPTIMAL]
end
const LPSTATUS_STRING = Dict{Int,String}(
Xpress.Lib.XPRS_LP_UNSTARTED => "0 Unstarted ( XPRS_LP_UNSTARTED).",
Xpress.Lib.XPRS_LP_OPTIMAL => "1 Optimal ( XPRS_LP_OPTIMAL).",
Xpress.Lib.XPRS_LP_INFEAS => "2 Infeasible ( XPRS_LP_INFEAS).",
Xpress.Lib.XPRS_LP_CUTOFF => "3 Objective worse than cutoff ( XPRS_LP_CUTOFF).",
Xpress.Lib.XPRS_LP_UNFINISHED => "4 Unfinished ( XPRS_LP_UNFINISHED).",
Xpress.Lib.XPRS_LP_UNBOUNDED => "5 Unbounded ( XPRS_LP_UNBOUNDED).",
Xpress.Lib.XPRS_LP_CUTOFF_IN_DUAL => "6 Cutoff in dual ( XPRS_LP_CUTOFF_IN_DUAL).",
Xpress.Lib.XPRS_LP_UNSOLVED => "7 Problem could not be solved due to numerical issues. ( XPRS_LP_UNSOLVED).",
Xpress.Lib.XPRS_LP_NONCONVEX => "8 Problem contains quadratic data, which is not convex ( XPRS_LP_NONCONVEX).",
)
const STOPSTATUS_STRING = Dict{Int, String}(
Xpress.Lib.XPRS_STOP_NONE => "no interruption - the solve completed normally",
Xpress.Lib.XPRS_STOP_TIMELIMIT => "time limit hit",
Xpress.Lib.XPRS_STOP_CTRLC => "control C hit",
Xpress.Lib.XPRS_STOP_NODELIMIT => "node limit hit",
Xpress.Lib.XPRS_STOP_ITERLIMIT => "iteration limit hit",
Xpress.Lib.XPRS_STOP_MIPGAP => "MIP gap is sufficiently small",
Xpress.Lib.XPRS_STOP_SOLLIMIT => "solution limit hit",
Xpress.Lib.XPRS_STOP_USER => "user interrupt.",
)