Skip to content

Commit 992cf1f

Browse files
committed
Address mlubin's comments
1 parent 110374c commit 992cf1f

File tree

3 files changed

+67
-41
lines changed

3 files changed

+67
-41
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ model = Model(
5151

5252
**Note: previous versions of `GLPK.jl` required you to choose either `GLPKSolverLP` or `GLPKSolverMIP`. This is no longer needed; just use `GLPK.Optimizer`.**
5353

54+
## Pre-emptive checks
55+
56+
`GLPK.jl` has a lot of pre-emptive checks to catch cases where the C API might
57+
throw an uninformative error. However, in benchmarks, this takes a
58+
non-negligible amount of time (e.g. 20% in add_constraints). At the risk of
59+
possibly running into an uninformative error, you can run the following after
60+
importing GLPK:
61+
```julia
62+
using GLPK
63+
GLPK.jl_set_preemptive_check(false)
64+
```
65+
5466
[travis-img]: https://api.travis-ci.org/JuliaOpt/GLPK.jl.svg?branch=master
5567
[travis-url]: https://travis-ci.org/JuliaOpt/GLPK.jl
5668

src/MOI_wrapper.jl

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ mutable struct ConstraintInfo
4141
end
4242

4343
# Dummy callback function for internal use only. Responsible for updating the
44-
# objective bound and saving the mip gap.
45-
function __internal_callback__(tree::Ptr{Cvoid}, info::Ptr{Cvoid})
44+
# objective bound, saving the mip gap, and calling the user's callback.
45+
function _internal_callback(tree::Ptr{Cvoid}, info::Ptr{Cvoid})
4646
callback_data = unsafe_pointer_to_objref(info)::CallbackData
4747
model = callback_data.model
4848
callback_data.tree = tree
@@ -115,11 +115,6 @@ mutable struct Optimizer <: MOI.ModelLike
115115
See the GLPK pdf documentation for a full list of parameters.
116116
"""
117117
function Optimizer(; presolve = false, method = SIMPLEX, kwargs...)
118-
# GLPK.jl has a lot of pre-emptive checks to catch cases where the C API
119-
# might throw an uninformative error. However, in benchmarks, this takes
120-
# a non-negligible amount of time (e.g. 20% in add_constraints). Until
121-
# someone complains otherwise, we're going to disable these checks
122-
GLPK.jl_set_preemptive_check(false)
123118
model = new()
124119
model.presolve = presolve
125120
model.method = method
@@ -135,10 +130,10 @@ mutable struct Optimizer <: MOI.ModelLike
135130
model.intopt_param = GLPK.IntoptParam()
136131
model.simplex_param = GLPK.SimplexParam()
137132

138-
# We initialize a default callback (__internal_callback__) to manage the
139-
# user's callback, and to update the objective bound.
133+
# We initialize a default callback (_internal_callback) to manage the
134+
# user's callback, and to update the objective bound and MIP gap.
140135
model.callback_data = CallbackData(model)
141-
model.intopt_param.cb_func = @cfunction(__internal_callback__, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}))
136+
model.intopt_param.cb_func = @cfunction(_internal_callback, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}))
142137
model.intopt_param.cb_info = pointer_from_objref(model.callback_data)
143138
model.callback_function = (cb_data) -> nothing
144139

@@ -286,21 +281,28 @@ MOI.supports(::Optimizer, ::MOI.RawStatusString) = true
286281
MOI.supports(::Optimizer, ::MOI.RawParameter) = true
287282

288283
function MOI.set(model::Optimizer, param::MOI.RawParameter, value)
289-
model.params[param.name] = value
290-
set_parameter(model, param.name, value)
284+
if typeof(param.name) != String
285+
error("GLPK.jl requires strings as arguments to `RawParameter`.")
286+
end
287+
model.params[Symbol(param.name)] = value
288+
set_parameter(model, Symbol(param.name), value)
291289
return
292290
end
293291

294292
function MOI.get(model::Optimizer, param::MOI.RawParameter)
295-
if (model.method == SIMPLEX || model.method == EXACT) && param.name in fieldnames(GLPK.SimplexParam)
296-
return getfield(model.simplex_param, param.name)
297-
elseif model.method == INTERIOR && param.name in fieldnames(GLPK.InteriorParam)
298-
return getfield(model.interior_param, param.name)
293+
if typeof(param.name) != String
294+
error("GLPK.jl requires strings as arguments to `RawParameter`.")
295+
end
296+
name = Symbol(param.name)
297+
if (model.method == SIMPLEX || model.method == EXACT) && name in fieldnames(GLPK.SimplexParam)
298+
return getfield(model.simplex_param, name)
299+
elseif model.method == INTERIOR && name in fieldnames(GLPK.InteriorParam)
300+
return getfield(model.interior_param, name)
299301
end
300-
if param.name in fieldnames(GLPK.IntoptParam)
301-
return getfield(model.intopt_param, param.name)
302+
if name in fieldnames(GLPK.IntoptParam)
303+
return getfield(model.intopt_param, name)
302304
end
303-
error("Unable to get RawParameter. Invalid name: $(param.name)")
305+
error("Unable to get RawParameter. Invalid name: $(name)")
304306
end
305307

306308
MOI.Utilities.supports_default_copy_to(::Optimizer, ::Bool) = true
@@ -432,7 +434,7 @@ end
432434
function _rebuild_name_to_variable(model::Optimizer)
433435
model.name_to_variable = Dict{String, MOI.VariableIndex}()
434436
for (index, info) in model.variable_info
435-
if info.name == ""
437+
if isempty(info.name)
436438
continue
437439
end
438440
if haskey(model.name_to_variable, info.name)
@@ -719,7 +721,17 @@ function _set_variable_bound(
719721
else
720722
upper >= GLPK.DBL_MAX ? GLPK.LO : GLPK.DB
721723
end
722-
GLPK.set_col_bnds(model.inner, column, bound_type, lower, upper)
724+
if upper < lower
725+
# Here, we disable GLPK's pre-emptive checks, because otherwise GLPK
726+
# will through an error complaining about invalid bounds when `upper <
727+
# lower`. This let's us throw `INFEASIBLE` instead of erroring.
728+
prev_preemptive = GLPK.jl_get_preemptive_check()
729+
GLPK.jl_set_preemptive_check(false)
730+
GLPK.set_col_bnds(model.inner, column, bound_type, lower, upper)
731+
GLPK.jl_set_preemptive_check(prev_preemptive)
732+
else
733+
GLPK.set_col_bnds(model.inner, column, bound_type, lower, upper)
734+
end
723735
return
724736
end
725737

@@ -834,7 +846,7 @@ function MOI.add_constraint(
834846
info = _info(model, f.variable)
835847
# See https://github.com/JuliaOpt/GLPKMathProgInterface.jl/pull/15
836848
# for why this is necesary. GLPK interacts weirdly with binary variables and
837-
# bound modification. So lets set binary variables as "Integer" with [0,1]
849+
# bound modification. So let's set binary variables as "Integer" with [0,1]
838850
# bounds that we enforce just before solve.
839851
GLPK.set_col_kind(model.inner, info.column, GLPK.IV)
840852
info.type = BINARY
@@ -1137,7 +1149,7 @@ function MOI.get(
11371149
elseif info.type in _type_enums(S)
11381150
constraint_name = info.type_constraint_name
11391151
end
1140-
if constraint_name == ""
1152+
if isempty(constraint_name)
11411153
continue
11421154
elseif constraint_name == name
11431155
if index === nothing
@@ -1173,7 +1185,7 @@ end
11731185

11741186
function _rebuild_name_to_constraint_index_util(model::Optimizer, dict, F)
11751187
for (index, info) in dict
1176-
info.name == "" && continue
1188+
isempty(info.name) && continue
11771189
if haskey(model.name_to_constraint_index, info.name)
11781190
model.name_to_constraint_index = nothing
11791191
error("Duplicate variable name detected: $(info.name)")

test/MOI_wrapper.jl

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -256,31 +256,33 @@ end
256256
@testset "RawParameter" begin
257257
model = GLPK.Optimizer(method = GLPK.SIMPLEX)
258258
exception = ErrorException("Invalid option: cb_func. Use the MOI attribute `GLPK.CallbackFunction` instead.")
259-
@test_throws exception MOI.set(model, MOI.RawParameter(:cb_func), (cb) -> nothing)
260-
MOI.set(model, MOI.RawParameter(:tm_lim), 100)
261-
@test MOI.get(model, MOI.RawParameter(:tm_lim)) == 100
262-
@test_throws ErrorException MOI.set(model, MOI.RawParameter(:bad), 1)
263-
@test_throws ErrorException MOI.get(model, MOI.RawParameter(:bad))
259+
@test_throws exception MOI.set(model, MOI.RawParameter("cb_func"), (cb) -> nothing)
260+
MOI.set(model, MOI.RawParameter("tm_lim"), 100)
261+
@test MOI.get(model, MOI.RawParameter("tm_lim")) == 100
262+
@test_throws ErrorException MOI.get(model, MOI.RawParameter(:tm_lim))
263+
@test_throws ErrorException MOI.set(model, MOI.RawParameter(:tm_lim), 120)
264+
@test_throws ErrorException MOI.set(model, MOI.RawParameter("bad"), 1)
265+
@test_throws ErrorException MOI.get(model, MOI.RawParameter("bad"))
264266

265267
model = GLPK.Optimizer(method = GLPK.INTERIOR)
266268
exception = ErrorException("Invalid option: cb_func. Use the MOI attribute `GLPK.CallbackFunction` instead.")
267-
@test_throws exception MOI.set(model, MOI.RawParameter(:cb_func), (cb) -> nothing)
268-
MOI.set(model, MOI.RawParameter(:tm_lim), 100)
269-
@test MOI.get(model, MOI.RawParameter(:tm_lim)) == 100
270-
@test_throws ErrorException MOI.set(model, MOI.RawParameter(:bad), 1)
271-
@test_throws ErrorException MOI.get(model, MOI.RawParameter(:bad))
269+
@test_throws exception MOI.set(model, MOI.RawParameter("cb_func"), (cb) -> nothing)
270+
MOI.set(model, MOI.RawParameter("tm_lim"), 100)
271+
@test MOI.get(model, MOI.RawParameter("tm_lim")) == 100
272+
@test_throws ErrorException MOI.set(model, MOI.RawParameter("bad"), 1)
273+
@test_throws ErrorException MOI.get(model, MOI.RawParameter("bad"))
272274

273275
model = GLPK.Optimizer(method = GLPK.EXACT)
274276
exception = ErrorException("Invalid option: cb_func. Use the MOI attribute `GLPK.CallbackFunction` instead.")
275-
@test_throws exception MOI.set(model, MOI.RawParameter(:cb_func), (cb) -> nothing)
276-
MOI.set(model, MOI.RawParameter(:tm_lim), 100)
277-
@test MOI.get(model, MOI.RawParameter(:tm_lim)) == 100
278-
@test_throws ErrorException MOI.set(model, MOI.RawParameter(:bad), 1)
279-
@test_throws ErrorException MOI.get(model, MOI.RawParameter(:bad))
277+
@test_throws exception MOI.set(model, MOI.RawParameter("cb_func"), (cb) -> nothing)
278+
MOI.set(model, MOI.RawParameter("tm_lim"), 100)
279+
@test MOI.get(model, MOI.RawParameter("tm_lim")) == 100
280+
@test_throws ErrorException MOI.set(model, MOI.RawParameter("bad"), 1)
281+
@test_throws ErrorException MOI.get(model, MOI.RawParameter("bad"))
280282

281283
model = GLPK.Optimizer()
282-
MOI.set(model, MOI.RawParameter(:mip_gap), 0.001)
283-
@test MOI.get(model, MOI.RawParameter(:mip_gap)) == 0.001
284+
MOI.set(model, MOI.RawParameter("mip_gap"), 0.001)
285+
@test MOI.get(model, MOI.RawParameter("mip_gap")) == 0.001
284286
end
285287

286288
@testset "RelativeGap" begin

0 commit comments

Comments
 (0)