MOI seems like the right level of abstraction to formulate
No. You should use JuMP instead. MOI is too complicated, and lacks good support for nonlinear problems.
Part of the requirement will be that it should very simple to present one’s current understanding of the optimization problem to stakeholders, in a language that they understand and including giving a full list of all constraints that are in place.
Use JuMP. The algebraic model can be simple to present the understanding of the optimization model to stakeholders.
moving the model definition away from the simple enumeration of variables-constraints-objective in a single file
Many projects have success in a functional form with method dispatch to create different sets of variables or constraints.
PowerSimulations is probably the largest example of this:
A concrete example might look like:
struct Object
weight::Float64
profit::Float64
end
struct KnapsackData
objects::Vector{Object}
capacity::Float64
end
abstract type AbstractKnapsack end
struct BinaryKnapsack <: AbstractKnapsack end
struct IntegerKnapsack <: AbstractKnapsack end
function add_knapsack_variables(model, data::KnapsackData, ::BinaryKnapsack)
@variable(model, x[1:length(data.objects)], Bin)
return
end
function add_knapsack_variables(model, data::KnapsackData, ::IntegerKnapsack)
@variable(model, x[1:length(data.objects)] >= 0, Int)
return
end
function add_capacity_constraint(model, data::KnapsackData, ::AbstractKnapsack)
x = model[:x]
N = length(x)
@constraint(
model,
capacity,
sum(data.objects[i].weight * x[i] for i in 1:N) <= data.capacity,
)
return
end
function add_profit_objective(model, data::KnapsackData, ::AbstractKnapsack)
x = model[:x]
N = length(x)
@objective(model, Max, sum(data.objects[i].profit * x[i] for i in 1:N))
return
end
function knapsack_model(data::KnapsackData, config::AbstractKnapsack)
model = Model()
add_knapsack_variables(model, data, config)
add_capacity_constraint(model, data, config)
add_profit_objective(model, data, config)
return model
end
data = KnapsackData([Object(rand(), rand()) for i in 1:20], 10.0)
binary_model = knapsack_model(data, BinaryKnapsack())
integer_model = knapsack_model(data, IntegerKnapsack())
A key benefit of using JuMP is that you can write the algebraic model in a readable form, and then combine it with the power of Julia to build a framework that suits you. A downside of the generality is that there aren’t off-the-shelf solutions.