7.1 The conic interface tutorial

In this tutorial we demonstrate how to set up and solve a linear or conic problem with mosekmodel, the main interface of the API for MATLAB.

A problem solved with mosekmodel has the form

(7.1)\[\begin{split}\begin{array}{lccccl} \mbox{minimize/maximize} & & & c^T x+c^f & & \\ \mbox{subject to} & & & Fx+g & \in & \D, \end{array}\end{split}\]

where

  • \(n\) is the number of decision variables.

  • \(x \in \real^n\) is a vector of decision variables.

  • \(c \in \real^n\) is the linear part of the objective function.

  • \(c^f\in \real\) is a constant term in the objective

  • \(F \in \real^{k \times n}\) is the affine conic constraint matrix.,

  • \(g \in \real^{k}\) is the affine conic constraint constant term vector.,

  • \(\D\) is a domain of dimension \(k\), constructed from the Sec. 13.8 (Supported domains).

Let us indicate a few syntactic features of the API for MATLAB, which allow specifying such poblems with greater flexibility:

  • Compact input with one call. The problem data has only three major elements, F,g,c, so if these matrices are constructed in advance, the problem can be set up with a single call to mosekmodel.

  • Setting up constraint blocks separately. Alternatively, as it is in most practical applications, the constraint section may consist of blocks, each corresponding to some logical part of the problem specification. We refer to them as affine conic constraints (ACCs). In this case it may be more natural to enter individual ACCs

    \[F_ix +g_i\in \D_i\]

    separately, using the method mosekmodel.appendcons. The API for MATLAB will then internally combine these blocks into the \(F,g\) data by vertical stacking and let \(\D = \D_1 \times \cdots \times \D_p\), where \(p\) is the number of ACCs.

  • Linear problems. The general formulation in (7.1) includes standard linear problems by using linear domains. For example \(Ax+b\leq 0\) is expressed as \(Ax+b\in\real_{\leq 0}\) and \(Ax+b=0\) as \(Ax+b\in\{0\}\), see Sec. 13.8 (Supported domains).

  • Shifted domains. To simplify some constructions a domain may be shifted by a vector \(b\), so that effectively one can write

    \[Fx+g\in \D + b.\]

    This is a syntactic feature as one could achieve the same effect replacing \(g\) with \(g-b\), but it is sometimes more natural; especially for linear problems it may be more intuitive to write \(Ax\geq b\) as \(Ax\in\real_{\geq 0} + b\) rather than \(Ax-b\in \real_{\geq 0}\). For obvious reasons this argument is called rhs (right-hand side) when creating a domain with mosekdomain.

After this introduction we proceed to demonstrate examples of linear and conic problems, set up either at once or in chunks. We then finish the tutorial demonstrating how to invoke the optimizer and retrieve the solutions.

7.1.1 Linear example (LO1)

We set up a linear problem in one go. Consider the problem

(7.2)\[\begin{split}\begin{array} {lccccccccl} \mbox{maximize} & 3 x_0 & + & 1 x_1 & + & 5 x_2 & + & 1 x_3 & & \\ \mbox{subject to} & 3 x_0 & + & 1 x_1 & + & 2 x_2 & & & = & 30, \\ & 2 x_0 & + & 1 x_1 & + & 3 x_2 & + & 1 x_3 & \geq & 15, \\ & & & 2 x_1 & & & + & 3 x_3 & \leq & 25, \\ & x_0, & & x_1, & & x_2, & & x_3 & \geq & 0,\\ & & & x_1 & & & & & \leq & 10.\\ \end{array}\end{split}\]

We create a model by providing all data at once:

  • the number of variables is 4 and is set as numvar,

  • the objective vector is \(c=[3,1,5,1]\) and is passed as objective,

  • the objective sense is maximization, and is passed as objsense,

  • the problem name name (optional) is lo1.

Listing 7.1 Setting up a linear model in a single call. Click here to download.
    model = mosekmodel(...
                  name = "lo1", ...
                  objsense = "maximize", ...
                  objective = [ 3 1 5 1 ]', ...
                  numvar = 4, ...
                  F = [ 3 1 2 0 ; ...
                        2 1 3 1 ; ...
                        0 2 0 3 ; ...
                        1 0 0 0 ; ...
                        0 1 0 0 ; ...
                        0 0 1 0 ; ...
                        0 0 0 1 ; ...
                        0 1 0 0 ;], ...
                  domain = [ mosekdomain("equal",        rhs=30), ...
                             mosekdomain("greater than", rhs=15), ...
                             mosekdomain("less than",    rhs=25), ...
                             mosekdomain("nonnegative",  n=4), ...
                             mosekdomain("less than",    rhs=10) ]);

The remaining data is the matrix \(F\), each row of \(F\) corresponding to one linear bound in the problem. For each of those bounds there is a corresponding domain in the list of domains domain which indicates the type of (in)equality (lower-bounded, upper-bounded, equals). We exploit the rhs vector mentioned in the introduction to pass the bounds inside domains, rather than having to add a g vector, although both options would be just as good. Note that the bounds

\[x_0, x_1, x_2, x_3 \geq 0\]

are covered by one domain \(\R_{\geq 0}^4\), that is of dimension n=4 to indicate the number of rows of \(F\) it covers. All other bounds are (by default) 1-dimensional, i.e. correspond to a single row of F. The total dimension of all domains (\(1+1+1+4+1=8\)) equals the number of rows in \(F\).

In large, practical applications the matrix \(F\) can, and should, be specified as a sparse matrix.

See the API reference for mosekmodel for a specification of all possible arguments.

7.1.2 Linear example with multiple calls (LO2)

We can set up the model of (7.2) adding linear constraints one by one with multiple calls to mosekmodel.appendcons. In the example below we first initialize the model object with the name, objective and number of variables, and then we add the linear constraints separately

Listing 7.2 Setting up a linear model with multiple calls. Click here to download.
    model = mosekmodel(name = "lo2", ...
                       objsense = "maximize", ...
                       objective = [ 3 1 5 1 ]', ...
                       numvar = 4);

    model.appendcons(name="con-eq30", F = [ 3 1 2 0 ], domain = mosekdomain("equal",        rhs=30));
    model.appendcons(name="con-gt14", F = [ 2 1 3 1 ], domain = mosekdomain("greater than", rhs=15));
    model.appendcons(name="con-lt25", F = [ 0 2 0 3 ], domain = mosekdomain("less than",    rhs=25)); 
    model.appendcons(name="con-nneg", F = speye(4),    domain = mosekdomain("nonnegative", n=4));
    model.appendcons(name="con-lt10", F = [ 0 1 0 0 ], domain = mosekdomain("less than", rhs="10"));

Each call to mosekmodel.appendcons contains the constraint’s name (optional) and it’s \(F\) matrix and domain as before. In each call to mosekmodel.appendcons the number of rows in \(F\) equals the dimension of the domain.

7.1.3 Conic quadratic example (ACC1)

We now go through an example with non-linear conic constraints, in this case quadratic. All other cones would be added in a similar way. Consider the problem

(7.3)\[\begin{split}\begin{array}{ll} \mbox{maximize} & c^T x \\ \mbox{subject to} & \sum_i x_i = 1, \\ & \gamma \geq \| Gx+h \|_2, \end{array}\end{split}\]

where \(x\in \real^n\) is the optimization variable and \(G\in\real^{k\times n}\), \(h\in\real^k\), \(c\in\real^n\) and \(\gamma\in\real\).

The norm constraint has a conic representation:

(7.4)\[\begin{split}\begin{array}{ll} \mbox{maximize} & c^T x \\ \mbox{subject to} & \sum_i x_i = 1, \\ & (\gamma, Gx+h)\in\Q^{k+1}, \end{array}\end{split}\]

and we can write it explicitly in matrix format \(Fx+g\in\D\) as follows:

\[\begin{split}\left[\begin{array}{c}0\\ G\end{array}\right]x + \left[\begin{array}{c}\gamma\\ h\end{array}\right] \in \Q^{k+1}.\end{split}\]

We formulate the problem by including the linear constraint and the conic constraint in two separate calls.

Listing 7.3 Setting up a conic quadratic model. Click here to download.
    % Initialize the model
    model = mosekmodel(name = "acc1",...
                       numvar = n);
    
    % Set objective vector
    model.objective("maximize", c);

    % The constraint sum(x) = 1
    model.appendcons(F = ones(1,n), domain = mosekdomain("equal", rhs=1));

    % The conic quadratic constraint Fx+g \in \Quad with k+1 rows
    model.appendcons(F = sparse([zeros(1,n); G]), ...
                     g = [gamma; h], ...
                     domain = mosekdomain("quadratic cone", dim=k+1));

7.1.4 Solving and retrieving the solution

We wrap up with a short demonstration of what to do after the model has been defined, that is how to solve the model and retrieve solutions.

Listing 7.4 Solving and retrieving the solution. Click here to download.
    % Solve the problem
    model.solve();

    % Check if solution is available
    [hassol, prosta, solsta] = model.hassolution("interior"); 

    if hassol && solsta == "OPTIMAL"
        % Get primal solution
        xx = model.getsolution("interior", "x");

        % Get dual solution
        y = model.getsolution("interior", "y");

        disp("Primal solution");
        disp(xx);
    end

We note that