If you want to model ScalarAffineFunction-in-GreaterThan
with VariableIndex
-in-Interval
constraints, then you’ll need something like the following. (It isn’t sufficient to just define supports_constraint
, you also need to setup the data structure accordingly.)
import MathOptInterface as MOI
MOI.Utilities.@product_of_sets(RHS, MOI.GreaterThan{T})
const OptimizerCache = MOI.Utilities.GenericModel{
Float64,
MOI.Utilities.ObjectiveContainer{Float64},
MOI.Utilities.VariablesContainer{Float64},
MOI.Utilities.MatrixOfConstraints{
Float64,
MOI.Utilities.MutableSparseMatrixCSC{
Float64,
Int,
MOI.Utilities.OneBasedIndexing,
},
Vector{Float64},
RHS{Float64},
},
}
mutable struct Optimizer <: MOI.AbstractOptimizer
x_primal::Dict{MOI.VariableIndex,Float64}
termination_status::MOI.TerminationStatusCode
function Optimizer()
return new(Dict{MOI.VariableIndex,Float64}(), MOI.OPTIMIZE_NOT_CALLED)
end
end
function MOI.is_empty(model::Optimizer)
return isempty(model.x_primal) &&
model.termination_status == MOI.OPTIMIZE_NOT_CALLED
end
function MOI.empty!(model::Optimizer)
empty!(model.x_primal)
model.termination_status = MOI.OPTIMIZE_NOT_CALLED
return
end
function MOI.supports_constraint(
::Optimizer,
::Type{MOI.ScalarAffineFunction{Float64}},
::Type{MOI.GreaterThan{Float64}},
)
return true
end
function MOI.supports_constraint(
::Optimizer,
::Type{MOI.VariableIndex},
::Type{
<:Union{
MOI.LessThan{Float64},
MOI.GreaterThan{Float64},
MOI.EqualTo{Float64},
MOI.Interval{Float64},
},
}
)
return true
end
function MOI.supports(
::Optimizer,
::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}},
)
return true
end
function MOI.optimize!(dest::Optimizer, src::MOI.ModelLike)
cache = OptimizerCache()
index_map = MOI.copy_to(cache, src)
A = convert(
SparseArrays.SparseMatrixCSC{Float64,Int},
cache.constraints.coefficients,
)
b = cache.constraints.constants
c = zeros(size(A, 2))
offset = cache.objective.scalar_affine.constant
for term in cache.objective.scalar_affine.terms
c[term.variable.value] += term.coefficient
end
if cache.objective.sense == MOI.MAX_SENSE
c *= -1
end
xl, xu = cache.variables.lower, cache.variables.upper
dest.termination_status, x_primal = solve_pdhg(A, b, c, xl, xu)
for x in MOI.get(src, MOI.ListOfVariableIndices())
dest.x_primal[x] = x_primal[index_map[x].value]
end
return index_map, false
end
If you’re wanting to work on this, I think you’ll need to dig and learn more of the MathOptInterface internals, including the definitions of what the various sets and functions are. Unfortunately, much of the @product_of_sets
stuff is not well documented, because it isn’t meant for widespread public consumption. It’s really just an internal helper for solver wrappers, with the assumption that people using it are familiar with MOI. The best place to look are solver wrappers like Cbc.jl, Clp.jl, and SCS.jl.
For example,
function MOI.supports_constraint(
::Optimizer,
::Type{MOI.VariableIndex{Float64}},
::Type{MOI.Interval},
)
return true
end
needs to be
function MOI.supports_constraint(
::Optimizer,
::Type{MOI.VariableIndex},
::Type{MOI.Interval{Float64}},
)
return true
end
VariableIndex
does not have a type parameter, Interval
does.