Skip to main content

How to formulate a general second-order cone constraint

Answered

Comments

7 comments

  • Riley Clement
    • Gurobi Staff Gurobi Staff

    Hi Fengyu,

    If you want to model ||A_i * x + b_i || <= c_i.T * x + d_i then it should be sufficient to introduce an auxiliary non-negative variable v := c_i.T * x + d_i then square both sides.  You can optionally introduce a free variable (y) to simplify the left hand side, eg

    m = gp.Model()
    ...
    v = m.addVar()
    y = m.addVar(lb=-float("inf"))
    m.addConstr(v == c_i@x + d_i)
    m.addConstr(y == A_i@x + b_i)
    m.addConstr(y*y <= v*v) # soc

    - Riley

     

     

    0
  • Fengyu Yang
    • First Comment
    • Gurobi-versary
    • First Question

    Hi Riley, 

    Thanks for your help. I tried this way but got “Model is infeasible or unbounded”: 

    Gurobi Optimizer version 11.0.0 build v11.0.0rc2 (mac64[arm] - Darwin 22.3.0 22D49)
    CPU model: Apple M2
    Thread count: 8 physical cores, 8 logical processors, using up to 8 threads
    Optimize a model with 14 rows, 9 columns and 82 nonzeros
    Model fingerprint: 0xb0a81c5d
    Model has 2 quadratic constraints
    Coefficient statistics:
     Matrix range     [4e-03, 2e+00]
     QMatrix range    [1e+00, 1e+00]
     Objective range  [3e-01, 2e+00]
     Bounds range     [0e+00, 0e+00]
     RHS range        [2e-02, 7e+00]
    Presolve time: 0.00s
    Barrier solved model in 0 iterations and 0.00 seconds (0.00 work units)
    Model is infeasible or unbounded

    However, the problem is feasible and can be solved by other models like CVXPY. Below is my code. Could you help me find the issue? 

    import numpy as np 
    import gurobipy as gp
    from gurobipy import GRB
    
    
    q = np.array([ 1.54099611, -0.29342891, -2.17878938,  0.56843128, -1.08452234])
    
    A = [np.array([[-0.55882344,  0.13559269,  0.2309147 , -0.61956913,  0.8847803 ],
            [-1.06620742,  0.15351884, -0.63327461, -0.94543633, -1.21768778],
            [-1.26546531, -1.11953518,  1.16648572,  0.92623246,  0.79701002],
            [ 0.21166418,  0.13557114,  1.87277638, -0.30991497,  0.24018023],
            [-1.34876103,  0.24439909, -0.0137207 , -0.19960823, -1.65642469]]),
     np.array([[ 0.11494421,  0.14466901,  1.07417172, -1.12879864, -0.09376858],
            [ 0.99834358, -0.08262167,  0.18253195, -0.29286147, -0.39490341],
            [ 0.44642257,  0.54011354,  0.28233708, -0.514967  , -1.46687432],
            [ 1.10965351,  0.27565496, -1.31818712,  0.88871446,  1.1200386 ],
            [ 0.60258537, -0.92537068, -1.3154607 ,  1.96756662, -0.04131868]])]
    
    b = [np.array([-0.02403159,  0.64799465,  1.73401236, -0.13356864, -0.83510869]),
     np.array([-0.56646389, -1.15361677, -2.50230094,  0.87555775, -2.6725649 ])]
    
    c = [np.array([ 0.98554328, -0.00522718, -0.09443805, -1.12359376, -0.65126342]),
     np.array([-0.0313326 ,  0.49878616, -0.52332327, -0.2514795 , -1.05553181])]
    
    d = [np.array(5.29530156), np.array(7.12259806)]
    
    F = np.array([[-0.55926127, -0.1197086 , -0.16354572, -0.25046325, -1.08822452],
           [ 0.00440725,  0.23278888, -0.82014159, -0.67901932,  1.42508336]])
    g = np.array([ 1.21591643, -0.6859779 ])
    
    dim = q.shape[0]
    nIneq = len(A)
    
    m = gp.Model()
    x = m.addMVar(shape=dim)
    for i in range(nIneq):
        A_i,b_i,c_i,d_i = A[i],b[i],c[i],d[i]
        
        y_i = m.addVar()
        z_i = m.addVar(lb=0)
        
        m.addConstr(z_i==c_i @ x + d_i)
        m.addConstr(y_i==A_i @ x + b_i)
        m.addConstr(y_i*y_i <= z_i*z_i)
        
    m.addConstr(F @ x == g)
    m.setObjective(q @ x, GRB.MINIMIZE)
    
    m.optimize()

     

     

    0
  • Riley Clement
    • Gurobi Staff Gurobi Staff

    Hi Fengyu,

    It will be good for you to read this article: How do I resolve the error "Model is infeasible or unbounded"? 

    Then follow it with How do I determine why my model is infeasible?

    When you compute an IIS you will find the infeasibility is unrelated to the cones.

    Note that the default lower bound for variables in Gurobi is 0 - this is a common cause of unexpected infeasibility.

    - Riley

    0
  • Fengyu Yang
    • First Comment
    • Gurobi-versary
    • First Question

    Hi Riley,

    Thanks a lot for your help! Now I have fixed the infeasibility issue. I wonder if it is possible to get the dual variables corresponding to the cone constraint. Now I get an error at 

     nu = np.array([cone.Pi for cone in cones])
     File src/gurobipy/mconstr.pxi:52 in gurobipy.MConstr.__getattr__
     File src/gurobipy/mqconstr.pxi:169 in gurobipy.MQConstr.getAttr
    AttributeError: 'gurobipy.MQConstr' object has no attribute 'Pi'

    Besides, am I right that additional computation is needed for Gurobi to get the dual variable for QCP ?  My updated code is attached. Thanks! 

    dim = q.shape[0]
    nIneq = len(A)
    m = gp.Model()
    x = m.addMVar(shape=dim,lb=-GRB.INFINITY)
    m.setObjective(q @ x, GRB.MINIMIZE)
    # m.Params.DualReductions = 0
    m.Params.QCPDual = 1 
    cones = []
    for i in range(nIneq):
       A_i,b_i,c_i,d_i = A[i],b[i],c[i],d[i]
       
       y_i = m.addMVar(shape=A_i.shape[0],lb=-GRB.INFINITY)
       z_i = m.addVar()
       
       m.addConstr(z_i == c_i @ x + d_i)
       m.addConstr(y_i == A_i @ x + b_i)
       cone_i = m.addConstr(gp.quicksum(y_i[j] * y_i[j] for j in range(y_i.shape[0])) <= z_i * z_i)
       cones.append(cone_i)
    if F is not None:
       eq_constr = m.addConstr(F @ x == g)
    
    m.optimize()
    x = x.X
    nu = np.array([cone.Pi for cone in cones])
    mu = None
    if F is not None:
       mu = np.array([c.Pi for c in eq_constr])

     

     

    0
  • Riley Clement
    • Gurobi Staff Gurobi Staff

    Hi Fengyu,

    Yes, dual values are available for quadratic constraints.  You can find “QCPi” and other quadratic constraint attributes here: https://docs.gurobi.com/projects/optimizer/en/current/reference/attributes/constraintquadratic.html#qcpi

    - Riley

    0
  • Fengyu Yang
    • First Comment
    • Gurobi-versary
    • First Question

    Hi Riley,

    Thanks! I ran 

    nu = np.array([cone.QCPi for cone in cones])

    , then I got the dual variable for the quadratic constraints. But it is very different from the dual solutions corresponding to the cone conditions from other solvers, e.g. Cvxpy, which exactly formulates the problem as a SOCP. Is the reason Gurobi has to solve SOCP in a QCP format?

    And I notice that I got this output when setting QCPDual=1,

    Solving KKT system to obtain QCP duals...

    Does it mean Gurobi did additional computation to retrieve the dual solutions? 

    Fengyu
     

    1
  • Riley Clement
    • Gurobi Staff Gurobi Staff

    Hi Fengyu,

    Is the reason Gurobi has to solve SOCP in a QCP format?

    I don't think so.  The logs indicate the constraints are recognized as SOCP so then our SOCP solver is used.  What is different about the dual values?  The same primal solution could be dual degenerate giving rise to different dual values, or there could be multiple optimal primal solutions.

    Does it mean Gurobi did additional computation to retrieve the dual solutions? 

    Correct.  From QCPDual docs: “Computing them [QC dual values] can add significant time to the optimization, so you should only set this parameter to 1 if you need them.”

    - Riley

     

    0

Please sign in to leave a comment.