Skip to main content

Differentiating an equation and updating as a constraint

Answered

Comments

4 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?.
  • Jaromił Najman
    • Gurobi Staff

    Hi Varun,

    You are correct, Gurobi does not accept \(\texttt{sympy}\) objects as input for constraints. However, you can use the \(\texttt{sympy}\) objects to construct strings and work with these. For the sake of simplicity, let us assume that you want to add the partial derivatives of the term \(x_1 \cdot x_2 - x_1 + x_2\) to your model as \( \leq 0\) constraints, i.e., you want to add the constraints \( x_2 + 1 \leq 0\) and \( x_1 - 1 \leq 0\) to your model, which are computed by the \(\texttt{sympy}\) package.

    You can do so with the following code

    import gurobipy as gp
    from gurobipy import GRB
    import sympy as sym

    try:
    # Create a new model
    m = gp.Model("test")

    # Create variable objects bound to the model m
    m.addVar(vtype=GRB.CONTINUOUS,name="x1")
    m.addVar(vtype=GRB.CONTINUOUS,name="x2")

    # now the sympy part
    constraints = []
    variableNames = ["x1","x2"] # save all variable names
    x1 = sym.Symbol("x1")
    x2 = sym.Symbol("x2")

    test = x1*x2 - x1 + x2 # some test term
    # store the partial derivatives of the test term in the constraints list
    constraints.append(str(sym.diff(test,x1))) # = x2 + 1
    constraints.append(str(sym.diff(test,x2))) # = x1 - 1

    # write a Python file which is used to add the partial derivatives
    file = open("partialDerivatives.py","w")
    file.write("def addPartialDerivatives(model):\n")
    for v in variableNames:
    file.write(" "+v+" = model.getVarByName(\""+v+"\")\n")
    for c in constraints:
    file.write(" model.addConstr("+c+" <= 0)\n")
    file.close()

    m.update() # essential to get all information
    # use the written file to add the partial derivative constraints
    import partialDerivatives
    partialDerivatives.addPartialDerivatives(m)

    # write an LP file to check whether everything worked
    m.write("myLP.lp")

    except gp.GurobiError as e:
    print('Error code ' + str(e.errno) + ': ' + str(e))

    except AttributeError:
    print('Encountered an attribute error')

    The above code writes a Python file \(\texttt{partialDerivatives.py}\) which holds the lines

    def addPartialDerivatives(model):
    x1 = model.getVarByName("x1")
    x2 = model.getVarByName("x2")
    model.addConstr(x2 - 1 <= 0)
    model.addConstr(x1 + 1 <= 0)

    In the file, we are first getting the correct variable objects via the getVarByName() function and then add the partial derivative constraints.

    This example is very simple and you will very likely have to adjust a few things for it to work with your mode. Please note, that the focus of the above code is not about performance or automation of each step but rather to present a possible solution.

    Best regards,
    Jaromił

    0
  • Satender .
    • Gurobi-versary
    • First Question
    • First Comment

    Hi Jaromił Najman,

    I tried to extend your code above on matrix variables for adding KKT stationary condition but ran into an error. 

    import sympy as sym
    import numpy as np
    from sympy import Array
    from sympy import MatrixSymbol, Matrix
    from sympy import MatMul

    try:   
       # Create a new model
       m = gp.Model("test")

       # Example Data
       Mvar_rows, Mvar_cols = 1, 2
       c = np.array([1,10])
       A = np.array([
                     [1,2],
                     [3,-4]
       ])
        b = np.array([1,-2])

       # Variables objects for the model
       m.addMVar((Mvar_rows,Mvar_cols), vtype=GRB.CONTINUOUS,name="X")
        m.addMVar((1,A.shape[0]), vtype=GRB.CONTINUOUS, name = "L")

       # the sympy part
       X = MatrixSymbol('X', Mvar_rows, Mvar_cols)
        L = MatrixSymbol('L', 1, A.shape[0])         # Lagrange Multipliers

       constraints = []
       variableNames = ['X', 'L']
       Expression = c @ X.T
       for index in range(A.shape[0]):
         Expression += L[0,index]*(A[index,:]@X.T - b[index])

       # store the partial derivatives of the test term in the constraints list
       for i in range(Mvar_rows):
         for j in range(Mvar_cols):
            constraints.append(str(sym.diff(Expression[0], X[i,j]))) 

       # write a Python file which is used to add the partial derivatives
       file = open("partialDerivatives.py","w")
       file.write("def addPartialDerivatives(model):\n")
       for v in variableNames:
         file.write("    "+v+" = model.getVarByName(\""+v+"\")\n")
       for c in constraints:
           file.write("    model.addConstr("+c+" == 0)\n")
       file.close()
        m.update() # essential to get all information

       # use the written file to add the partial derivative constraints
       import partialDerivatives
        partialDerivatives.addPartialDerivatives(m)

       # write an LP file to check whether everything worked
        m.write("myLP.lp")

    except gp.GurobiError as e:
       print('Error code ' + str(e.errno) + ': ' + str(e))
    except AttributeError:
    print("Attribute Error")
    The file is written correctly (provided below).
    def addPartialDerivatives(model):
       X = model.getVarByName("X")
       L = model.getVarByName("L")
       model.addConstr(L[0, 0] + 3*L[0, 1] + 1 == 0)
        model.addConstr(2*L[0, 0] - 4*L[0, 1] + 10 == 0)

     The error is with getVarByName used on matrix variables. Error is given below, the program is reading var names L and X as none types.

    TypeError                                 Traceback (most recent call last)
    

    <ipython-input-155-2c179d7a7531> in <module>()
    49 # use the written file to add the partial derivative constraints
    50 import partialDerivatives
    ---> 51 partialDerivatives.addPartialDerivatives(m)
    52
    53 # write an LP file to check whether everything worked

    /content/partialDerivatives.py in addPartialDerivatives(model)
    2 X = model.getVarByName("X")
    3 L = model.getVarByName("L")
    ----> 4 model.addConstr(L[0, 0] + 3*L[0, 1] + 1 == 0)
    5 model.addConstr(2*L[0, 0] - 4*L[0, 1] + 10 == 0)


    TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'

     

    0
  • Jaromił Najman
    • Gurobi Staff

    Hi,

    Your variables are named \(\texttt{L[0,i]}\). Thus, you have to use these exact names to retrieve them when using the getVarByName method. Something like the following should work

    L = []
    for i in A.shape[0]:
    L.append(model.getVarByName("L[0,%d]"%i))

    Note that you might have to pass the \(\texttt{A.shape[0]}\) value to your function.

    Best regards, 
    Jaromił

    1

Post is closed for comments.