Skip to main content

gurobipy.Model.addGenConstrMin() Invalid data in vars array

Answered

Comments

14 comments

  • Matthias Miltenberger
    Gurobi Staff Gurobi Staff

    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,
    Matthias

     

    0
  • Michael Choy
    Gurobi-versary
    First Question
    First Comment

    Hi Matthias,

    This works like a charm! I hope this post will help people running into the same issue. Thank you!

    0
  • Sophie Wuyckens
    Gurobi-versary
    First Question
    Conversationalist

    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
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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
  • Sophie Wuyckens
    Gurobi-versary
    First Question
    Conversationalist

    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 again

    0
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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
  • Sophie Wuyckens
    Gurobi-versary
    First Question
    Conversationalist

    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
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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
  • Sophie Wuyckens
    Gurobi-versary
    First Question
    Conversationalist

    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
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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
  • Sophie Wuyckens
    Gurobi-versary
    First Question
    Conversationalist

    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
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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
  • Sophie Wuyckens
    Gurobi-versary
    First Question
    Conversationalist

    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_min

    Thank you.

     

    0
  • Eli Towle
    Gurobi Staff Gurobi Staff

    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:

    1. Solving a small instance of the problem
    2. Using the optimal solution of the small instance to construct a MIP start for a larger instance
    3. 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_min

    What'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

Please sign in to leave a comment.