From 99c12094027fe7de530d3c17b36aa53b68b676a8 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 30 Oct 2023 15:57:07 +1300 Subject: [PATCH 1/6] Add documentation for the C API --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/README.md b/README.md index 29eb37c..f6c08ab 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,55 @@ 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 + `Cint(Gurobi.column(model, x) - 1)` + * When Gurobi requires a `Ptr{T}` that holds one element, like `* int`, + use a `Ref{T}()` + * When Gurobi requries a `Ptr{T}` that holds multiple elements, use + a `Vector{T}`. + +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 = Cint(Gurobi.column(model, x) - 1) +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), From 0de4167973c3468c8e74956c0d0ebcd44ca27436 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 30 Oct 2023 16:39:55 +1300 Subject: [PATCH 2/6] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f6c08ab..02b4d43 100644 --- a/README.md +++ b/README.md @@ -131,10 +131,13 @@ As general rules when converting from Julia to C: * When Gurobi requires the column index of a variable `x`, use `Cint(Gurobi.column(model, x) - 1)` - * When Gurobi requires a `Ptr{T}` that holds one element, like `* int`, - use a `Ref{T}()` + * 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: From 4f18b665b5e67a576f601216e2a9acd7df274a7a Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 30 Oct 2023 16:47:08 +1300 Subject: [PATCH 3/6] Add c_column --- README.md | 12 ++-- src/MOI_wrapper/MOI_indicator_constraint.jl | 4 +- src/MOI_wrapper/MOI_wrapper.jl | 68 ++++++++++----------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 02b4d43..7838c79 100644 --- a/README.md +++ b/README.md @@ -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 @@ -130,7 +130,7 @@ for details. As general rules when converting from Julia to C: * When Gurobi requires the column index of a variable `x`, use - `Cint(Gurobi.column(model, x) - 1)` + `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 @@ -151,7 +151,7 @@ julia> model = Gurobi.Optimizer(); julia> x = MOI.add_variable(model) MOI.VariableIndex(1) -julia> x_col = Cint(Gurobi.column(model, x) - 1) +julia> x_col = Gurobi.c_column(model, x) 0 julia> GRBupdatemodel(model) diff --git a/src/MOI_wrapper/MOI_indicator_constraint.jl b/src/MOI_wrapper/MOI_indicator_constraint.jl index 56a1010..26610c1 100644 --- a/src/MOI_wrapper/MOI_indicator_constraint.jl +++ b/src/MOI_wrapper/MOI_indicator_constraint.jl @@ -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 diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index c6bf167..2734c36 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -764,7 +764,7 @@ function _indices_and_coefficients( ) 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 @@ -797,8 +797,8 @@ function _indices_and_coefficients( 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 @@ -815,7 +815,7 @@ function _indices_and_coefficients( 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 @@ -862,12 +862,19 @@ end 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) return _info(model, x).column end +""" + c_column(model::Optimizer, x::MOI.VariableIndex) --> Cint + +Return the `Cint` 0-indexed column associated with `x` for use with the C API. +""" +c_column(model::Optimizer, x::MOI.VariableIndex) = Cint(column(model, x) - 1) + function _get_next_column(model::Optimizer) model.next_column += 1 return model.next_column - 1 @@ -2509,7 +2516,7 @@ function MOI.is_valid( 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, @@ -2894,9 +2901,8 @@ function MOI.get( ) 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 @@ -2988,7 +2994,7 @@ function MOI.get( ) _throw_if_optimize_in_progress(model, attr) MOI.check_result_index_bounds(model, attr) - col = Cint(column(model, c) - 1) + col = c_column(model, x) if model.has_infeasibility_cert dual = _farkas_variable_dual(model, col) return min(dual, 0.0) @@ -3022,7 +3028,7 @@ function MOI.get( ) _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) @@ -3056,7 +3062,7 @@ function MOI.get( ) _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 @@ -3073,7 +3079,7 @@ function MOI.get( ) _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 @@ -3479,7 +3485,7 @@ function MOI.modify( 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) @@ -3500,7 +3506,7 @@ function MOI.modify( 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) @@ -3518,7 +3524,7 @@ function MOI.modify( ret = GRBsetdblattrelement( model, "Obj", - Cint(column(model, chg.variable) - 1), + c_column(model, chg.variable), chg.new_coefficient, ) _check_ret(model, ret) @@ -3537,7 +3543,7 @@ function MOI.modify( 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) @@ -3570,7 +3576,7 @@ function _replace_with_matching_sparsity!( 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) @@ -3603,13 +3609,13 @@ function _replace_with_different_sparsity!( ) # 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] 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] coefs = MOI.coefficient.(replacement.terms) ret = GRBchgcoeffs(model, length(cols), rows, cols, coefs) _check_ret(model, ret) @@ -3700,8 +3706,7 @@ function MOI.get( ) _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 @@ -3753,8 +3758,7 @@ function MOI.set( @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 @@ -3821,8 +3825,7 @@ function MOI.get( 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 @@ -3837,8 +3840,7 @@ function MOI.get( 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 @@ -3856,14 +3858,12 @@ function MOI.get( 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_ret(model, ret) if p[] > 0 return MOI.IN_CONFLICT @@ -4165,7 +4165,7 @@ function MOI.set( 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 @@ -4176,7 +4176,7 @@ function MOI.get( 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 """ @@ -4239,7 +4239,7 @@ function MOI.add_constraint( # 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( From 143bcb8329f51e9aef6b1b165b1e3386ed6ffc97 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 30 Oct 2023 17:03:58 +1300 Subject: [PATCH 4/6] Update MOI_wrapper.jl --- src/MOI_wrapper/MOI_wrapper.jl | 38 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index 2734c36..2da77df 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -858,22 +858,36 @@ function _info(model::Optimizer, key::MOI.VariableIndex) 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`. 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 - + """ - c_column(model::Optimizer, x::MOI.VariableIndex) --> Cint + 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. """ -c_column(model::Optimizer, x::MOI.VariableIndex) = Cint(column(model, x) - 1) +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 @@ -1303,20 +1317,6 @@ function _info( 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}}, From 8cd38142971edeadc1457d0b0d2276d81dc22796 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 30 Oct 2023 18:17:23 +1300 Subject: [PATCH 5/6] Update src/MOI_wrapper/MOI_wrapper.jl --- src/MOI_wrapper/MOI_wrapper.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index 2da77df..bc9e495 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -2994,7 +2994,7 @@ function MOI.get( ) _throw_if_optimize_in_progress(model, attr) MOI.check_result_index_bounds(model, attr) - col = c_column(model, x) + col = c_column(model, c) if model.has_infeasibility_cert dual = _farkas_variable_dual(model, col) return min(dual, 0.0) From ae89a67fd12acd386a12904d9e05dcf5be64c955 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 30 Oct 2023 18:18:11 +1300 Subject: [PATCH 6/6] Update src/MOI_wrapper/MOI_wrapper.jl --- src/MOI_wrapper/MOI_wrapper.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MOI_wrapper/MOI_wrapper.jl b/src/MOI_wrapper/MOI_wrapper.jl index bc9e495..71e27a0 100644 --- a/src/MOI_wrapper/MOI_wrapper.jl +++ b/src/MOI_wrapper/MOI_wrapper.jl @@ -873,7 +873,6 @@ function column( ) return _info(model, x).column end - """ c_column( model::Optimizer,