7.2 Conic Quadratic Optimization

The structure of a typical conic optimization problem is

\[\begin{split}\begin{array}{lccccl} \mbox{minimize} & & & c^T x+c^f & & \\ \mbox{subject to} & l^c & \leq & A x & \leq & u^c, \\ & l^x & \leq & x & \leq & u^x, \\ & & & Fx+g & \in & \D, \end{array}\end{split}\]

(see Sec. 12 (Problem Formulation and Solutions) for detailed formulations). Here we discuss how to set-up problems with the (rotated) quadratic cones.

MOSEK supports two types of quadratic cones, namely:

  • Quadratic cone:

    \[\Q^n = \left\lbrace x \in \real^n: x_0 \geq \sqrt{\sum_{j=1}^{n-1} x_j^2} \right\rbrace.\]
  • Rotated quadratic cone:

    \[\Qr^n = \left\lbrace x \in \real^n: 2 x_0 x_1 \geq \sum_{j=2}^{n-1} x_j^2,\quad x_0\geq 0,\quad x_1 \geq 0 \right\rbrace.\]

For example, consider the following constraint:

\[(x_4, x_0, x_2) \in \Q^3\]

which describes a convex cone in \(\real^3\) given by the inequality:

\[x_4 \geq \sqrt{x_0^2 + x_2^2}.\]

For other types of cones supported by MOSEK, see Sec. 14.9 (Supported domains) and the other tutorials in this chapter. Different cone types can appear together in one optimization problem.

7.2.1 Example CQO1

Consider the following conic quadratic problem which involves some linear constraints, a quadratic cone and a rotated quadratic cone.

(7.2)\[\begin{split} \begin{array}{ll} \minimize & y_1 + y_2 + y_3 \\ \st & x_1 + x_2 + 2.0 x_3 = 1.0,\\ & x_1,x_2,x_3 \geq 0.0,\\ & (y_1,x_1,x_2) \in \Q^3,\\ & (y_2,y_3,x_3) \in \Qr^3. \end{array}\end{split}\]

We start by creating the optimization model:

with Model('cqo1') as M:

We then define variables x and y. Two logical variables (aliases) z1 and z2 are introduced to model the quadratic cones. These are not new variables, but map onto parts of x and y for the sake of convenience.

    x = M.variable('x', 3, Domain.greaterThan(0.0))
    y = M.variable('y', 3, Domain.unbounded())

    # Create the aliases
    #      z1 = [ y[0],x[0],x[1] ]
    #  and z2 = [ y[1],y[2],x[2] ]
    z1 = Var.vstack(y[0], x[0:2])
    z2 = Var.vstack(y[1:3], x[2])

The linear constraint is defined using the dot product:

    # Create the constraint
    #      x[0] + x[1] + 2.0 x[2] = 1.0
    M.constraint("lc", Expr.dot([1.0, 1.0, 2.0], x) == 1.0)

The conic constraints are defined using the logical views z1 and z2 created previously. Note that this is a basic way of defining conic constraints, and that in practice they would have more complicated structure.

    # Create the constraints
    #      z1 belongs to C_3
    #      z2 belongs to K_3
    # where C_3 and K_3 are respectively the quadratic and
    # rotated quadratic cone of size 3, i.e.
    #                 z1[0] >= sqrt(z1[1]^2 + z1[2]^2)
    #  and  2.0 z2[0] z2[1] >= z2[2]^2
    qc1 = M.constraint("qc1", z1, Domain.inQCone())
    qc2 = M.constraint("qc2", z2, Domain.inRotatedQCone())

We only need the objective function:

    # Set the objective function to (y[0] + y[1] + y[2])
    M.objective("obj", ObjectiveSense.Minimize, Expr.sum(y))

Calling the Model.solve method invokes the solver:

    M.solve()
    M.writeTask('cqo1.ptf')

The primal and dual solution values can be retrieved using Variable.level, Constraint.level and Variable.dual, Constraint.dual, respectively:

    # Get the linear solution values
    solx = x.level()
    soly = y.level()
    # Get conic solution of qc1
    qc1lvl = qc1.level()
    qc1sn = qc1.dual()
Listing 7.2 Fusion implementation of model (7.2). Click here to download.
from mosek.fusion import *
import mosek.fusion.pythonic

with Model('cqo1') as M:

    x = M.variable('x', 3, Domain.greaterThan(0.0))
    y = M.variable('y', 3, Domain.unbounded())

    # Create the aliases
    #      z1 = [ y[0],x[0],x[1] ]
    #  and z2 = [ y[1],y[2],x[2] ]
    z1 = Var.vstack(y[0], x[0:2])
    z2 = Var.vstack(y[1:3], x[2])

    # Create the constraint
    #      x[0] + x[1] + 2.0 x[2] = 1.0
    M.constraint("lc", Expr.dot([1.0, 1.0, 2.0], x) == 1.0)

    # Create the constraints
    #      z1 belongs to C_3
    #      z2 belongs to K_3
    # where C_3 and K_3 are respectively the quadratic and
    # rotated quadratic cone of size 3, i.e.
    #                 z1[0] >= sqrt(z1[1]^2 + z1[2]^2)
    #  and  2.0 z2[0] z2[1] >= z2[2]^2
    qc1 = M.constraint("qc1", z1, Domain.inQCone())
    qc2 = M.constraint("qc2", z2, Domain.inRotatedQCone())

    # Set the objective function to (y[0] + y[1] + y[2])
    M.objective("obj", ObjectiveSense.Minimize, Expr.sum(y))

    # Solve the problem
    M.solve()
    M.writeTask('cqo1.ptf')

    # Get the linear solution values
    solx = x.level()
    soly = y.level()
    print('x1,x2,x3 = %s' % str(solx))
    print('y1,y2,y3 = %s' % str(soly))

    # Get conic solution of qc1
    qc1lvl = qc1.level()
    qc1sn = qc1.dual()
    print('qc1 levels                = %s' % str(qc1lvl))
    print('qc1 dual conic var levels = %s' % str(qc1sn))