Skip to content

Commit b51f9c3

Browse files
authored
Fix callback attributes in MOI.ListOfModelAttributesSet (#584)
1 parent d7f1459 commit b51f9c3

File tree

3 files changed

+88
-9
lines changed

3 files changed

+88
-9
lines changed

src/MOI_wrapper/MOI_callbacks.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,17 @@ function MOI.set(model::Optimizer, ::CallbackFunction, f::Function)
8787
_update_if_necessary(model)
8888
return
8989
end
90+
9091
MOI.supports(::Optimizer, ::CallbackFunction) = true
9192

93+
function MOI.set(model::Optimizer, ::CallbackFunction, ::Nothing)
94+
ret = GRBsetcallbackfunc(model, C_NULL, C_NULL)
95+
_check_ret(model, ret)
96+
model.generic_callback = nothing
97+
model.has_generic_callback = false
98+
return
99+
end
100+
92101
"""
93102
load_callback_variable_primal(cb_data, cb_where)
94103
@@ -187,8 +196,14 @@ function MOI.set(model::Optimizer, ::MOI.LazyConstraintCallback, cb::Function)
187196
model.lazy_callback = cb
188197
return
189198
end
199+
190200
MOI.supports(::Optimizer, ::MOI.LazyConstraintCallback) = true
191201

202+
function MOI.set(model::Optimizer, ::MOI.LazyConstraintCallback, ::Nothing)
203+
model.lazy_callback = nothing
204+
return
205+
end
206+
192207
function MOI.submit(
193208
model::Optimizer,
194209
cb::MOI.LazyConstraint{CallbackData},
@@ -223,6 +238,7 @@ function MOI.submit(
223238
_check_ret(model, ret)
224239
return
225240
end
241+
226242
MOI.supports(::Optimizer, ::MOI.LazyConstraint{CallbackData}) = true
227243

228244
# ==============================================================================
@@ -233,8 +249,14 @@ function MOI.set(model::Optimizer, ::MOI.UserCutCallback, cb::Function)
233249
model.user_cut_callback = cb
234250
return
235251
end
252+
236253
MOI.supports(::Optimizer, ::MOI.UserCutCallback) = true
237254

255+
function MOI.set(model::Optimizer, ::MOI.UserCutCallback, ::Nothing)
256+
model.user_cut_callback = nothing
257+
return
258+
end
259+
238260
function MOI.submit(
239261
model::Optimizer,
240262
cb::MOI.UserCut{CallbackData},
@@ -269,6 +291,7 @@ function MOI.submit(
269291
_check_ret(model, ret)
270292
return
271293
end
294+
272295
MOI.supports(::Optimizer, ::MOI.UserCut{CallbackData}) = true
273296

274297
# ==============================================================================
@@ -279,8 +302,14 @@ function MOI.set(model::Optimizer, ::MOI.HeuristicCallback, cb::Function)
279302
model.heuristic_callback = cb
280303
return
281304
end
305+
282306
MOI.supports(::Optimizer, ::MOI.HeuristicCallback) = true
283307

308+
function MOI.set(model::Optimizer, ::MOI.HeuristicCallback, ::Nothing)
309+
model.heuristic_callback = nothing
310+
return
311+
end
312+
284313
function MOI.submit(
285314
model::Optimizer,
286315
cb::MOI.HeuristicSolution{CallbackData},
@@ -310,4 +339,5 @@ function MOI.submit(
310339
# later in the optimization process.
311340
return MOI.HEURISTIC_SOLUTION_UNKNOWN
312341
end
342+
313343
MOI.supports(::Optimizer, ::MOI.HeuristicSolution{CallbackData}) = true

src/MOI_wrapper/MOI_wrapper.jl

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,18 @@ function MOI.get(model::Optimizer, ::MOI.ListOfModelAttributesSet)
779779
if MOI.get(model, MOI.Name()) != ""
780780
push!(attributes, MOI.Name())
781781
end
782+
if model.has_generic_callback
783+
push!(attributes, CallbackFunction())
784+
end
785+
if model.lazy_callback !== nothing
786+
push!(attributes, MOI.LazyConstraintCallback())
787+
end
788+
if model.user_cut_callback !== nothing
789+
push!(attributes, MOI.UserCutCallback())
790+
end
791+
if model.heuristic_callback !== nothing
792+
push!(attributes, MOI.HeuristicCallback())
793+
end
782794
return attributes
783795
end
784796

@@ -2702,10 +2714,10 @@ end
27022714
function MOI.optimize!(model::Optimizer)
27032715
_update_if_necessary(model, force = true)
27042716
# Initialize callbacks if necessary.
2705-
has_null_callback = false
2717+
set_temporary_callback = false
27062718
if _check_moi_callback_validity(model)
27072719
MOI.set(model, CallbackFunction(), _default_moi_callback(model))
2708-
model.has_generic_callback = false
2720+
set_temporary_callback = true
27092721
elseif model.enable_interrupts && !model.has_generic_callback
27102722
# From the docstring of disable_sigint, "External functions that do not
27112723
# call julia code or julia runtime automatically disable sigint during
@@ -2715,7 +2727,7 @@ function MOI.optimize!(model::Optimizer)
27152727
# https://github.com/JuliaLang/julia/issues/2622 --- set a null
27162728
# callback.
27172729
MOI.set(model, CallbackFunction(), (x, y) -> nothing)
2718-
has_null_callback = true
2730+
set_temporary_callback = true
27192731
end
27202732

27212733
# Catch [CTRL+C], even when Julia is run from a script not in interactive
@@ -2737,10 +2749,10 @@ function MOI.optimize!(model::Optimizer)
27372749
model.has_unbounded_ray =
27382750
MOI.get(model, MOI.PrimalStatus()) == MOI.INFEASIBILITY_CERTIFICATE
27392751

2740-
if has_null_callback
2752+
if set_temporary_callback
27412753
# See https://github.com/jump-dev/Gurobi.jl/issues/395 - avoid error
27422754
# from _check_moi_callback_validity upon next optimize! call.
2743-
model.has_generic_callback = false
2755+
MOI.set(model, CallbackFunction(), nothing)
27442756
end
27452757
return
27462758
end

test/MOI/MOI_callbacks.jl

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ using Gurobi
1010
using Random
1111
using Test
1212

13+
import MathOptInterface as MOI
14+
1315
function runtests()
1416
for name in names(@__MODULE__; all = true)
1517
if startswith("$(name)", "test_")
@@ -21,8 +23,6 @@ function runtests()
2123
return
2224
end
2325

24-
const MOI = Gurobi.MOI
25-
2626
const GRB_ENV =
2727
isdefined(Main, :GRB_ENV) ? Main.GRB_ENV : Gurobi.Env(output_flag = 0)
2828

@@ -122,6 +122,12 @@ function test_lazy_constraint_callback()
122122
@test lazy_called
123123
@test MOI.get(model, MOI.VariablePrimal(), x) == 1
124124
@test MOI.get(model, MOI.VariablePrimal(), y) == 2
125+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
126+
@test MOI.LazyConstraintCallback() in attrs
127+
MOI.set(model, MOI.LazyConstraintCallback(), nothing)
128+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
129+
@test !(MOI.LazyConstraintCallback() in attrs)
130+
return
125131
end
126132

127133
function test_lazy_constraint_callback_fractional()
@@ -148,6 +154,7 @@ function test_lazy_constraint_callback_fractional()
148154
MOI.optimize!(model)
149155
@test lazy_called_integer
150156
@test lazy_called_fractional
157+
return
151158
end
152159

153160
function test_lazy_constraint_callback_OptimizeInProgress()
@@ -170,7 +177,8 @@ function test_lazy_constraint_callback_OptimizeInProgress()
170177
)
171178
end,
172179
)
173-
return MOI.optimize!(model)
180+
MOI.optimize!(model)
181+
return
174182
end
175183

176184
function test_lazy_constraint_callback_UserCut()
@@ -193,6 +201,7 @@ function test_lazy_constraint_callback_UserCut()
193201
MOI.InvalidCallbackUsage(MOI.LazyConstraintCallback(), MOI.UserCut(cb)),
194202
MOI.optimize!(model)
195203
)
204+
return
196205
end
197206

198207
function test_lazy_constraint_callback_HeuristicSolution()
@@ -213,6 +222,7 @@ function test_lazy_constraint_callback_HeuristicSolution()
213222
),
214223
MOI.optimize!(model)
215224
)
225+
return
216226
end
217227

218228
function test_user_cut_callback()
@@ -245,6 +255,12 @@ function test_user_cut_callback()
245255
@test MOI.supports(model, MOI.UserCutCallback())
246256
MOI.optimize!(model)
247257
@test user_cut_submitted
258+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
259+
@test MOI.UserCutCallback() in attrs
260+
MOI.set(model, MOI.UserCutCallback(), nothing)
261+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
262+
@test !(MOI.UserCutCallback() in attrs)
263+
return
248264
end
249265

250266
function test_user_cut_callback_LazyConstraint()
@@ -267,6 +283,7 @@ function test_user_cut_callback_LazyConstraint()
267283
MOI.InvalidCallbackUsage(MOI.UserCutCallback(), MOI.LazyConstraint(cb)),
268284
MOI.optimize!(model)
269285
)
286+
return
270287
end
271288

272289
function test_user_cut_callback_HeuristicSolution()
@@ -287,6 +304,7 @@ function test_user_cut_callback_HeuristicSolution()
287304
),
288305
MOI.optimize!(model)
289306
)
307+
return
290308
end
291309

292310
function test_heuristic_callback()
@@ -329,6 +347,12 @@ function test_heuristic_callback()
329347
MOI.optimize!(model)
330348
@test solution_accepted
331349
@test solution_rejected
350+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
351+
@test MOI.HeuristicCallback() in attrs
352+
MOI.set(model, MOI.HeuristicCallback(), nothing)
353+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
354+
@test !(MOI.HeuristicCallback() in attrs)
355+
return
332356
end
333357

334358
function test_heuristic_callback_LazyConstraint()
@@ -354,6 +378,7 @@ function test_heuristic_callback_LazyConstraint()
354378
),
355379
MOI.optimize!(model)
356380
)
381+
return
357382
end
358383

359384
function test_heuristic_callback_UserCut()
@@ -376,6 +401,7 @@ function test_heuristic_callback_UserCut()
376401
MOI.InvalidCallbackUsage(MOI.HeuristicCallback(), MOI.UserCut(cb)),
377402
MOI.optimize!(model)
378403
)
404+
return
379405
end
380406

381407
function test_CallbackFunction_callback_OptimizeInProgress()
@@ -399,7 +425,8 @@ function test_CallbackFunction_callback_OptimizeInProgress()
399425
end,
400426
)
401427
@test MOI.supports(model, Gurobi.CallbackFunction())
402-
return MOI.optimize!(model)
428+
MOI.optimize!(model)
429+
return
403430
end
404431

405432
function test_CallbackFunction_callback_LazyConstraint()
@@ -443,6 +470,12 @@ function test_CallbackFunction_callback_LazyConstraint()
443470
@test Gurobi.GRB_CB_MESSAGE in cb_calls
444471
@test Gurobi.GRB_CB_PRESOLVE in cb_calls
445472
@test Gurobi.GRB_CB_MIPSOL in cb_calls
473+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
474+
@test Gurobi.CallbackFunction() in attrs
475+
MOI.set(model, Gurobi.CallbackFunction(), nothing)
476+
attrs = MOI.get(model, MOI.ListOfModelAttributesSet())
477+
@test !(Gurobi.CallbackFunction() in attrs)
478+
return
446479
end
447480

448481
function test_CallbackFunction_callback_UserCut()
@@ -490,6 +523,7 @@ function test_CallbackFunction_callback_UserCut()
490523
MOI.optimize!(model)
491524
@test user_cut_submitted
492525
@test Gurobi.GRB_CB_MIPNODE in cb_calls
526+
return
493527
end
494528

495529
function test_CallbackFunction_callback_HeuristicSolution()
@@ -548,6 +582,7 @@ function test_CallbackFunction_callback_HeuristicSolution()
548582
@test solution_rejected
549583
@test solution_unknown
550584
@test Gurobi.GRB_CB_MIPNODE in cb_calls
585+
return
551586
end
552587

553588
function test_CallbackFunction_CallbackNodeStatus()
@@ -565,6 +600,7 @@ function test_CallbackFunction_CallbackNodeStatus()
565600
)
566601
MOI.optimize!(model)
567602
@test unknown_reached
603+
return
568604
end
569605

570606
function test_CallbackFunction_broadcast()
@@ -584,6 +620,7 @@ function test_CallbackFunction_broadcast()
584620
MOI.optimize!(model)
585621
@test length(solutions) > 0
586622
@test length(solutions[1]) == length(x)
623+
return
587624
end
588625

589626
end # module TestCallbacks

0 commit comments

Comments
 (0)