How to model a piecewise linear function?
AnsweredHello, I am currently trying to implement the following in Gurobi. I currently have a cosntraint that looks like this: \(A_i\cdot x_{it}\leq Para \cdot \sum_j y_{jit}\). Here, \(x_{it},y_{ijt}\in \left\{0,1\right\}\) and \(A_j,Para\) are two parameters. Thus \(Para\) is constant. But now I want to model a relationship where \(Para_{it}\) now depends on \(i\) and \(t\) in a non-linear dependency. Specifically, \(Para_{it}\) should depend on the sum of the previous \(y_{ijt}\) (formally defined by the variable \(Sum_{it}\)). This results in \(Para_{it}(Sum_{jt})=1-\exp^{-0.5\cdot Sum_{it}}\). I want to use a linear approaximation for this. I have the following code for this:
breakpoints = list(range(1, 10))
slopes = [1 - math.exp(-0.5 * x) for x in breakpoints]
for i in I:
for t in T:
Model.addLConstr(Sum[i, t] == gu.quicksum(y[i, j, d] for j in J for d in range(1, t + 1)))
Model.addGenConstrPWL(Sum[i, t], Para[i, t], breakpoints, slopes)
Strangely enough, it doesn't work with addGenConstrExp(). How do I get it to work? And a theoretical question. Isn't \(Para_{it}\) also a decision variable and doesn't the model then become 'non-linear'?
-
Hi Dejan,
Strangely enough, it doesn't work with addGenConstrExp(). How do I get it to work?
Could you please elaborate what exactly does not work?
Note that you have to introduce an auxiliary variable to capture the \(\exp\) function. This means that you would have to model something like
z = Model.addVars(I, T, name="z") # add auxiliary variable for exp
w = Model.addVars(I, T, name="w") # add auxiliary variable for -0.5 * Sum[i,t]
for i in I: for t in T: Model.addLConstr(Sum[i, t] == gu.quicksum(y[i, j, d] for j in J for d in range(1, t + 1)))
# w[i,t] = -0.5 * Sum[i,t]
Model.addConstr(w[i,t] == -0.5 * Sum[i,t])
# z[i,t] = exp(w[i,t])
Model.addGenConstrExp(w[i,t], z[i,t])
# Para[i,t] = 1 - z[i,t]
Model.addConstr(Para[i,t] == 1 - z[i,t])
Model.setParam("Funcnonlinear", 0) # tell Gurobi to handle all general constraints via PWL approximationAlternatively, you could avoid using a piecewise-linear approximation and let Gurobi handle the nonlinear constraints directly. For this, you would have to set the FuncNonlinear parameter to 1. Note that the FuncNonlinear parameter is only available starting with version 11. If you are using an older version, then Gurobi will automatically compute a PWL approximation of function constraints introduced by \(\texttt{addGenConstrExp}\).
If you are using Gurobi version 12, you can use the new nonlinear expression API to write
for i in I:
for t in T:
Model.addLConstr(Sum[i, t] == gu.quicksum(y[i, j, d] for j in J for d in range(1, t + 1)))
# Para[i,t] = 1 - exp(-0.5 * Sum[i,t])
Model.addGenConstrNL(Para[i,t], 1 - gu.nlfunc.exp(-0.5 * Sum[i,t]))Note that in the above formulation, Gurobi will handle the nonlinear constraints directly and not via a piecewise-linear approximation.
Best regards,
Jaromił0 -
Dear Jaromił Najman. Thank you for your anwser. Sadly i cannot reproduce the error i was facing. Now it work. Cann i also model functions such a power or a sigmoid function without using addGenConstrPWL? Which command would i use for that?
0 -
Cann i also model functions such a power or a sigmoid function without using addGenConstrPWL? Which command would i use for that?
With version 11 you can use the addGenConstrPow method to add powers. For a sigmoid function, you can use addGenConstrLogistic.
Starting with version 12, we recommend to use the addGenConstrNL method. You could add a power and a logistic term in one equality constraint via
# add constraint resvar = x^3 + logistic(y)
Model.addGenConstrNL(resvar, x ** 3 + gu.nlfunc.logistic(y))Best regards,
Jaromił0
Please sign in to leave a comment.
Comments
3 comments