Skip to content

Commit a901911

Browse files
authored
Add CallbackNodeStatus (#162)
* Add CallbackNodeStatus * Update Project.toml
1 parent 93cc698 commit a901911

File tree

3 files changed

+63
-2
lines changed

3 files changed

+63
-2
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "GLPK"
22
uuid = "60bf3e95-4087-53dc-ae20-288a0d20c6a6"
33
repo = "https://github.com/jump-dev/GLPK.jl.git"
4-
version = "0.14.3"
4+
version = "0.14.4"
55

66
[deps]
77
BinaryProvider = "b99e7846-7c00-51b0-8f62-c81ae34c0232"
@@ -14,7 +14,7 @@ MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
1414
BinaryProvider = "~0.5"
1515
CEnum = "0.3, 0.4"
1616
GLPK_jll = "~4.64.0"
17-
MathOptInterface = "~0.9.15"
17+
MathOptInterface = "~0.9.19"
1818
julia = "1"
1919

2020
[extras]

src/MOI_wrapper/MOI_callbacks.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,45 @@ function MOI.get(
5050
return glp_get_col_prim(subproblem, _info(model, x).column)
5151
end
5252

53+
function MOI.get(model::Optimizer, attr::MOI.CallbackNodeStatus{CallbackData})
54+
reason = glp_ios_reason(attr.callback_data.tree)
55+
if reason == GLP_ISELECT
56+
return MOI.CALLBACK_NODE_STATUS_UNKNOWN
57+
elseif reason == GLP_IPREPRO
58+
return MOI.CALLBACK_NODE_STATUS_UNKNOWN
59+
elseif reason == GLP_IROWGEN
60+
# From the the GLPK documentation:
61+
#
62+
# The callback routine is called with the reason code GLP_IROWGEN if LP
63+
# relaxation of the current subproblem has just been solved to
64+
# optimality and its objective value is better than the best known
65+
# integer feasible solution.
66+
#
67+
# This can mean the solution is integer _or_ fractional, so we need to
68+
# check.
69+
subproblem = glp_ios_get_prob(attr.callback_data.tree)
70+
for info in values(model.variable_info)
71+
if info.type == CONTINUOUS
72+
continue
73+
end
74+
x = glp_get_col_prim(subproblem, info.column)
75+
if abs(x - round(Int, x)) > 1e-7
76+
return MOI.CALLBACK_NODE_STATUS_FRACTIONAL
77+
end
78+
end
79+
return MOI.CALLBACK_NODE_STATUS_INTEGER
80+
elseif reason == GLP_IHEUR
81+
return MOI.CALLBACK_NODE_STATUS_FRACTIONAL
82+
elseif reason == GLP_ICUTGEN
83+
return MOI.CALLBACK_NODE_STATUS_FRACTIONAL
84+
elseif reason == GLP_IBRANCH
85+
return MOI.CALLBACK_NODE_STATUS_FRACTIONAL
86+
else
87+
@assert reason == GLP_IBINGO
88+
return MOI.CALLBACK_NODE_STATUS_INTEGER
89+
end
90+
end
91+
5392
# ==============================================================================
5493
# MOI.LazyConstraint
5594
# ==============================================================================

test/MOI_callbacks.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ end
4848
x_val = MOI.get(model, MOI.CallbackVariablePrimal(cb_data), x)
4949
y_val = MOI.get(model, MOI.CallbackVariablePrimal(cb_data), y)
5050
@test MOI.supports(model, MOI.LazyConstraint(cb_data))
51+
status = MOI.get(model, MOI.CallbackNodeStatus(cb_data))
52+
if [x_val, y_val] round.(Int, [x_val, y_val]) atol=1e-7
53+
@test status == MOI.CALLBACK_NODE_STATUS_INTEGER
54+
else
55+
@test status == MOI.CALLBACK_NODE_STATUS_FRACTIONAL
56+
end
5157
if y_val - x_val > 1 + 1e-6
5258
MOI.submit(
5359
model,
@@ -145,6 +151,8 @@ end
145151
end
146152
end
147153
@test MOI.supports(model, MOI.UserCut(cb_data))
154+
status = MOI.get(model, MOI.CallbackNodeStatus(cb_data))
155+
@test status == MOI.CALLBACK_NODE_STATUS_FRACTIONAL
148156
if accumulated > 10.0
149157
MOI.submit(
150158
model,
@@ -221,6 +229,8 @@ end
221229
) == MOI.HEURISTIC_SOLUTION_REJECTED
222230
solution_rejected = true
223231
end
232+
status = MOI.get(model, MOI.CallbackNodeStatus(cb_data))
233+
@test status == MOI.CALLBACK_NODE_STATUS_FRACTIONAL
224234
end)
225235
@test MOI.supports(model, MOI.HeuristicCallback())
226236
MOI.optimize!(model)
@@ -296,6 +306,12 @@ end
296306
end
297307
x_val = MOI.get(model, MOI.CallbackVariablePrimal(cb_data), x)
298308
y_val = MOI.get(model, MOI.CallbackVariablePrimal(cb_data), y)
309+
status = MOI.get(model, MOI.CallbackNodeStatus(cb_data))
310+
if [x_val, y_val] round.(Int, [x_val, y_val]) atol=1e-7
311+
@test status == MOI.CALLBACK_NODE_STATUS_INTEGER
312+
else
313+
@test status == MOI.CALLBACK_NODE_STATUS_FRACTIONAL
314+
end
299315
if y_val - x_val > 1 + 1e-6
300316
MOI.submit(model, MOI.LazyConstraint(cb_data),
301317
MOI.ScalarAffineFunction{Float64}(
@@ -333,6 +349,8 @@ end
333349
reason = GLPK.glp_ios_reason(cb_data.tree)
334350
push!(cb_calls, reason)
335351
if reason != GLPK.GLP_ICUTGEN
352+
status = MOI.get(model, MOI.CallbackNodeStatus(cb_data))
353+
@test status !== nothing
336354
return
337355
end
338356
terms = MOI.ScalarAffineTerm{Float64}[]
@@ -352,6 +370,8 @@ end
352370
)
353371
user_cut_submitted = true
354372
end
373+
status = MOI.get(model, MOI.CallbackNodeStatus(cb_data))
374+
@test status == MOI.CALLBACK_NODE_STATUS_FRACTIONAL
355375
end)
356376
MOI.optimize!(model)
357377
@test user_cut_submitted
@@ -385,6 +405,8 @@ end
385405
) == MOI.HEURISTIC_SOLUTION_REJECTED
386406
solution_rejected = true
387407
end
408+
status = MOI.get(model, MOI.CallbackNodeStatus(cb_data))
409+
@test status == MOI.CALLBACK_NODE_STATUS_FRACTIONAL
388410
end)
389411
MOI.optimize!(model)
390412
@test solution_accepted

0 commit comments

Comments
 (0)