Skip to main content

About functions cannot reference array form arguments

Answered

Comments

9 comments

  • Official comment
    Simranjit Kaur
    • Gurobi Staff
    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?.
  • Eli Towle
    • Gurobi Staff

    The error message comes from this line:

    rhs.addTerms(-2, qc1[k][0], q[k, 0])

    where \( \texttt{k=0} \), \( \texttt{qc1[k][0]=0} \), and \( \texttt{q[k, 0]} \) is Var object \( q_{00} \). This is not a valid call to QuadExpr.addTerms(), since the second argument should be Var object. You never change the value of \( \texttt{qc1[0]} \), so it remains an array of zeros:

    qc1 = np.array([[[0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]],
    [[0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]]])
    for j in range(4):
    if j != 0: # qc1[0] is never changed
    qc1[j] = gurobi_demo(qc1[j - 1])

    But the bigger issue is that in each call to the \( \texttt{gurobi_demo()} \) function, you create a new Model object. The function returns variables from this model, which you then try to use in a later call to \( \texttt{gurobi_demo()} \). This will not work, since you cannot add variables from one model to a different model. Maybe you could provide a mathematical description of what this sequential optimization algorithm is trying to accomplish. The solution could be to build a single model that is re-used across multiple solves.

    Note that the \( \texttt{gurobipy} \) package includes functions for building models clearly and concisely. For example, instead of setting the objective function with

    sum = LinExpr()
    for k in range(2):
    sum += z[k]
    model.setObjective(sum, sense=GRB.MINIMIZE)

    you could take advantage of tupledict.sum():

    model.setObjective(z.sum(), sense=GRB.MINIMIZE)

    Lastly, there is a typo in the spelling of the NonConvex parameter.

    0
  • Yanbo Fan
    • Gurobi-versary
    • Investigator
    • Thought Leader

    Hi Eli Towle,

       First of all, thank you very much for your detailed answer. But I have the following questions about my problems.

    1. About the model. I used this sequential optimization algorithm to solve a path planning problem, In each optimization process, I use the previous location information to get the location information of the next time interval. I didn't put all the time interval position information in one model and the optimization was due to the consideration of solver solution time. I divide the total time into four identical time intervals, so qc1 is a 4 * 2 * 3 matrix, 2 is two targets in the same time interval, and 3 is the 3D position information of each target.

    2. When it is solved in a model and the total time is divided into 10, the solution time is not expected, so I changed it to a sequential optimization algorithm. 

    3. qc1[0] is never changed, because I give the initial position and starting from the second time interval. 

    4. What can I do to use qc1 [J-1] (previous position) as a parameter to optimize qc1 [J] (next position), and using the form of the sequential optimization algorithm I gave. 

    def gurobi_demo(qc1[j - 1]):
    q = model.addVars()
    ....
    return q
    def main():
    Initialize qc1
    for j in range(Number of time slots):
    if j != 0:
    qc[j] = gurobi_demo(qc1[j - 1])
    print q

    Best wishes, 

    Yanbo

    0
  • Eli Towle
    • Gurobi Staff

    Based on your description, you only need the solution values of the variables from the previous optimization, not the variable objects themselves. Is this correct? If so, you can query a variable's solution value using the X variable attribute:

    # tupledict mapping q's indices to solution values
    vals = model.getAttr('X', q)

    You can manipulate these values to fit whatever structure you want (e.g., a list of lists).

    When building the \( \texttt{rhs} \) expression, you can either:

    1. Fix your calls to QuadExpr.addTerms() so you only use two arguments when adding a linear term to the quadratic expression, or
    2. Use Gurobi's overloaded operators. E.g.,
    rhs = q[k, 0]*q[k, 0] - 2*qc1[k][0]*q[k, 0] + ...
    0
  • Yanbo Fan
    • Gurobi-versary
    • Investigator
    • Thought Leader

    Hi Eli Towle,

    Thank you very much for your answer again, but I still have an important problem to solve. 

    According to the error information, I think the formal parameter qc1 of the function was not successfully used in the function, which qc1 is not involved in the operation. What' s more, the qc1 from the main function in gurobi_demo is a 2 * 3 matrix, but qc1 in main function is a 4 * 2 * 3 matrix. Do I need to declare this qc1 in the function?

    def gurobi_demo(qc1[j - 1]):
    ...
    for
    k in range(2):
    rhs = QuadExpr()
    rhs.addTerms(1, q[k, 0], q[k, 0])
    rhs.addTerms(-2, qc1[k][0], q[k, 0])
    rhs.addTerms(1, qc1[k][0], qc1[k][0])
    rhs.addTerms(1, q[k, 1], q[k, 1])
    rhs.addTerms(-2, qc1[k][1], q[k, 1])
    rhs.addTerms(1, qc1[k][1], qc1[k][1])
    model.addConstr(z[k] * z[k] == rhs)
    ...
    def main():
    #qc1 is a (4 * 2 * 3) matrix
    qc1 = np.array([[[0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]],
    [[0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0]]])
    for
    j in range(4):
    if j != 0:
    #qc[j], qc[j-1] is a (2 * 3) matrix
    qc[j] = gurobi_demo(qc1[j - 1])
    print q
    0
  • Eli Towle
    • Gurobi Staff

    You can resolve the \( \texttt{TypeError: object of type 'int' has no len() } \) by changing your construction of \( \texttt{rhs} \) to one of the two methods I described earlier. The values in \( \texttt{qc1} \) are numbers, not Var objects. So these values cannot be the second or third arguments to QuadExpr.addTerms().

    For example:

    rhs = QuadExpr()
    rhs.addTerms(1, q[k, 0], q[k, 0])
    rhs.addTerms(-2*qc1[k][0], q[k, 0])
    rhs.addConstant(qc1[k][0]*qc1[k][0])

    rhs.addTerms(1, q[k, 1], q[k, 1])
    rhs.addTerms(-2*qc1[k][1], q[k, 1])
    rhs.addConstant(qc1[k][1]*qc1[k][1])

    Regarding the use of \( \texttt{qc1} \): if you really want to maintain a list of the \( q \) variable values from each iteration, I would use something like this:

    def main():
        qvals = [[[0 for j in range(3)] for i in range(2)]]
        for j in range(1, 4):
    # Pass last (most recently added) list in qvals to gurobi_demo
            qvals.append(gurobi_demo(qvals[-1]))
        print(qvals)

    Lastly, I find the algorithm a bit strange. Isn't the optimal solution of every iteration to set all variables to \( 0 \)?

    0
  • Yanbo Fan
    • Gurobi-versary
    • Investigator
    • Thought Leader

    Hi Eli Towle,

    I'm really moved by your careful answers over and over again. I'm lucky to meet such a warm-hearted person as you. After you answer again and again, I finally know the problem. The formal parameters in the function declaration cannot be called in the form of Var, is that right? Use quadexpr. Addconstant() is to pass qc1 as form of number (constant) to Quadexpr. Am I right? 

    What' s more, There is also a small problem. I guess it may be about the call of formal parameters. 

    def gurobi_demo(qc1):
    model = grb.Model('')
    q = model.addVars(2, 3, vtype=GRB.CONTINUOUS, name='q')
    z = model.addVars(2, vtype=GRB.CONTINUOUS, name='z')
    model.update()
    sum = LinExpr()
    model.setObjective(z.sum(), sense=GRB.MINIMIZE)
    for k in range(2):
    rhs = QuadExpr()
    rhs.addTerms(1, q[k, 0], q[k, 0])
    rhs.addTerms(-2 * qc1[k][0], q[k, 0])
    rhs.addConstant(qc1[k][0] * qc1[k][0])

    rhs.addTerms(1, q[k, 1], q[k, 1])
    rhs.addTerms(-2 * qc1[k][1], q[k, 1])
    rhs.addConstant(qc1[k][1] * qc1[k][1])

    model.addConstr(z[k] * z[k] == rhs)
    model.addConstr(z[k] <= 30)
    model.addConstr(z[k] >= 0)
    model.setParam('NonConvex', 2)
    model.optimize()
    return q
    def main():
    qc1 = [[[0 for j in range(3)] for i in range(2)]]
    for j in range(1, 4):
    qc1.append(gurobi_demo(qc1[-1]))
    print(qc1)
    0
  • Eli Towle
    • Gurobi Staff

    Depending on whether your arguments are Var objects or fixed numbers (e.g., the solution values of the variables), you may need to use a different QuadExpr method or pass in the arguments differently. In your case, it's probably easier to build the \( \texttt{rhs} \) expression using only the overloaded \( \texttt{+} \), \( \texttt{-} \), and \( \texttt{+=} \) operators:

    rhs = QuadExpr()
    for i in range(2):

    rhs += q[k, i]*q[k, i] - 2*qc1[k][i]*q[k, i] + qc1[k][i]*qc1[k][i]

    Regarding the \( \texttt{KeyError} \): right now, the \(\texttt{gurobi_demo}\) function returns \( \texttt{q} \), which is a tupledict containing Var objects. We want to return the solution values (X attributes) for these variables. Furthermore, the return values must be structured so they are compatible with how you index \( \texttt{qc1} \) in \(\texttt{gurobi_demo}\). The following should work:

    return [[q[i, j].X for j in range(3)] for i in range(2)]

    By the way, the variables \( q_{0, 2} \) and \( q_{1, 2} \) are not used in any constraints.

    0
  • Yanbo Fan
    • Gurobi-versary
    • Investigator
    • Thought Leader

    Hi Eli Towle, 

    After so many days of your answers again and again, I finally succeeded in solving my problem. I can't imagine how long this problem would bother me without your help. Thank you again for your help. 

    Best wishes, 

    Yanbo

    0

Post is closed for comments.