I am looking for tips on building a model with variables, expressions, and constraints over sets that depends on indexes from other sets. For instance:
@expression(model, [i = 1:5, j = blah[i]], ...)
where blah
is a dictionary.
Using add_to_expression!
with a Dict gives some speed up, but it also makes the model harder to read, which we would like to avoid.
I have a MWE below, based on the larger model in GitHub - TulipaEnergy/TulipaEnergyModel.jl: Tulipa Energy Model.
The context here is, in few words, flow in a graph over time blocks. These time blocks depend on the flow itself.
Furthermore, there are balancing constraints that happens over possibly other time blocks.
using JuMP
function create_model(num_vertices)
model = Model()
V = 1:num_vertices
E = vcat([[(i, j) for j = i+1:num_vertices] for i = 1:num_vertices]...)
T1 = [1:3, 4:6, 7:9, 10:12]
T2 = [1:4, 5:8, 9:12]
T3 = [1:6, 7:12]
T_options = [T1, T2, T3]
TV = Dict(
v => T_options[v%3+1]
for v in V
)
TE = Dict(
(a, b) => T_options[(a+b)%3+1]
for (a, b) in E
)
@variable(model, flow[e ∈ E, TE[e]] ≥ 0)
@objective(model, Min,
sum(flow[e, T] for e in E, T ∈ TE[e])
)
@expression(model,
incoming[v ∈ V, Tᵥ ∈ TV[v]],
sum(
length(Tₑ ∩ Tᵥ) * flow[(src, v), Tₑ]
for src in [src for (src, dst) in E if dst == v], Tₑ ∈ TE[(src, v)]
)
)
@expression(model,
outgoing[v ∈ V, Tᵥ ∈ TV[v]],
sum(
length(Tₑ ∩ Tᵥ) * flow[(v, dst), Tₑ]
for dst in [dst for (src, dst) in E if src == v], Tₑ ∈ TE[(v, dst)]
)
)
@constraint(model,
balance[v ∈ V, Tᵥ ∈ TV[v]],
incoming[v, Tᵥ] == outgoing[v, Tᵥ]
)
return model
end
function main(; num_vertices=1000)
create_model(num_vertices)
end
main()
@time main()
@profview main()
Thanks in advance.