How to insert a IF inside a declaration of a variable JuMP

In order to avoid the creation of variables that are not really necessary in my problem, I’d like to insert a IF in the declaration of the variables. Like this :

@variable(model, x_prod[l in localization, inst in inst_by_localisation[l], c in commodities_installation[inst] if commodity_class[c]=="Grid", t in timeslice, dy in div_years, y in years]>=0) 

where commodity_class is a Dict that contains the association commodity => type of commodity.
I know I could create a dict before creating the variable that contains all the relationship allowed but I’d like to try to find a way to do it by using a IF because it can be useful for other situations. Also I would like to avoid to write many nested loops as the conventional way.
Anyone know if is it possible ? I’ve tried many different syntax without a good result. Thanks in advance.

From the manual page on variables:

Conditions can be placed on the index values for which variables are created; the condition follows the statement of the index sets and is separated with a semicolon:

@variable(m, x[i=1:10,j=1:10; isodd(i+j)] >= 0)

Note that only one condition can be added, although expressions can be built up by using the usual && and || logical operators.

[On a side note: the above condition can be taken literally. The semicolon syntax definitely isodd, or at least unJulian. I would prefer condition syntax exactly as you wrote it in your question. But I don’t think that would parse, so it’s probably not possible in JuMP.]

So in your case, a straightforward way is to just replace the if with a semicolon and put it at the end, like this:

@variable(model,
    x_prod[l in localization, inst in inst_by_localisation[l], c in commodities_installation[inst],
           t in timeslice, dy in div_years, y in years; commodity_class[c]=="Grid"] >=0)

The only problem is that this will be unnecessarily slow since the condition will end up in the innermost part of the multinested loop. You can speed it up by doing something like this instead:

# This is simplest if it works:
gridcommodities = [c for c in commodities_installation if commodity_class[c]=="Grid"]
# If not, try this:
gridcommodities = [c for l in localization, inst in inst_by_localisation[l], c in commodities_installation[inst] if commodity_class[c]=="Grid"]

@variable(model,
    x_prod[l in localization, inst in inst_by_localisation[l], c in gridcommodities,
           t in timeslice, dy in div_years, y in years] >=0)
1 Like

Thanks for your answer. I had already seen the manual but I was writing the wrong syntax. I put

@variable(model,
    x_prod[l in localization, inst in inst_by_localisation[l], c in commodities_installation[inst],
           t in timeslice, dy in div_years, y in years; if commodity_class[c]=="Grid"] >=0)

I corrected it thanks to your answer.
Concerning your suggestion, gridcommodities = [c in commodities_installation[inst] if commodity_class[c]=="Grid"] @variable(model, x_prod[l in localization, inst in inst_by_localisation[l], c in gridcommodities, t in timeslice, dy in div_years, y in years] >=0)
I was wondering as I need other loops nested before write `gridcommodities = [c in commodities_installation[inst] if commodity_class[c]==“Grid”] The time comparing to putting the IF inside the declaration wouldn’t be the same?

I had a typo on the gridcommodities line, sorry. Fixed now. I wrote two versions of the array comprehension. The simpler version may work (and may be faster) depending on what your other arrays look like and how many elements they have. If not, try the other one.

The main time saver in separating out the condition like this is that you don’t have to loop over timeslice, div_years and years when the condition doesn’t hold.

1 Like

Thanks again. The second one didn’t work well, i got an error like that:

gridcommodities = [l for l in localization, inst in inst_by_localisation[l], c in commodities_installation[inst] if commodity_class[c]=="Grid"]

ERROR: LoadError: UndefVarError: l not defined

once my variables are very related I think I’ll keep with the slow way in order to be sure the indexation is right. Thanks a lot!!

Wow. That array comprehension that I wrote is nowhere near correct. I really shouldn’t be writing untested code after a sleepless night. But risking public humiliation yet again, here’s a final attempt with dummy data:

localization = [:a,:b,:c,:d]
inst_by_localisation = Dict(:a => :e, :b => :f, :c => :g, :d => :h)
commodities_installation = Dict(:e => :x, :f => :y, :g => :z, :h => :w)
commodity_class = Dict(:x => "no", :y => "Grid", :z => "Grid", :w => "no")

gridcommodities = [c for l in localization for inst in [inst_by_localisation[l]] for c in [commodities_installation[inst]] if commodity_class[c]=="Grid"]
1 Like