I'm trying to implement conditional logic in Gekko using "if3" but am unsure how to successfully layer 2 conditions at different levels of granularity.
"x1" is vector of binary values (0/1) that controls when an alternative rhs value should be used on element i to constrain x2 and x3.
"x2" is a vector of floats where I want to make the lower and upper bounds dynamic for element i based on the binary values in "x1" above. If the value for x1 for element i is = 1, I want to use "window_lnuc_min_promo_price" (vector of same length) as the lower bound and "window_lnuc_max_promo_price" as the upper bound. If the value for x1 for element i is = 0, I want to use "min_promo_price" as the lower bound and "max_promo_price" as the upper bound.
Similarly, "x3" is a vector of floats where I want to apply the same logic but just to the lower bound using values from "window_lnuc" when element i in x1 is = 1 and values from "lnuc" when it is = 0.
Lastly, I want to limit how many times X1 can be = 1 (4 in the example below). This would mean the alternative values are limited to 4 occurrences in total.
The problem I think I'm having is that because x1 is a variable with a 0-1 range, the optimizer is changing the default "0" values in "lnuc_weeks" (which I don't want it to do). I want the optimizer to basically keep anything that is 0 in "lnuc_weeks" as is and only select a maximum of 4 values from the elements in "lnuc_weeks" that are = 1 initially.
There is probably a better way to write this, but any help/feedback is appreciated. The full solution is a bit long to display for reproducibility of output, but hopefully the above/below sufficiently describe the problem.
x1 = m.Array(m.Var,(n), integer=True) #LNUC weeks
i = 0
for xi in x1:
xi.value = lnuc_weeks[i]
xi.lower = 0
xi.upper = 1
i += 1
x2 = m.Array(m.Var,(n)) #Blended SRP
i = 0
for xi in x2:
xi.value = blended_srp[i]
xi.lower = m.if3((x1[i]) - 1, min_promo_price[i], window_lnuc_min_promo_price[i])
xi.upper = m.if3((x1[i]) - 1, max_promo_price[i], window_lnuc_max_promo_price[i])
i += 1
x3 = m.Array(m.Var,(n)) #Blended NUC
i = 0
for xi in x3:
xi.value = blended_nuc[i]
xi.lower = m.if3((x1[i]) - 1, lnuc[i], window_lnuc[i])
xi.upper = 10
i += 1
#Limit max lnuc weeks
m.Equation(sum(x1)<=4)
The .lower
and .upper
bounds are defined when the model is initialized and do not change to reflect newly optimized values. To implement these, use an inequality expression. Use a switching point of 0.5
instead of 1
to avoid numerical issues with an integer value >1
or >=1
. The solver tolerance is 1e-6
by default so a value of 0.999999 is considered the same as 1.000001 for convergence of equations.
i = 0
for xi in x2:
xi.value = blended_srp[i]
m.Equation(xi >= m.if3((x1[i]) - 0.5, min_promo_price[i], window_lnuc_min_promo_price[i]))
m.Equation(xi <= m.if3((x1[i]) - 0.5, max_promo_price[i], window_lnuc_max_promo_price[i]))
i += 1
and
i = 0
for xi in x3:
xi.value = blended_nuc[i]
m.Equation(xi >= m.if3((x1[i]) - 0.5, lnuc[i], window_lnuc[i]))
xi.upper = 10
i += 1
The select of any four elements with m.Equation(sum(x1)<=4)
is correct.