gurobipy.Model.addGenConstrMin() Invalid data in vars array
AnsweredI was trying to introduce a constraint in my model as follow, where m is a constant (say 100) and n / y are decision variables
y[i,d] = min_{j} (m[i,j] * n[i,j,d]) for i for d
I tried both of the functions below which result in identical error: Invalid data in vars array
model.addGenConstrMin(fg_id['A1','d1'], [100*n_ijd['A1','P1','d1'], 200*n_ijd['A1', 'P2','d1']])
model.addConstr(fg_id['A1', 'd1'] == gp.min_(100*n_ijd['A1','P1','d1'], 200*n_ijd['A1','P2','d1']), 'Test')
My variables are generated with the following codes:
n_ijd = model.addVars(['A1'], ['P1', 'P2'], ['d1'], vtype=GRB.INTEGER, name="nijd")
fg_id = model.addVars(['A1'], ['d1'], vtype=GRB.INTEGER, name="fgid")
I thought I was following the instructions as written in the documentation's example. but I don't know what went wrong. Any advice will be appreciated, thanks!
https://www.gurobi.com/documentation/9.1/refman/py_model_agc_min.html
-
Official comment
This post is more than three years old. Some information may not be up to date. For current information, please check the Gurobi Documentation or Knowledge Base. If you need more help, please create a new post in the community forum. Or why not try our AI Gurobot?. -
Hi Michael,
You are only allowed to pass variables and constants to the Min constraints. You are also scaling the variables. This would need to formulated using additional auxiliary variables, e.g.
n_ijd = model.addVars(['A1'], ['P1', 'P2'], ['d1'], vtype=GRB.INTEGER, name="nijd")
fg_id = model.addVars(['A1'], ['d1'], vtype=GRB.INTEGER, name="fgid")
n_ijd_scaled = model.addVars(['A1'], ['P1', 'P2'], ['d1'], vtype=GRB.INTEGER, name="nijd_scaled")
model.addConstr(n_ijd_scaled['A1','P1','d1'] == 100*n_ijd['A1','P1','d1'])
model.addConstr(n_ijd_scaled['A1','P2','d1'] == 200*n_ijd['A1','P1','d1'])
model.addGenConstrMin(fg_id['A1','d1'], [n_ijd_scaled['A1','P1','d1'], n_ijd_scaled['A1', 'P2','d1']])Cheers,
Matthias0 -
Hi Matthias,
This works like a charm! I hope this post will help people running into the same issue. Thank you!
0 -
Hello,
I have a very similar problem but a bit more tricky. I have two nested sum with various operations between the two sums : an abs function and a minimum function. The core of this nested loop is a LinExpr() which is not acceptable for general constraints as you mentionned above. How can I extract the LinExpr() to make it as a variable first and then put it back into the General Constraint ? I joined my current line of code below.
model.addConstr(minobj == quicksum(abs_(min_(quicksum(60. - dose[i,j] * x[j] for j in range(N)), constant = 0)) for i in range(M))
0 -
The general constraint helper functions only accept Var objects (and in some cases, constants) as arguments. Additionally, these functions can only be used in a constraint to set the value of a variable equal to the function's output. For example, given Var objects \( \texttt{x} \), \( \texttt{y} \), and \( \texttt{z} \):
- \(\texttt{model.addConstr(z == min_(x, y))}\) will work
- \(\texttt{model.addConstr(z == min_(x, y + 1))}\) will not work
- \(\texttt{model.addConstr(z == min_(x, y) + 2)}\) will not work
This means you need to introduce auxiliary variables for:
- Each expression used as input for a min_() function
- The output of each min_() function (which is used as input for the abs_() functions)
- The output of each abs_() function
However, I am confused about this part of your expression:
min_(quicksum(60. - dose[i,j] * x[j] for j in range(N)), constant = 0)
For a fixed \( i \in M \), the linear expression \( \sum_{j \in N} (60 - d_{ij}x_j) \) is a single scalar value, so the \( \min \) function accomplishes nothing here. Is the summation supposed to be here? Do you really mean to model the expression \( \min_{j \in N}\{60 - d_{ij}x_j\}\)?
If so, then you can model
$$\begin{align*} \texttt{minobj} &= \sum_{i \in M} |\min_{j \in N}\{60 - d_{ij}x_j\}|\end{align*}$$
by introducing auxiliary variables \( y \in \mathbb{R}^{|M| \times |N|} \), \( z \in \mathbb{R}^{|M|} \), and \( u \in \mathbb{R}^{|M|} \), then adding the constraints
$$\begin{alignat*}{2} y_{ij} &= 60 - d_{ij}x_j \quad && \forall i \in M,\ j \in N \\ z_i &= \min_{j \in N} y_{ij} && \forall i \in M \\ u_i &= |z_i| && \forall i \in M \\ \texttt{minobj} &= \sum_{i \in M} u_i. \end{alignat*}$$
In Python:
y = model.addVars(M, N, lb=-GRB.INFINITY, name="y")
z = model.addVars(M, lb=-GRB.INFINITY, name="z")
u = model.addVars(M, name="u")
model.addConstrs(
(y[i, j] == 60 - dose[i, j] * x[j] for i in range(M) for j in range(N)),
name="set_y",
)
model.addConstrs((z[i] == min_(y.select(i, "*")) for i in range(M)), name="set_z")
model.addConstrs((u[i] == abs_(z[i]) for i in range(M)), name="set_u")
model.addConstr(minobj == u.sum(), name="set_minobj")0 -
Hello,
Thank you so much for your very fast and comprehensive explanation.
Actually, as you pointed it out, I realized I made a mistake in my objective. The minimum function should be used to find the element-wise minimum of array elements. The correct expression is as follows:
Then, the associated Python code (following your development above) would be:
y = model.addVars(M, lb=-GRB.INFINITY, name="y")
z = model.addVars(M, lb=-GRB.INFINITY, name="z")
u = model.addVars(M, name="u")
model.addConstrs(
(y[i] == (60 - dose[i, j] * x[j]) for i in range(M) for j in range(N)),
name="set_y",
)
model.addConstrs((z[i] == min_(y[i], constant = 0) for i in range(M)), name="set_z")
model.addConstrs((u[i] == abs_(z[i]) for i in range(M)), name="set_u")
model.addConstr(minobj == u.sum(), name="set_minobj")Do you agree ? This objective is supposed to push all w_i variables to have at least 60 as value. However, with the current model, this is not observed. Any tips ?
Thank you again0 -
Ah, I now see I completely missed the \( \texttt{constant=0} \) in your min_() function. Based on your description, your code looks very close. I think you just need to add the quicksum() function back into the first constraint family, which I had removed based on my misunderstanding of the problem:
y = model.addVars(M, lb=-GRB.INFINITY, name="y")
z = model.addVars(M, lb=-GRB.INFINITY, name="z")
u = model.addVars(M, name="u")
model.addConstrs(
(y[i] == 60 - quicksum(dose[i, j] * x[j] for j in range(N)) for i in range(M)),
name="set_y",
)
model.addConstrs((z[i] == min_(y[i], constant=0) for i in range(M)), name="set_z")
model.addConstrs((u[i] == abs_(z[i]) for i in range(M)), name="set_u")
model.addConstr(minobj == u.sum(), name="set_minobj")By the way, isn't this equivalent to modeling the following?
$$\begin{align*}u_i &= \max\{0, \sum_{j \in N} d_{ij} x_j - 60\} \quad \forall i \in M \\ \texttt{minobj} &= \sum_{i \in M} u_i \end{align*}$$
I.e., for a fixed \( i \in M \), the contribution of each \( w_i \) to \( \texttt{minobj} \) is its excess over \(60\) (or \( 0 \) if \( w_i \leq 60 \)). If this is what you are trying to model, you could use the max_() general constraint helper function.
Better yet, you might be able to model this without the use of general constraints:
$$\begin{alignat*}{3} \min\ && \sum_{i \in M} &\, u_i && \\ \textrm{s.t.}\ && u_i &\geq \sum_{j \in N} d_{ij} x_j - 60 \quad && \forall i \in M \\ && u_i &\geq 0 && \forall i \in M.\end{alignat*}$$
But whether or not this works depends on what the rest of your model looks like. If you are trying to push all of the \( w_i \) to be over \( 60 \), this formulation won't work.
0 -
Hi Eli!
Indeed the formulations are equivalent for max value penalty on this w_i, thanks for noticing it. The problem is: I want to penalize any deviation of w_i above AND below 60 therefore I also need to add a min penalty on this w_i as the following:
which is equivalent to minimize a squared norm ||wi - 60||^2 when put together. The full formulation is then:
I am sorry I did not give the full problem on my first comment. I really appreciate your help.
Also, I just don't understand your last sentence. Why would it not work if I want to penalize all the w_i ?
0 -
I want to penalize any deviation of w_i above AND below 60
So for a fixed \( i \in M \), you want the following?
- \( w_i = 65 \) should incur a penalty of \( 5 \) in the objective function
- \( w_i = 50 \) should incur a penalty of \( 10 \) in the objective function
If this is the case, I don't see a reason to introduce the \( \min \) and \( \max \) functions; your objective function is equivalent to minimizing the sum over all \( i \in M \) of the absolute values of \( w_i - 60 \):
$$\begin{align*} \min\ \sum_{i \in M} \Big\lvert \sum_{j \in N} d_{ij} x_j - 60\Big\rvert.\end{align*}$$
You can model these absolute values without the use of general constraints using a formulation similar to the one I described earlier:
$$\begin{alignat*}{3} \min\ && \sum_{i \in M} &\, u_i && \\ \textrm{s.t.}\ && u_i &\geq \sum_{j \in N} d_{ij} x_j - 60 \quad && \forall i \in M \\ && u_i &\geq 60 - \sum_{j \in N} d_{ij} x_j && \forall i \in M.\end{alignat*}$$
Also, I just don't understand your last sentence. Why would it not work if I want to penalize all the w_i ?
I'm sorry for the confusion. I interpreted your earlier description of the objective function as one that incentivized values of \( w_i \) over \( 60 \). If you really want to penalize \( w_i \) values above and below \( 60 \) at a linear rate, then this formulation should work.
0 -
Thank you so much for all of your quick and comprehensive answers. The reason I am using min and max function is due to the fact that I want to separate the two objectives to use different weighting depending on my problem at stake.
0 -
Okay, I see. Could you instead create two sets of variables, \( u^{+} \) and \( u^{-} \), to represent respectively the deviations above and below \( 60 \)? Then, use a formulation similar to the one above to set \( u^{+}_i = \max\{w_i - 60, 0\} \) and \( u^{-}_i = \max\{60 - w_i, 0\} \) for all \( i \in M \). I.e.:
$$\begin{alignat*}{3} \min\ && \sum_{i \in M} (u^{+}_i & + u^{-}_i) && \\ \textrm{s.t.}\ && u^{+}_i &\geq \sum_{j \in N} d_{ij} x_j - 60 \quad && \forall i \in M \\ && u^{-}_i &\geq 60 - \sum_{j \in N} d_{ij} x_j && \forall i \in M \\ && u^{+}_i, u^{-}_i & \geq 0 && \forall i \in M.\end{alignat*}$$
This way, your objective function can weigh the deviations differently depending on if they are positive (above \(60\)) or negative (below \(60\)).
If that won't work for your application (maybe your objective function includes other terms that result in the model not necessarily pushing the \( u \) variables as low as possible), I would try to use two max_() general constraints instead of a max_(), min_(), and abs_() general constraint. It sounds like all you need is \( \max\{w_i - 60, 0\} \) and \( \max\{60 - w_i, 0\} \), both of which are already nonnegative.
0 -
I was not familiar with this min-max constraint trick in Gurobi but now it is all clear thank you. I tried and it works. Indeed I will have to add other objectives to the overall objective function for my application but I will see how gurobi manages. Is there any particular reason why you suggest to avoid the use of general constraints or to reduce them as much as possible (using only two max_()) ? I have a simple test case, but in terms of computation time, it is almost the same on my side.
0 -
Internally, Gurobi introduces binary variables to model these general constraints. This transforms an otherwise continuous model into a mixed-integer model. Typically, a continuous linear formulation will solve faster than an equivalent mixed-integer linear formulation, but there may not be much difference if the model is relatively easy to solve.
0 -
Hi Eli,
If you allow me, I have two remaining questions regarding my model stated above.
Firstly, I generated an initial feasible solution from a reduced problem and I want to feed the MIP start of my larger instance with this solution. However, my constraint to penalize values below 60 is violated for some index i. How this can be ? This is not supposed to be a "real constraint" given that it is defined with the variable $u^{ -}_{i} $ for which I want to minimize the sum.
Secondly, I also want to add an objective that penalizes a maximum on mean values as follows:
I implemeted something and it seems to work but I was wondering if there is a more efficient way to do it ? See my code below (I switch to matrix model for numerical performances)
finalObj = m.addMvar(1, name="totalObj)
u_mean = m.addMVar(1,lb=0,name="meanObj")
aux = m.addMVar(M,name="aux")
# D is a MLinExpr()representing the sparse matrix product sum_j dij xj
m.addConstr(aux == D, name = "auxConstr")
m.addConstr((u_mean >= (aux.sum()/M)-60), name = "meanConstr)
# u_max and u_min defined in previous comments
finalObj += u_mean + u_max + u_minThank you.
0 -
I generated an initial feasible solution from a reduced problem and I want to feed the MIP start of my larger instance with this solution. However, my constraint to penalize values below 60 is violated for some index i. How this can be ?
For a fixed \( i \in M \), \( u_i^{+} + u_i^{-} \) is equivalent to \( | 60 - \sum_{j \in N} d_{ij} x_j | \) precisely because those two non-negative variables appear in the objective function and are bounded below by \(\sum_{j \in N} d_{ij} x_j - 60 \) and \(60 - \sum_{j \in N} d_{ij} x_j \), respectively. If the constraints on \(u_i^{+}\) and/or \(u_i^{-}\) are not satisfied, then \( u_i^{+} + u_i^{-} \) will likely be less than the absolute value expression, in which case the solution should not be feasible.
Can you post a minimal working code example that reproduces the constraint violation by:
- Solving a small instance of the problem
- Using the optimal solution of the small instance to construct a MIP start for a larger instance
- Solving the larger instance using the MIP start
That would be the easiest way to figure out what's going on. There are a few factors at play, like how exactly \(M\) and \(N\) differ (if at all) between the instances, and how exactly you generate the MIP start for the larger instance from the smaller instance's solution.
Out of curiosity, what prompted you to try giving Gurobi a MIP start?
I implemeted something and it seems to work but I was wondering if there is a more efficient way to do it ?
I'll assume the purpose of \( \texttt{u_mean} \) is to only penalize a mean \( (\sum_{i \in M} \sum_{j \in N} d_{ij} x_j)/|M|\) in excess of \( 60 \). I only have one question about these two lines:
finalObj = m.addMvar(1, name="totalObj)
finalObj += u_mean + u_max + u_minWhat's the purpose of the \( \texttt{totalObj} \) variable? With the above code, \( \texttt{finalObj} \) is equal to \( \texttt{totalObj + u_mean + u_max + u_min} \). Otherwise, the code looks good to me. I like how you introduced an auxiliary variable to represent \( \sum_{j \in N} d_{ij} x_j \) for each \( i \in M \).
Is the model taking a long time to build or solve?
0
Post is closed for comments.
Comments
15 comments