Hello everyone. I am currently transitioning from GAMS optimisation to Julia optimisation (in VScode) and trying to figure out the “optimal/fastest” way of constructing conditional constraints based on the presence (or absence) of input data per possible set combinations, which will result at the fastest possible evaluation by Julia. An example:
The following sets are utilisised in the definition of key constraint/variables:
#Utilised packages
using JuMP
using HiGHS
using DataFrames
using XLSX
#Model declaration
Model_m = Model(HiGHS.Optimizer)
#Declare sets. For simplicity, I'll just give you their size and not the actual elements
Technology = collect(1:8)
Country = collect(1:1)
Region = collect(1:1)
Area = collect(1:2)
Scenario = collect(1:5)
Year = collect(1:4)
Season = collect(1:52)
Time = collect(1:168)
Fuel = collect(1:11)
Direction = ["Input", "Output"]
Assume that I have a spreadsheet with all required parameters, named “InputData.xlsm”.
There, a parameter “CarrierMix” exists, in a form of a 16x9 table, which I push into a Dataframe to further handle in the optimisation process (15x9 dataframe, with the first row being the header, present in row 20 of the excel file.
See screenshot below just for the visual explanation:
#Declare parameters by importing data from excel file into a dataframe
CarrierMix = DataFrame(XLSX.readtable("InputData.xlsm", "CarrierMix"; first_row=20)...)
which results in the following dataframe (see screenshot for clarification):
As you will see in the constraint formation below, the first 2 columns of the dataframe are practically identifiers of 2 sets (Direction, Fuel), and the rest of the column headers correspond to identifiers of another set (Technology). Note that in the Dataframe, the first 2 columns don’t include all possible combinations of the sets Direction (2 elements) and Fuel (11 elements) (the resulting dataframe is 15 x 9, while all possible combinations would lead to 22 x 9).
Based on this dataframe, I aim to construct a constraint for each combination of the first 2 columns which includes a number in the other columns, while do nothing for the rest.
See below:
#Declare variables
@variable(Model_m, 0<=vTotalFuelUse[tech in 1:length(Technology), c in 1:length(Country), r in 1:length(Region), a in 1:length(Area), sc in 1:length(Scenario), y in 1:length(Year), s in 1:length(Season), t in 1:length(Time)]);
@variable(Model_m, 0<=vSpecificFuelUse[tech in 1:length(Technology), c in 1:length(Country), r in 1:length(Region), a in 1:length(Area), f in 1:length(Fuel), sc in 1:length(Scenario), y in 1:length(Year), s in 1:length(Season), t in 1:length(Time)]);
#Declare constraints
@constraint(Model_m, FuelMix[tech in 1:length(Technology), c in 1:length(Country), r in 1:length(Region), a in 1:length(Area), f in 1:length(Fuel), sc in 1:length(Scenario), y in 1:length(Year), s in 1:length(Season), t in 1:length(Time)],
vSpecificFuelUse[tech, c, r, a, f, sc, y, s, t]
==
if !isempty(filter(row -> row.Direction == "Input" && row.Fuel == f, CarrierMix))
if !ismissing(filter(row -> row.Direction == "Input" && row.Fuel == f, CarrierMix)[!,Technology[tech]][1]) && !isempty(filter(row -> row.Direction == "Input" && row.Fuel == f, CarrierMix)[!,Technology[tech]][1])
filter(row -> row.Direction == "Input" && row.Fuel == f, CarrierMix)[!,Technology[tech]][1]
*
vTotalFuelUse[tech, c, r, a, sc, y, s, t]
end
else
0
end
);
Ultimately I what I do is that I handle the dataframe as a 2D mapping, and I want for each combination of sets which has a corresponding number in the dataframe, to grab that number and multiply it with the variable “vTotalFuelUse”, while ignoring the constraint for whatever combination doesn’t exist or doesn’t have data (which would result in an error).
It seems that it takes an extremely lengthy time for Julia to evaluate this constraint when I hit Shift + Enter on it, so I assume I am making this declaration too complicated, or there is a fastest workaround (similar to the dollar operator ($) in GAMS).
For the reference, I also attempted to simply use the following format of error avoidance, but I got no faster luck:
@constraint(Model_m, FuelMix[tech in 1:length(Technology), c in 1:length(Country), r in 1:length(Region), a in 1:length(Area), f in 1:length(Fuel), sc in 1:length(Scenario), y in 1:length(Year), s in 1:length(Season), t in 1:length(Time)],
vSpecificFuelUse[tech, c, r, a, f, sc, y, s, t]
==
try
filter(row -> row.Direction == "Input" && row.Fuel == f, CarrierMix)[!,Technology[tech]][1]
catch
0
end
*
vTotalFuelUse[tech, c, r, a, sc, y, s, t]
);
Any suggestions would be greatly appreciated.
Thanks in advance.