Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add c_column and documentation for the C API #526

Merged
merged 6 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 56 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ It has two components:

## Affiliation

This wrapper is maintained by the JuMP community with help from Gurobi.
This wrapper is maintained by the JuMP community with help from Gurobi.

If you encounter a problem with this interface, please either open an issue in
this repository directly or create a topic in the [Julia Discourse](https://discourse.julialang.org/c/domain/opt/13)
with the [`gurobi` tag](https://discourse.julialang.org/tag/gurobi).
with the [`gurobi` tag](https://discourse.julialang.org/tag/gurobi).

If you encounter a problem with the Gurobi solver, please post in Gurobi’s
[Community Forum](https://support.gurobi.com/hc/en-us/community/topics), or if
you are a commercial customer, please contact Gurobi directly through the
Expand Down Expand Up @@ -127,6 +127,58 @@ arguments are identical to the C API.
See the [Gurobi documentation](https://www.gurobi.com/documentation/current/refman/c_api_details.html)
for details.

As general rules when converting from Julia to C:

* When Gurobi requires the column index of a variable `x`, use
`Gurobi.c_column(model, x)`
* When Gurobi requires a `Ptr{T}` that holds one element, like `double *`,
use a `Ref{T}()`.
* When Gurobi requries a `Ptr{T}` that holds multiple elements, use
a `Vector{T}`.
* When Gurobi requires a `double`, use `Cdouble`
* When Gurobi requires an `int`, use `Cint`
* When Gurobi requires a `NULL`, use `C_NULL`

For example:

```julia
julia> import MathOptInterface as MOI

julia> using Gurobi

julia> model = Gurobi.Optimizer();

julia> x = MOI.add_variable(model)
MOI.VariableIndex(1)

julia> x_col = Gurobi.c_column(model, x)
0

julia> GRBupdatemodel(model)
0

julia> pValue = Ref{Cdouble}(NaN)
Base.RefValue{Float64}(NaN)

julia> GRBgetdblattrelement(model, "LB", x_col, pValue)
0

julia> pValue[]
-1.0e100

julia> GRBsetdblattrelement(model, "LB", x_col, 1.5)
0

julia> GRBupdatemodel(model)
0

julia> GRBgetdblattrelement(model, "LB", x_col, pValue)
0

julia> pValue[]
1.5
```

## Reusing the same Gurobi environment for multiple solves

When using this package via other packages such as [JuMP.jl](https://github.com/jump-dev/JuMP.jl),
Expand Down
4 changes: 2 additions & 2 deletions src/MOI_wrapper/MOI_indicator_constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ function MOI.add_constraint(
"be 1.0. Got $(term.coefficient).",
)
end
binvar = Cint(column(model, term.variable) - 1)
binvar = c_column(model, term.variable)
else
@assert row == 2
vars[i] = Cint(column(model, term.variable) - 1)
vars[i] = c_column(model, term.variable)
vals[i] = term.coefficient
i += 1
end
Expand Down
100 changes: 50 additions & 50 deletions src/MOI_wrapper/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@
)
i = 1
for term in f.terms
indices[i] = Cint(column(model, term.variable) - 1)
indices[i] = c_column(model, term.variable)
coefficients[i] = term.coefficient
i += 1
end
Expand Down Expand Up @@ -797,8 +797,8 @@
f::MOI.ScalarQuadraticFunction,
)
for (i, term) in enumerate(f.quadratic_terms)
I[i] = Cint(column(model, term.variable_1) - 1)
J[i] = Cint(column(model, term.variable_2) - 1)
I[i] = c_column(model, term.variable_1)
J[i] = c_column(model, term.variable_2)
V[i] = term.coefficient
# Gurobi returns a list of terms. MOI requires 0.5 x' Q x. So, to get
# from
Expand All @@ -815,7 +815,7 @@
end
end
for (i, term) in enumerate(f.affine_terms)
indices[i] = Cint(column(model, term.variable) - 1)
indices[i] = c_column(model, term.variable)
coefficients[i] = term.coefficient
end
return
Expand Down Expand Up @@ -858,15 +858,36 @@
end

"""
column(model::Optimizer, x::MOI.VariableIndex)
column(
model::Optimizer,
x::Union{MOI.VariableIndex,<:MOI.ConstraintIndex{MOI.VariableIndex}},
) --> Int

Return the 1-indexed column associated with `x`.

The C API requires 0-indexed columns.
For use with the C API, see `Gurobi.c_column`.
"""
function column(model::Optimizer, x::MOI.VariableIndex)
function column(
model::Optimizer,
x::Union{MOI.VariableIndex,<:MOI.ConstraintIndex{MOI.VariableIndex}},
)
return _info(model, x).column
end

odow marked this conversation as resolved.
Show resolved Hide resolved
"""
c_column(
model::Optimizer,
x::Union{MOI.VariableIndex,<:MOI.ConstraintIndex{MOI.VariableIndex}},
) --> Cint

Return the `Cint` 0-indexed column associated with `x` for use with the C API.
"""
function c_column(
model::Optimizer,
x::Union{MOI.VariableIndex,<:MOI.ConstraintIndex{MOI.VariableIndex}},
)
return Cint(column(model, x) - 1)
end

function _get_next_column(model::Optimizer)
model.next_column += 1
Expand Down Expand Up @@ -1296,20 +1317,6 @@
return throw(MOI.InvalidIndex(c))
end

"""
column(model::Optimizer, c::MOI.ConstraintIndex{MOI.VariableIndex, <:Any})

Return the 1-indexed column associated with `c`.

The C API requires 0-indexed columns.
"""
function column(
model::Optimizer,
c::MOI.ConstraintIndex{MOI.VariableIndex,<:Any},
)
return _info(model, c).column
end

function MOI.is_valid(
model::Optimizer,
c::MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}},
Expand Down Expand Up @@ -2509,7 +2516,7 @@
end

function MOI.add_constraint(model::Optimizer, f::MOI.VectorOfVariables, s::_SOS)
columns = Cint[column(model, v) - 1 for v in f.variables]
columns = Cint[c_column(model, v) for v in f.variables]
ret = GRBaddsos(
model,
1,
Expand Down Expand Up @@ -2894,9 +2901,8 @@
)
key = "Xn"
end
col = Cint(column(model, x) - 1)
valueP = Ref{Cdouble}()
ret = GRBgetdblattrelement(model, key, col, valueP)
ret = GRBgetdblattrelement(model, key, c_column(model, x), valueP)
_check_ret(model, ret)
return valueP[]
end
Expand Down Expand Up @@ -2988,7 +2994,7 @@
)
_throw_if_optimize_in_progress(model, attr)
MOI.check_result_index_bounds(model, attr)
col = Cint(column(model, c) - 1)
col = c_column(model, c)
if model.has_infeasibility_cert
dual = _farkas_variable_dual(model, col)
return min(dual, 0.0)
Expand Down Expand Up @@ -3022,7 +3028,7 @@
)
_throw_if_optimize_in_progress(model, attr)
MOI.check_result_index_bounds(model, attr)
col = Cint(column(model, c) - 1)
col = c_column(model, c)
if model.has_infeasibility_cert
dual = _farkas_variable_dual(model, col)
return max(dual, 0.0)
Expand Down Expand Up @@ -3056,7 +3062,7 @@
)
_throw_if_optimize_in_progress(model, attr)
MOI.check_result_index_bounds(model, attr)
col = Cint(column(model, c) - 1)
col = c_column(model, c)
if model.has_infeasibility_cert
return _farkas_variable_dual(model, col)
end
Expand All @@ -3073,7 +3079,7 @@
)
_throw_if_optimize_in_progress(model, attr)
MOI.check_result_index_bounds(model, attr)
col = Cint(column(model, c) - 1)
col = c_column(model, c)
if model.has_infeasibility_cert
return _farkas_variable_dual(model, col)
end
Expand Down Expand Up @@ -3479,7 +3485,7 @@
model,
1,
Ref{Cint}(_info(model, c).row - 1),
Ref{Cint}(column(model, chg.variable) - 1),
Ref{Cint}(c_column(model, chg.variable)),
Ref{Cdouble}(chg.new_coefficient),
)
_check_ret(model, ret)
Expand All @@ -3500,7 +3506,7 @@
coefs = Vector{Cdouble}(undef, nels)
for i in 1:nels
rows[i] = Cint(_info(model, cis[i]).row - 1)
cols[i] = Cint(column(model, changes[i].variable) - 1)
cols[i] = c_column(model, changes[i].variable)
coefs[i] = changes[i].new_coefficient
end
ret = GRBchgcoeffs(model, nels, rows, cols, coefs)
Expand All @@ -3518,7 +3524,7 @@
ret = GRBsetdblattrelement(
model,
"Obj",
Cint(column(model, chg.variable) - 1),
c_column(model, chg.variable),
chg.new_coefficient,
)
_check_ret(model, ret)
Expand All @@ -3537,7 +3543,7 @@
cols = Vector{Cint}(undef, nels)
coefs = Vector{Cdouble}(undef, nels)
for i in 1:nels
cols[i] = Cint(column(model, changes[i].variable) - 1)
cols[i] = c_column(model, changes[i].variable)
coefs[i] = changes[i].new_coefficient
end
ret = GRBsetdblattrlist(model, "Obj", nels, cols, coefs)
Expand Down Expand Up @@ -3570,7 +3576,7 @@
row::Int,
)
rows = fill(Cint(row - 1), length(replacement.terms))
cols = Cint[column(model, t.variable) - 1 for t in replacement.terms]
cols = Cint[c_column(model, t.variable) for t in replacement.terms]
coefs = MOI.coefficient.(replacement.terms)
ret = GRBchgcoeffs(model, length(cols), rows, cols, coefs)
_check_ret(model, ret)
Expand Down Expand Up @@ -3603,13 +3609,13 @@
)
# First, zero out the old constraint function terms.
rows = fill(Cint(row - 1), length(previous.terms))
cols = Cint[column(model, t.variable) - 1 for t in previous.terms]
cols = Cint[c_column(model, t.variable) for t in previous.terms]

Check warning on line 3612 in src/MOI_wrapper/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/MOI_wrapper/MOI_wrapper.jl#L3612

Added line #L3612 was not covered by tests
coefs = fill(0.0, length(previous.terms))
ret = GRBchgcoeffs(model, length(cols), rows, cols, coefs)
_check_ret(model, ret)
# Next, set the new constraint function terms.
rows = fill(Cint(row - 1), length(replacement.terms))
cols = Cint[column(model, t.variable) - 1 for t in replacement.terms]
cols = Cint[c_column(model, t.variable) for t in replacement.terms]

Check warning on line 3618 in src/MOI_wrapper/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/MOI_wrapper/MOI_wrapper.jl#L3618

Added line #L3618 was not covered by tests
coefs = MOI.coefficient.(replacement.terms)
ret = GRBchgcoeffs(model, length(cols), rows, cols, coefs)
_check_ret(model, ret)
Expand Down Expand Up @@ -3700,8 +3706,7 @@
)
_update_if_necessary(model)
valueP = Ref{Cint}()
col = Cint(column(model, x) - 1)
ret = GRBgetintattrelement(model, "VBasis", col, valueP)
ret = GRBgetintattrelement(model, "VBasis", c_column(model, x), valueP)
_check_ret(model, ret)
if valueP[] == 0
return MOI.BASIC
Expand Down Expand Up @@ -3753,8 +3758,7 @@
@assert bs == MOI.SUPER_BASIC
Cint(-3)
end
col = Cint(column(model, x) - 1)
ret = GRBsetintattrelement(model, "VBasis", col, valueP)
ret = GRBsetintattrelement(model, "VBasis", c_column(model, x), valueP)
_check_ret(model, ret)
_require_update(model)
return
Expand Down Expand Up @@ -3821,8 +3825,7 @@
return MOI.NOT_IN_CONFLICT
end
p = Ref{Cint}()
ret =
GRBgetintattrelement(model, "IISUB", Cint(column(model, index) - 1), p)
ret = GRBgetintattrelement(model, "IISUB", c_column(model, index), p)
_check_ret(model, ret)
return p[] > 0 ? MOI.IN_CONFLICT : MOI.NOT_IN_CONFLICT
end
Expand All @@ -3837,8 +3840,7 @@
return MOI.NOT_IN_CONFLICT
end
p = Ref{Cint}()
ret =
GRBgetintattrelement(model, "IISLB", Cint(column(model, index) - 1), p)
ret = GRBgetintattrelement(model, "IISLB", c_column(model, index), p)
_check_ret(model, ret)
return p[] > 0 ? MOI.IN_CONFLICT : MOI.NOT_IN_CONFLICT
end
Expand All @@ -3856,14 +3858,12 @@
return MOI.NOT_IN_CONFLICT
end
p = Ref{Cint}()
ret =
GRBgetintattrelement(model, "IISLB", Cint(column(model, index) - 1), p)
ret = GRBgetintattrelement(model, "IISLB", c_column(model, index), p)
_check_ret(model, ret)
if p[] > 0
return MOI.IN_CONFLICT
end
ret =
GRBgetintattrelement(model, "IISUB", Cint(column(model, index) - 1), p)
ret = GRBgetintattrelement(model, "IISUB", c_column(model, index), p)

Check warning on line 3866 in src/MOI_wrapper/MOI_wrapper.jl

View check run for this annotation

Codecov / codecov/patch

src/MOI_wrapper/MOI_wrapper.jl#L3866

Added line #L3866 was not covered by tests
_check_ret(model, ret)
if p[] > 0
return MOI.IN_CONFLICT
Expand Down Expand Up @@ -4165,7 +4165,7 @@
vi::MOI.VariableIndex,
value::T,
) where {T}
_set_attribute(model, attr, Cint(column(model, vi) - 1), value)
_set_attribute(model, attr, c_column(model, vi), value)
_require_update(model)
return
end
Expand All @@ -4176,7 +4176,7 @@
vi::MOI.VariableIndex,
)
_update_if_necessary(model)
return _get_attribute(model, attr, Cint(column(model, vi) - 1))
return _get_attribute(model, attr, c_column(model, vi))
end

"""
Expand Down Expand Up @@ -4239,7 +4239,7 @@

# Now add the quadratic constraint.

I = Cint[column(model, v) - 1 for v in f.variables]
I = Cint[c_column(model, v) for v in f.variables]
V = fill(Cdouble(-1.0), length(f.variables))
V[1] = 1.0
ret = GRBaddqconstr(
Expand Down
Loading