Skip to content

Commit

Permalink
Add Env(params::Dict) for setting parameters before GRBstartenv (#596)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Dec 1, 2024
1 parent 5a9b7d9 commit bfa6139
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 29 deletions.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,22 @@ end
# Note the need for GRB_ENV_REF[] not GRB_ENV_REF
create_optimizer() = Gurobi.Optimizer(GRB_ENV_REF[])

end
end # MyModule
```

## Pass parameters to an Environment

To set parameters in an environment before the environment is started, pass a
`Dict{String,Any}` that maps parameter names to values:

```julia
env = Gurobi.Env(
Dict{String,Any}(
"CSAppName" => "some name",
"CSManager" => "some url",
"CSPriority" => 10,
),
)
```

## Accessing Gurobi-specific attributes
Expand Down Expand Up @@ -368,7 +383,7 @@ optimize!(model)
See the [Gurobi documentation](https://www.gurobi.com/documentation/current/refman/cb_codes.html)
for other information that can be queried with `GRBcbget`.

### Common Performance Pitfall with JuMP
## Common Performance Pitfall with JuMP

Gurobi's API works differently than most solvers. Any changes to the model are
not applied immediately, but instead go sit in a internal buffer (making any
Expand Down
85 changes: 58 additions & 27 deletions src/MOI_wrapper/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,36 @@ mutable struct _NLConstraintInfo
end
end

"""
Env(
params::Dict{String,Any} = Dict{String,Any}();
started::Bool = true,
)
Create a new Gurobi environment object.
Optionally pass a `params` dictionary which sets parameters for the created
environment before starting.
## kwargs
The extra keyword argument `started` delays starting the environment if set to
`false`.
## Example
```julia
using JuMP, Gurobi
const GRB_ENV = Gurobi.Env(
Dict(
"ComputeServer" => "localhost:61000",
"OutputFlag" => 0,
);
started = true,
)
model = Model(() -> Gurobi.Optimizer(GRB_ENV))
```
"""
mutable struct Env
ptr_env::Ptr{Cvoid}
# These fields keep track of how many models the `Env` is used for to help
Expand All @@ -110,34 +140,41 @@ mutable struct Env
finalize_called::Bool
attached_models::Int

function Env(;
function Env(
params::Union{Nothing,Dict{String,Any}} = nothing;
started::Bool = true,
# These kwargs are provided for legacy backwards compatibility
output_flag::Int = 1,
memory_limit::Union{Nothing,Real} = nothing,
started::Bool = true,
)
a = Ref{Ptr{Cvoid}}()
ret = GRBemptyenv(a)
env = new(a[], false, 0)
_check_ret(env, ret)
ret = GRBsetintparam(env.ptr_env, GRB_INT_PAR_OUTPUTFLAG, output_flag)
_check_ret(env, ret)
if _GUROBI_VERSION >= v"9.5.0" && memory_limit !== nothing
ret = GRBsetdblparam(env, GRB_DBL_PAR_MEMLIMIT, memory_limit)
_check_ret(env, ret)
end
if started
ret = GRBstartenv(env.ptr_env)
end
finalizer(env) do e
e.finalize_called = true
if e.attached_models == 0
# Only finalize the model if there are no models using it.
GRBfreeenv(e.ptr_env)
e.ptr_env = C_NULL
end
return
end
if params === nothing
# These two parameters are provided for backwards compability
_set_param(env.ptr_env, GRB_INT_PAR_OUTPUTFLAG, 1)
if _GUROBI_VERSION >= v"9.5.0" && memory_limit !== nothing
_set_param(env.ptr_env, GRB_DBL_PAR_MEMLIMIT, memory_limit)
end
else
for (param_name, value) in params
_set_param(env.ptr_env, param_name, value)
end
end
if started
ret = GRBstartenv(env.ptr_env)
_check_ret(env, ret)
end
# Even if the loadenv fails, the pointer is still valid.
_check_ret(env, ret)
return env
end
end
Expand Down Expand Up @@ -170,22 +207,11 @@ function Env(
server_password::Union{String,Nothing} = nothing;
started::Bool = true,
)
env = Env(; started = false)
ret = GRBsetstrparam(env.ptr_env, GRB_STR_PAR_COMPUTESERVER, server_address)
_check_ret(env, ret)
params = Dict{String,Any}(GRB_STR_PAR_COMPUTESERVER => server_address)
if server_password !== nothing
ret = GRBsetstrparam(
env.ptr_env,
GRB_STR_PAR_SERVERPASSWORD,
server_password,
)
_check_ret(env, ret)
params[GRB_STR_PAR_SERVERPASSWORD] = server_password
end
if started
ret = GRBstartenv(env.ptr_env)
_check_ret(env, ret)
end
return env
return Env(params; started)
end

Base.cconvert(::Type{Ptr{Cvoid}}, x::Env) = x
Expand Down Expand Up @@ -685,6 +711,11 @@ function MOI.set(model::Optimizer, raw::MOI.RawOptimizerAttribute, value)
env = GRBgetenv(model)
param = raw.name
model.params[param] = value
_set_param(env, param, value)
return
end

function _set_param(env, param::String, value)
param_type = GRBgetparamtype(env, param)
ret = if param_type == -1
throw(MOI.UnsupportedAttribute(MOI.RawOptimizerAttribute(param)))
Expand Down
19 changes: 19 additions & 0 deletions test/MOI/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,25 @@ function test_multiple_solution_nonlinear_objective()
return
end

function test_Env()
function test_err(f)
try
f()
@assert false
catch err
@test occursin("Gurobi Error 10022:", err.msg)
end
end
test_err(() -> Gurobi.Env("localhost:1234"))
test_err(() -> Gurobi.Env("localhost:1234", "password"))
test_err(() -> Gurobi.Env("localhost:1234", "password"; started = true))
env = Gurobi.Env(; output_flag = 2, memory_limit = 1)
p = Ref{Cdouble}()
@test GRBgetdblparam(env, "MemLimit", p) == 0
@test p[] == 1.0
return
end

end # TestMOIWrapper

TestMOIWrapper.runtests()

0 comments on commit bfa6139

Please sign in to comment.