5. Basic API tutorial


In this chapter the reader will learn how to build a simple application that uses MOSEK.

A number of examples is provided to demonstrate the functionality required for solving linear, quadratic, and conic problems as well as mixed integer problems.

Please note that the section on linear optimization also describes most of the basic functionality that is not specific to linear problems. Hence, it is recommended to read Section 5.2 before reading the rest of this chapter.

5.1. The basics

A typical program using the MOSEK .NET interface can be described shortly:

  1. Create an environment (mosek.Env) object.
  2. Set up some environment specific data and initialize the environment object.
  3. Create a task (mosek.Task) object.
  4. Load a problem into the task object.
  5. Optimize the problem.
  6. Fetch the result.
  7. Dispose of the environment and task.

5.1.1. The environment and the task

The first MOSEK related step in any program that employs MOSEK is to create an environment (mosek.Env) object. The environment contains environment specific data such as information about the license file, streams for environment messages etc. Before creating any task objects, the environment must be initialized using mosek.Env.initenv. When this is done one or more task (mosek.Task) objects can be created. Each task is associated with a single environment and defines a complete optimization problem as well as task message streams and optimization parameters.

When done, all task and environments created must be explicitly disposed of using the Dispose method. As tasks depend on their environment, a task must be disposed of before its environment; not doing so will cause memory leaks or fatal errors.

In .NET creation of an environment and a task would look something like this:

...
mosek.Env env = new mosek.Env ();
// input environment data here
env.Init ();
 
mosek.Task task = new mosek.Task (env, taskid, num_con, num_var);
...
// input some task data, optimize etc.
...
task.dispose ()
env.dispose ()

Please note that an environment should, if possible, be shared between multiple tasks.

5.1.2. A simple working example

The following simple example shows a working .NET program which

  • creates an environment and a task,
  • reads a problem from a file,
  • optimizes the problem, and
  • writes the solution to a file.

/* Copyright: Copyright (c) 1998-2007 MOSEK ApS, Denmark. All rights reserved. File: simple.cs Purpose: Demonstrates a very simple example using MOSEK by reading a problem file, solving the problem and writing the solution to a file. */ using System; public class simple { public static void Main (string[] args) { mosek.Task task = null; mosek.Env env = null; if (args.Length == 0) { Console.WriteLine ("Missing argument. The syntax is:"); Console.WriteLine (" simple inputfile [ solutionfile ]"); } else { try { // Make mosek environment. env = new mosek.Env (); // Initialize the environment. env.init (); // Create a task object linked with the environment env. // We create it initially with 0 variables and 0 columns, // since we don't know the size of the problem. task = new mosek.Task (env, 0,0); // We assume that a problem file was given as the first command // line argument (received in `args') task.readdata (args[0]); // Solve the problem task.optimize(); // Print a summary of the solution task.solutionsummary(mosek.streamtype.log); // If an output file was specified, write a solution if (args.Length > 1) { // We define the output format to be OPF, and tell MOSEK to // leave out parameters and problem data from the output file. task.putintparam (mosek.iparam.write_data_format, mosek.dataformat.op); task.putintparam (mosek.iparam.opf_write_solutions, mosek.onoffkey.on); task.putintparam (mosek.iparam.opf_write_hints, mosek.onoffkey.off); task.putintparam (mosek.iparam.opf_write_parameters, mosek.onoffkey.off); task.putintparam (mosek.iparam.opf_write_problem, mosek.onoffkey.off); task.writedata(args[1]); } } finally { // Dispose of task end environment if (task != null) task.Dispose (); if (env != null) env.Dispose (); } } } }

5.1.2.1. Writing a problem to a file

Use the mosek.Task.writedata function to write a problem to a file. By default MOSEK will determine the output file format by the extension of the filename, for example to write an OPF file:

   task.writedata("problem.opf");

5.1.2.2. Inputting and outputting problem data

An optimization problem consists of several components; objective, objective sense, constraints, variable bounds etc. Therefore, the task (mosek.Task) provides a number of methods to operate on the task specific data, all of which are listed in Section 14.10.

5.1.2.3. Setting parameters

Apart from the problem data, the task contains a number of parameters defining the behavior of MOSEK. For example the mosek.iparam.optimizer parameter defines which optimizer to use. A complete list of all parameters are listed in Chapter 15.

5.1.3. Compiling and running examples

All examples presented in this chapter are distributed with MOSEK and are available in the directory

 mosek/5/tools/examples/ 

in the MOSEK installation. Chapter 4 describes how to compile and run the examples.

It is recommended to copy examples to a different directory before modifying and compiling them.

5.2. Linear optimization

The simplest optimization problem is a purely linear problem. A linear optimization problem is a problem of the following form:

Minimize or maximize the objective function

\begin{math}\nonumber{}\sum _{{j=0}}^{{n-1}}c_{j}x_{j}+c^{f}\end{math} (5.2.1)

subject to the linear constraints

\begin{math}\nonumber{}l_{k}^{c}\leq{}\sum _{{j=0}}^{{n-1}}a_{{kj}}x_{j}\leq{}u_{k}^{c},~k=0,\ldots ,m-1,\end{math} (5.2.2)

and the bounds

\begin{math}\nonumber{}l_{j}^{x}\leq{}x_{j}\leq{}u_{j}^{x},~j=0,\ldots ,n-1,\end{math} (5.2.3)

where we have used the problem elements

m and n, which are the number of constraints and variables respectively,

x, which is the variable vector of length n,

c, which is a coefficient vector of size n

\begin{displaymath}\nonumber{}c=\left[\begin{array}{c}\nonumber{}c_{0}\\\nonumber{}\vdots \\\nonumber{}c_{{n-1}}\end{array}\right],\end{displaymath}

[[MathCmd 5]], which is a scalar constant,

A, which is a [[MathCmd 6]] matrix of coefficients is given by

\begin{displaymath}\nonumber{}A=\left[\begin{array}{ccc}\nonumber{}a_{{0,0}} & \cdots  & a_{{0,(n-1)}}\\\nonumber{}\vdots  & \cdots  & \vdots \\\nonumber{}a_{{(m-1),0}} & \cdots  & a_{{(m-1),(n-1)}}\end{array}\right],\end{displaymath}

[[MathCmd 8]] and [[MathCmd 9]], which specify the lower and upper bounds on constraints respectively, and

[[MathCmd 10]] and [[MathCmd 11]], which specifies the lower and upper bounds on variables respectively.

Please note the unconventional notation using 0 as the first index rather than 1. Hence, [[MathCmd 12]] is the first element in variable vector x. This convention has been adapted from .NET arrays which are indexed from 0.

5.2.1. Example: lo1

The following is an example of a linear optimization problem:

\begin{math}\nonumber{}\begin{array}{lccccccccl}\nonumber{}\mbox{maximize} & 3x_{0} & + & 1x_{1} & + & 5x_{2} & + & 1x_{3} &  & \\\nonumber{}\mbox{subject to} & 3x_{0} & + & 1x_{1} & + & 2x_{2} &  &  & = & 30,\\\nonumber{} & 2x_{0} & + & 1x_{1} & + & 3x_{2} & + & 1x_{3} & \geq{} & 15,\\\nonumber{} &  &  & 2x_{1} &  &  & + & 3x_{3} & \leq{} & 25,\end{array}\end{math} (5.2.4)

having the bounds

\begin{math}\nonumber{}\begin{array}{ccccc}\nonumber{}0 & \leq{} & x_{0} & \leq{} & \infty ,\\\nonumber{}0 & \leq{} & x_{1} & \leq{} & 10,\\\nonumber{}0 & \leq{} & x_{2} & \leq{} & \infty ,\\\nonumber{}0 & \leq{} & x_{3} & \leq{} & \infty .\end{array}\end{math} (5.2.5)

5.2.1.1. Source code

The data structures used in the following example will be explained in detail in 5.8.

The .NET program included below, which solves this problem, is found under examples as either a single file, lo1.cs, and as a Microsoft Visual Studio 7 (.NET) Project under

  mosek\5\tools\examp\ 

/* Copyright: Copyright (c) 1998-2007 MOSEK ApS, Denmark. All rights reserved. File: lo1.cs Purpose: Demonstrates how to solve small linear optimization problem using the MOSEK C# API. */ using System; class msgclass : mosek.Stream { string prefix; public msgclass (string prfx) { prefix = prfx; } public override void streamCB (string msg) { Console.Write ("{0}{1}", prefix,msg); } } public class lo1 { public static void Main () { const int NUMCON = 3; const int NUMVAR = 4; const int NUMANZ = 9; // Since the value infinity is never used, we define // 'infinity' symbolic purposes only double infinity = 0; double[] c = {3.0, 1.0, 5.0, 1.0}; int[] ptrb = {0, 2, 5, 7}; int[] ptre = {2, 5, 7, 9}; int[] asub = { 0, 1, 0, 1, 2, 0, 1, 1, 2}; double[] aval = { 3.0, 2.0, 1.0, 1.0, 2.0, 2.0, 3.0, 1.0, 3.0}; mosek.boundkey[] bkc = {mosek.boundkey.fx, mosek.boundkey.lo, mosek.boundkey.up}; double[] blc = {30.0, 15.0, -infinity}; double[] buc = {30.0, +infinity, 25.0}; mosek.boundkey[] bkx = {mosek.boundkey.lo, mosek.boundkey.ra, mosek.boundkey.lo, mosek.boundkey.lo}; double[] blx = {0.0, 0.0, 0.0, 0.0}; double[] bux = {+infinity, 10.0, +infinity, +infinity}; mosek.Task task = null; mosek.Env env = null; double[] xx = new double[NUMVAR]; try { // Make mosek environment. env = new mosek.Env (); // Direct the env log stream to the user specified // method env_msg_obj.streamCB env.set_streamCB (mosek.streamtype.log, new msgclass ("[env]")); // Initialize the environment. env.init (); // Create a task object linked with the environment env. task = new mosek.Task (env, NUMCON,NUMVAR); // Directs the log task stream to the user specified // method task_msg_obj.streamCB task.set_streamCB (mosek.streamtype.log, new msgclass ("[task]")); task.inputdata(NUMCON,NUMVAR, c, 0.0, ptrb, ptre, asub, aval, bkc, blc, buc, bkx, blx, bux); task.putobjsense(mosek.objsense.maximize); try { task.optimize(); } catch (mosek.Warning w) { Console.WriteLine("Mosek warning:"); Console.WriteLine (w.Code); Console.WriteLine (w); } task.getsolutionslice(mosek.soltype.bas, /* Basic solution. */ mosek.solitem.xx, /* Which part of solution. */ 0, /* Index of first variable. */ NUMVAR, /* Index of last variable+1 */ xx); for(int j = 0; j < NUMVAR; ++j) Console.WriteLine ("x[{0}]:{1}", j,xx[j]); } catch (mosek.Exception e) { Console.WriteLine (e.Code); Console.WriteLine (e); } if (task != null) task.Dispose (); if (env != null) env.Dispose (); } }

5.2.1.2. The same example in Visual Basic.NET

The Visual Basic .NET example below is found under examples as a single file lo1.vb.

'
'   File:    lo1.vb
'
'   Purpose: Demonstrates how to solve small linear
'            optimization problem using the MOSEK .net API.
'
Imports System,mosek


public class MsgTest
    Inherits mosek.Stream
    Dim name As String

    Public Sub New (e As mosek.Env, n As String)
'        MyBase.New ()
        name = n
    End Sub

    Public Overrides Sub streamCB (msg As String)
        Console.Write ("{0}: {1}", name, msg)
    End Sub
End Class


module lo1
    public sub main ()
        dim NUMCON as Integer = 3
        dim NUMVAR as Integer = 4

        dim bkc as boundkey() = { boundkey.fx, boundkey.lo, boundkey.up }
        dim bkx as boundkey() = { boundkey.lo, boundkey.ra, boundkey.lo, boundkey.lo }

        dim ptrb as Integer() = { 0, 2, 5, 7}
        dim ptre as Integer() = { 2, 5, 7, 9 }
        dim subs as Integer() = { 0, 1, _
                                  0, 1, 2, _
                                  0, 1, _
                                  1, 2 }

        dim blc as Double() = { 30.0, 15.0, -mosek.Val.infinity }
        dim buc as Double() = { 30.0, mosek.Val.infinity, 25.0 }
        dim c   as Double() = { 3.0, 1.0, 5.0, 1.0 }
        dim blx as Double() = { 0.0, 0.0, 0.0, 0,0}
        dim bux as Double() = { mosek.Val.infinity, 10, mosek.Val.infinity, mosek.Val.infinity }
        dim val as Double() = { 3.0, 2.0, _
                                1.0, 1.0, 2.0, _
                                2.0, 3.0, _
                                1.0, 3.0 }
        dim xx  as Double() = { 0, 0, 0, 0, 0 }
        
        dim task as mosek.Task
        dim env  as mosek.Env  
	dim msg as MsgTest

	Try
	        env  = new mosek.Env ()
        	env.init ()
        	task = new mosek.Task (env, NUMCON,NUMVAR)
		msg = New MsgTest (env, "msg")
		task.set_streamCB (streamtype.log, msg)
        
	        call task.inputdata(NUMCON, NUMVAR, _
                	            c, _
                        	    0.0, _
	                            ptrb,ptre, _
        	                    subs, _
                	            val, _
                        	    bkc, blc, buc, _
	                            bkx, blx, bux)

                task.putobjsense(mosek.objsense.maximize)

	        task.putintparam (iparam.write_generic_names, 1)
        	task.writedata ("small.lp")
    
	        task.optimize()

		Console.WriteLine ("*****************************")
		task.solutionsummary (streamtype.log)
    
	        task.getsolutionslice(soltype.itr, solitem.xx, 0, NUMVAR - 1, xx)

	        dim j as integer    
        	for j  = 0 to NUMVAR - 1
	            Console.WriteLine ("x[{0}]:{1}", j,xx(j))
        	next

	        Console.WriteLine ("Finished optimization")
	Catch e as mosek.Exception
		Console.WriteLine ("MosekException caught, {0}",e)
	Catch e as System.Exception
		Console.WriteLine ("System.Exception caught, {0}",e)
	End Try
    end sub
end module

5.2.1.3. Example code comments

The MOSEK environment:

Before setting up the optimization problem, a MOSEK environment must be created and initialized. This is done on the lines:

// Make mosek environment. env = new mosek.Env (); // Direct the env log stream to the user specified // method env_msg_obj.streamCB env.set_streamCB (mosek.streamtype.log, new msgclass ("[env]")); // Initialize the environment. env.init ();

We connect a call-back function to the environment log stream. In this case the call-back function simply prints messages to the standard output stream.

MOSEK optimization task:

Next, an empty task object is created:

// Create a task object linked with the environment env. task = new mosek.Task (env, NUMCON,NUMVAR); // Directs the log task stream to the user specified // method task_msg_obj.streamCB task.set_streamCB (mosek.streamtype.log, new msgclass ("[task]"));

We also connect a call-back function to the task log stream. Messages related to the task are passed to the call-back function. In this case the stream call-back function writes its messages to the standard output stream.

Inputting the problem data:

When the task has been created, data can be loaded into it. This happens here:

task.inputdata(NUMCON,NUMVAR, c, 0.0, ptrb, ptre, asub, aval, bkc, blc, buc, bkx, blx, bux);

There are several different ways to set up an optimization problem; in this case we loaded the whole problem using a single function, mosek.Task.inputdata.

The ptrb, ptre, asub, and aval arguments define the constraint matrix A in the column ordered sparse format (for details, see Section 5.8.3.2).

The c argument is a full vector defining the objective function.

The precise relation between the arguments and the mathematical expressions in (5.2.1)[[MathCmd 15]](5.2.3) is as follows.

  • The linear terms in the constraints:

    \begin{math}\nonumber{}\begin{array}{rl}\nonumber{}a_{{\mathtt{sub[t],j}}}=\mathtt{val[t]}, & t=\mathtt{ptrb[j]},\ldots ,\mathtt{ptre[j]}-1,\\\nonumber{} & j=0,\ldots ,\mathtt{numvar}-1.\end{array}\end{math} (5.2.6)

    For an illustrated example of the meaning of ptrb and ptre see Section 5.8.3.2.

  • The linear terms in the objective:

    \begin{math}\nonumber{}c_{j}=\mathtt{c[j]},~j=0,\ldots ,\mathtt{numvar}-1\end{math} (5.2.7)
  • The bounds for the constraints are specified using the bkc, blc, and buc variables. The components of the bkc integer array specify the type of the bounds according to Table 5.1.

    Symbolic constant Lower bound Upper bound
    mosek.boundkey.fx finite identical to the lower bound
    mosek.boundkey.fr minus infinity plus infinity
    mosek.boundkey.lo finite plus infinity
    mosek.boundkey.ra finite finite
    mosek.boundkey.up minus infinity finite
    Table 5.1: Interpretation of the bound keys.

    For instance bkc[2]= mosek.boundkey.lo means that [[MathCmd 18]] and [[MathCmd 19]]. Finally, the numerical values of the bounds are given by

    \begin{math}\nonumber{}l_{k}^{c}=\mathtt{blc[k]},~k=0,\ldots ,\mathtt{numcon}-1\end{math} (5.2.8)

    and

    \begin{math}\nonumber{}u_{k}^{c}=\mathtt{buc[k]},~k=0,\ldots ,\mathtt{numcon}-1.\end{math} (5.2.9)
  • The bounds on the variables are specified using the bkx, blx, and bux variables. The components in the bkx integer array specifies the type of the bounds according to Table 5.1. The numerical values for the lower bounds on the variables are given by

    \begin{math}\nonumber{}l_{j}^{x}=\mathtt{blx[j]},~j=0,\ldots ,\mathtt{numvar}-1.\end{math} (5.2.10)

    The numerical values for the upper bounds on the variables are given by

    \begin{math}\nonumber{}u_{j}^{x}=\mathtt{bux[j]},~j=0,\ldots ,\mathtt{numvar}-1.\end{math} (5.2.11)
Optimization:

After set-up the task can be optimized.

task.optimize();
Outputting the solution:

Finally, the primal solution is retrieved and printed.

task.getsolutionslice(mosek.soltype.bas, /* Basic solution. */ mosek.solitem.xx, /* Which part of solution. */ 0, /* Index of first variable. */ NUMVAR, /* Index of last variable+1 */ xx);

The mosek.Task.getsolutionslice function obtains a “slice” of the solution. In fact MOSEK may compute several solutions depending on the optimizer employed. In this example the basic solution is requested, specified by mosek.soltype.bas. The mosek.solitem.xx specifies that we want the variable values of the solution, and the following 0 and NUMVAR specifies the range of variable values we want.

The range specified is the first index (here “0”) up to but not including the second index (here “NUMVAR).

Catching exceptions:

We cache any exceptions thrown by mosek in the lines:

catch (mosek.Exception e) { Console.WriteLine (e.Code); Console.WriteLine (e); }

The types of exceptions that MOSEK can throw can be seen in 14.5 and 14.11.

5.2.2. An alternative implementation: lo2

In the previous example the problem data is loaded in one chunk. It is often more convenient to add one constraint or one variable at a time — this is possible using the following approach:

  • Before a constraint or a variable can be used it has to be added with mosek.Task.append or a similar function. By default the appended constraints will be empty and the bounds of the appended constraints are infinite. Variables are fixed at zero.
  • The objective function is specified using mosek.Task.putcfix and mosek.Task.putcj.
  • The lower and upper bounds on the constraints and variables are specified using mosek.Task.putbound.
  • The non-zero entries in A are added one column at a time using mosek.Task.putavec.

/* Copyright: Copyright (c) 1998-2007 MOSEK ApS, Denmark. All rights reserved. File: lo2.cs Purpose: Demonstrates how to solve small linear optimization problem using the MOSEK C# API. */ using System; public class lo2 { public static void Main () { // Since the value infinity is never used, we define // 'infinity' symbolic purposes only double infinity = 0; const int NUMCON = 3; const int NUMVAR = 4; const int NUMANZ = 9; double[] c = {3.0, 1.0, 5.0, 1.0}; mosek.boundkey[] bkc = {mosek.boundkey.fx, mosek.boundkey.lo, mosek.boundkey.up}; double[] blc = {30.0, 15.0, -infinity}; double[] buc = {30.0, +infinity, 25.0}; mosek.boundkey[] bkx = {mosek.boundkey.lo, mosek.boundkey.ra, mosek.boundkey.lo, mosek.boundkey.lo}; double[] blx = {0.0, 0.0, 0.0, 0.0}; double[] bux = {+infinity, 10.0, +infinity, +infinity}; int[][] asub = new int[NUMVAR][]; asub[0] = new int[] {0, 1}; asub[1] = new int[] {0, 1, 2}; asub[2] = new int[] {0, 1}; asub[3] = new int[] {1, 2}; double[][] aval = new double[NUMVAR][]; aval[0] = new double[] { 3.0, 2.0 }; aval[1] = new double[] { 1.0, 1.0, 2.0}; aval[2] = new double[] { 2.0, 3.0}; aval[3] = new double[] { 1.0, 3.0}; double[] xx = new double[NUMVAR]; mosek.Task task = null; mosek.Env env = null; try { // Make mosek environment. env = new mosek.Env (); // Initialize the environment. env.init (); // Create a task object linked with the environment env. task = new mosek.Task (env, NUMCON,NUMVAR); /* Give MOSEK an estimate on the size of the data to input. This is done to increase the speed of inputing data and is optional.*/ task.putmaxnumvar(NUMVAR); task.putmaxnumcon(NUMCON); task.putmaxnumanz(NUMANZ); /* Append the constraints. */ task.append(mosek.accmode.con,NUMCON); /* Append the variables. */ task.append(mosek.accmode.var,NUMVAR); /* Put C. */ task.putcfix(0.0); for(int j=0; j<NUMVAR; ++j) task.putcj(j,c[j]); /* Put constraint bounds. */ for(int i=0; i<NUMCON; ++i) task.putbound(mosek.accmode.con,i,bkc[i],blc[i],buc[i]); /* Put variable bounds. */ for(int j=0; j<NUMVAR; ++j) task.putbound(mosek.accmode.var,j,bkx[j],blx[j],bux[j]); /* Put A. */ if ( NUMCON>0 ) { for(int j=0; j<NUMVAR; ++j) task.putavec(mosek.accmode.var, j, asub[j], aval[j]); } task.putobjsense(mosek.objsense.maximize); try { task.optimize(); } catch (mosek.Warning w) { Console.WriteLine("Mosek warning:"); Console.WriteLine (w.Code); Console.WriteLine (w); } task.getsolutionslice(mosek.soltype.bas, /* Basic solution. */ mosek.solitem.xx, /* Which part of solution. */ 0, /* Index of first variable. */ NUMVAR, /* Index of last variable+1 */ xx); for(int j = 0; j < NUMVAR; ++j) Console.WriteLine ("x[{0}]:{1}", j,xx[j]); } catch (mosek.Exception e) { Console.WriteLine (e.Code); Console.WriteLine (e); } if (task != null) task.Dispose (); if (env != null) env.Dispose (); } }

5.3. Quadratic optimization

It is possible to solve quadratic and quadratically constrained convex problems using MOSEK. This class of problems can be formulated as follows:

\begin{math}\nonumber{}\begin{array}{lrcccll}\nonumber{}\mbox{minimize} &  &  & \frac{1}{2}x^{T}Q^{o}x+c^{T}x+c^{f} &  &  & \\\nonumber{}\mbox{subject to} & l_{k}^{c} & \leq{} & \frac{1}{2}x^{T}Q^{k}x+\sum \limits _{{j=0}}^{{n-1}}a_{{k,j}}x_{j} & \leq{} & u_{k}^{c}, & k=0,\ldots ,m-1,\\\nonumber{} & l^{x} & \leq{} & x & \leq{} & u^{x}, & j=0,\ldots ,n-1.\end{array}\end{math} (5.3.1)

Without loss of generality it is assumed that [[MathCmd 25]] and [[MathCmd 26]] are all symmetric because

\begin{displaymath}\nonumber{}x^{T}Qx=0.5x^{T}(Q+Q^{T})x.\end{displaymath}

This implies that a non-symmetric Q can be replaced by the symmetric matrix [[MathCmd 28]].

A very important restriction in MOSEK is that the problem should be convex. This implies that the matrix [[MathCmd 25]] should be positive semi-definite and that the kth constraint must be of the form

\begin{math}\nonumber{}l_{k}^{c}\leq{}\frac{1}{2}x^{T}Q^{k}x+\sum \limits _{{j=0}}^{{n-1}}a_{{k,j}}x_{j}\end{math} (5.3.2)

with a negative semi-definite [[MathCmd 26]], or of the form

\begin{math}\nonumber{}\frac{1}{2}x^{T}Q^{k}x+\sum \limits _{{j=0}}^{{n-1}}a_{{k,j}}x_{j}\leq{}u_{k}^{c}.\end{math} (5.3.3)

with a positive semi-definite [[MathCmd 26]]. This implies that quadratic equalities are specifically not allowed.

5.3.1. Example: Quadratic objective

The following is an example if a quadratic, linearly constrained problem:

\begin{math}\nonumber{}\begin{array}{lcccl}\nonumber{}\mbox{minimize} &  &  & x_{1}^{2}+0.1x_{2}^{2}+x_{3}^{2}-x_{1}x_{3}-x_{2} & \\\nonumber{}\mbox{subject to} & 1 & \leq{} & x_{1}+x_{2}+x_{3} & \\\nonumber{} &  &  & x\geq{}0 &\end{array}\end{math} (5.3.4)

This can be written equivalently as

\begin{math}\nonumber{}\begin{array}{lccl}\nonumber{}\mbox{minimize} & 1/2x^{T}Q^{o}x+c^{T}x &  & \\\nonumber{}\mbox{subject to} & Ax & \geq{} & b\\\nonumber{} & x & \geq{} & 0,\end{array}\end{math} (5.3.5)

where

\begin{math}\nonumber{}Q^{o}=\left[\begin{array}{ccc}\nonumber{}2 & 0 & -1\\\nonumber{}0 & 0.2 & 0\\\nonumber{}-1 & 0 & 2\end{array}\right],\quad{}c=\left[\begin{array}{c}\nonumber{}0\\\nonumber{}-1\\\nonumber{}0\end{array}\right],\quad{}A=\left[\begin{array}{ccc}\nonumber{}1 & 1 & 1\end{array}\right],\mbox{ and }b=1.\end{math} (5.3.6)

Please note that MOSEK always assumes that there is a 1/2 in front of the [[MathCmd 37]] term in the objective. Therefore, the 1 in front of [[MathCmd 38]] becomes 2 in Q, i.e. [[MathCmd 39]].

5.3.1.1. Source code

/* File: qo1.cs Purpose: Demonstrate how to solve a quadratic optimization problem using the MOSEK .NET API. */ using System; public class QuadOpt { public static void Main () { // Since the value infinity is never used, we define // 'infinity' symbolic purposes only const double infinity = 0; const int NUMCON = 1; /* Number of constraints. */ const int NUMVAR = 3; /* Number of variables. */ const int NUMANZ = 9; /* Number of numzeros in A. */ const int NUMQNZ = 4; /* Number of nonzeros in Q. */ double[] c = {0.0,-1.0,0.0}; mosek.boundkey[] bkc = {mosek.boundkey.lo}; double[] blc = {1.0}; double[] buc = {infinity}; mosek.boundkey[] bkx = {mosek.boundkey.lo, mosek.boundkey.lo, mosek.boundkey.lo}; double[] blx = {0.0, 0.0, 0.0}; double[] bux = {+infinity, +infinity, +infinity}; int[] ptrb = {0, 1, 2 }; int[] ptre = {1, 2, 3}; int[] asub = { 0, 0, 0}; double[] aval = {1.0, 1.0, 1.0}; mosek.Task task = null; mosek.Env env = null; double[] xx = new double[NUMVAR]; try { env = new mosek.Env (); env.init (); task = new mosek.Task (env, NUMCON,NUMVAR); task.inputdata(NUMCON,NUMVAR, c,0.0, ptrb, ptre, asub, aval, bkc, blc, buc, bkx, blx, bux); /* * The lower triangular part of the Q * matrix in the objective is specified. */ int[] qsubi = {0, 1, 2, 2 }; int[] qsubj = {0, 1, 0, 2 }; double[] qval = {2.0, 0.2, -1.0, 2.0}; /* Input the Q for the objective. */ task.putobjsense(mosek.objsense.minimize); task.putqobj(qsubi,qsubj,qval); task.optimize(); task.getsolutionslice(mosek.soltype.itr, mosek.solitem.xx, 0, NUMVAR, xx); Console.WriteLine ("Primal solution"); for ( int j=0; j<NUMVAR; ++j ) Console.Write("x[{0}]: {1}\n",j,xx[j]); } catch (mosek.Exception e) { Console.WriteLine (e); } if (task != null) task.Dispose (); if (env != null) env.Dispose (); } /* Main */ }

5.3.1.2. Example code comments

Most of the functionality in this example has already been explained for the linear optimization example in Section 5.2 and it will not be repeated here.

This example introduces one new function, mosek.Task.putqobj, which is used to input the quadratic terms of the objective function.

Since [[MathCmd 25]] is symmetric only the lower triangular part of [[MathCmd 25]] is inputted. The upper part of [[MathCmd 25]] is computed by MOSEK using the relation

\begin{displaymath}\nonumber{}Q^{o}_{{ij}}=Q^{o}_{{ji}}.\end{displaymath}

Entries from the upper part may not appear in the input.

The lower triangular part of the matrix [[MathCmd 25]] is specified using an unordered sparse triplet format (for details, see Section 5.8.3):

int[] qsubi = {0, 1, 2, 2 }; int[] qsubj = {0, 1, 0, 2 }; double[] qval = {2.0, 0.2, -1.0, 2.0};

Please note that

  • only non-zero elements are specified (any element not specified is 0 by definition),
  • the order of the non-zero elements is insignificant, and
  • only the lower triangular part should be specified.

Finally, the matrix [[MathCmd 25]] is loaded into the task:

task.putqobj(qsubi,qsubj,qval);

5.3.2. Example: Quadratic constraints

In this section describes how to solve a problem with quadratic constraints. Please note that quadratic constraints are subject to the convexity requirement (5.3.2).

Consider the problem:

\begin{math}\nonumber{}\begin{array}{lcccl}\nonumber{}\mbox{minimize} &  &  & x_{1}^{2}+0.1x_{2}^{2}+x_{3}^{2}-x_{1}x_{3}-x_{2} & \\\nonumber{}\mbox{subject to} & 1 & \leq{} & x_{1}+x_{2}+x_{3}-x_{1}^{2}-x_{2}^{2}-0.1x_{3}^{2}+0.2x_{1}x_{3}, & \\\nonumber{} &  &  & x\geq{}0. &\end{array}\end{math} (5.3.7)

This is equivalent to

\begin{math}\nonumber{}\begin{array}{lccl}\nonumber{}\mbox{minimize} & 1/2x^{T}Q^{o}x+c^{T}x &  & \\\nonumber{}\mbox{subject to} & 1/2x^{T}Q^{0}x+Ax & \geq{} & b,\end{array}\end{math} (5.3.8)

where

\begin{math}\nonumber{}Q^{o}=\left[\begin{array}{ccc}\nonumber{}2 & 0 & -1\\\nonumber{}0 & 0.2 & 0\\\nonumber{}-1 & 0 & 2\end{array}\right],\quad{}c=\left[\begin{array}{c}\nonumber{}0\\\nonumber{}-1\\\nonumber{}0\end{array}\right],\quad{}A=\left[\begin{array}{ccc}\nonumber{}1 & 1 & 1\end{array}\right],\quad{}b=1.\end{math} (5.3.9)
\begin{math}\nonumber{}Q^{0}=\left[\begin{array}{ccc}\nonumber{}-2 & 0 & 0.2\\\nonumber{}0 & -2 & 0\\\nonumber{}0.2 & 0 & -0.2\end{array}\right].\end{math} (5.3.10)

5.3.2.1. Source code

/* Copyright: Copyright (c) 1998-2007 MOSEK ApS, Denmark. All rights reserved. File: qcqo1.cs Purpose: Demonstrate how to solve a quadratic optimization problem using the MOSEK API. minimize x0^2 + 0.1 x1^2 + x2^2 - x0 x2 - x1 s.t 1 <= x0 + x1 + x2 - x0^2 - x1^2 - 0.1 x2^2 + 0.2 x0 x2 x >= 0 */ using System; public class qcqo1 { public static void Main () { const double inf = 0.0; const int NUMCON = 1; /* Number of constraints. */ const int NUMVAR = 3; /* Number of variables. */ const int NUMANZ = 3; /* Number of numzeros in A. */ const int NUMQNZ = 4; /* Number of nonzeros in Q. */ mosek.boundkey[] bkc = { mosek.boundkey.lo }, bkx = { mosek.boundkey.lo, mosek.boundkey.lo, mosek.boundkey.lo }; int[] ptrb = { 0, 1, 2 }, ptre = { 1, 2, 3 }, sub = { 0, 0, 0 }; double[] blc = { 1.0 }, buc = { inf }, c = { 0.0, -1.0, 0.0 }, blx = { 0.0, 0.0, 0.0 }, bux = { inf, inf, inf }, val = { 1.0, 1.0, 1.0 }, xx = new double[NUMVAR]; mosek.Task task = null; mosek.Env env = null; try { env = new mosek.Env (); env.init (); task = new mosek.Task (env, NUMCON,NUMVAR); /* Define bounds for the constraint. */ /* Constraint: 0 */ bkc[0] = mosek.boundkey.lo; blc[0] = 1.0; buc[0] = mosek.Val.infinity; /* Define information for the variables. */ /* Variable: x0 */ c[0] = 0.0; /* Constraint matrix. */ ptrb[0] = 0; ptre[0] = 1; sub[0] = 0; val[0] = 1.0; /* Bounds. */ bkx[0] = mosek.boundkey.lo; blx[0] = 0.0; bux[0] = mosek.Val.infinity; /* Variable: x1 */ c[1] = -1.0; ptrb[1] = 1; ptre[1] = 2; sub[1] = 0; val[1] = 1.0; bkx[1] = mosek.boundkey.lo; blx[1] = 0.0; bux[1] = mosek.Val.infinity; /* Variable: x2 */ c[2] = 0.0; ptrb[2] = 2; ptre[2] = 3; sub[2] = 0; val[2] = 1.0; bkx[2] = mosek.boundkey.lo; blx[2] = 0.0; bux[2] = mosek.Val.infinity; task.inputdata(NUMCON,NUMVAR, c,0.0, ptrb, ptre, sub, val, bkc, blc, buc, bkx, blx, bux); /* * The lower triangular part of the Q * matrix in the objective is specified. */ { int[] qsubi = { 0, 1, 2, 2 }, qsubj = { 0, 1, 0, 2 }; double[] qval = { 2.0, 0.2, -1.0, 2.0 }; /* Input the Q for the objective. */ task.putqobj(qsubi,qsubj,qval); } /* * The lower triangular part of the Q^0 * matrix in the first constraint is specified. * This corresponds to adding the term * - x0^2 - x1^2 - 0.1 x2^2 + 0.2 x0 x2 */ { int[] qsubi = { 0, 1, 2, 2 }, qsubj = { 0, 1, 2, 0 }; double[] qval = { -2.0, -2.0, -0.2, 0.2 }; /* put Q^0 in constraint with index 0. */ task.putqconk (0, qsubi, qsubj, qval); } task.putobjsense(mosek.objsense.minimize); task.optimize(); task.getsolutionslice(mosek.soltype.itr, mosek.solitem.xx, 0, NUMVAR, xx); Console.WriteLine ("Primal solution"); for ( int j=0; j<NUMVAR; ++j ) Console.Write("x[{0}]: {1}\n",j,xx[j]); } catch (mosek.Exception e) { Console.WriteLine (e); } if (task != null) task.Dispose (); if (env != null) env.Dispose (); } /* Main */ }

The only new function introduced in this example is mosek.Task.putqconk, which is used to add quadratic terms to the constraints. While mosek.Task.putqconk add quadratic terms to a specific constraint, it is also possible to input all quadratic terms in all constraints in one chunk using the mosek.Task.putqcon function.

5.4. Conic optimization

Conic problems are a generalization of linear problems, allowing constraints of the type

\begin{displaymath}\nonumber{}x\in{}\mathcal{C}\end{displaymath}

where [[MathCmd 51]] is a convex cone.

MOSEK can solve conic optimization problems of the following form

\begin{math}\nonumber{}\begin{array}{lccccl}\nonumber{}\mbox{minimize} &  &  & c^{T}x+c^{f} &  & \\\nonumber{}\mbox{subject to} & l^{c} & \leq{} & Ax & \leq{} & u^{c},\\\nonumber{} & l^{x} & \leq{} & x & \leq{} & u^{x},\\\nonumber{} &  &  & x\in{}\mathcal{C} &  &\end{array}\end{math} (5.4.1)

where [[MathCmd 51]] is a cone. [[MathCmd 51]] can be a product of cones, i.e.

\begin{displaymath}\nonumber{}\mathcal{C}=\mathcal{C}_{0}\times \cdots \times \mathcal{C}_{{p-1}}\end{displaymath}

in which case [[MathCmd 56]] means [[MathCmd 57]]. Please note that the set of real numbers [[MathCmd 58]] is itself a cone, so linear variables are still allowed.

MOSEK supports two specific cones apart from the real numbers:

When creating a conic problem in MOSEK, each cone is defined by a cone type (quadratic or rotated quadratic cone) and a list of variable indexes. To summarize:

5.4.1. Example: cqo1

The problem

\begin{math}\nonumber{}\begin{array}{lccccc}\nonumber{}\mbox{minimize} & x_{4}+x_{5} &  & \\\nonumber{}\mbox{subject to} & x_{0}+x_{1}+x_{2}+x_{3} & = & 1,\\\nonumber{} & x_{0},x_{1},x_{2},x_{3} & \geq{} & 0,\\\nonumber{} & x_{4}\geq{}\sqrt{x_{0}^{2} + x_{2}^{2}}, &  & \\\nonumber{} & x_{5}\geq{}\sqrt{x_{1}^{2} + x_{3}^{2}} &  &\end{array}\end{math} (5.4.2)

is an example of a conic quadratic optimization problem. The problem includes a set of linear constraints and two quadratic cones.

5.4.1.1. Source code

/* Copyright: Copyright (c) 1998-2007 MOSEK ApS, Denmark. All rights reserved. File: cqo1.cs Purpose: Demonstrates how to solve a small conic qaudratic optimization problem using the MOSEK API. */ using System; public class cqo1 { public static void Main () { const int NUMCON = 1; const int NUMVAR = 6; const int NUMANZ = 4; // Since the value infinity is never used, we define // 'infinity' symbolic purposes only double infinity = 0; mosek.boundkey[] bkc = { mosek.boundkey.fx }; double[] blc = { 1.0 }; double[] buc = { 1.0 }; mosek.boundkey[] bkx = {mosek.boundkey.lo, mosek.boundkey.lo, mosek.boundkey.lo, mosek.boundkey.lo, mosek.boundkey.fr, mosek.boundkey.fr}; double[] blx = { 0.0, 0.0, 0.0, -infinity, -infinity}; double[] bux = { 0.0, 0.0, 0.0, +infinity, +infinity}; double[] c = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0}; double[] aval = {1.0, 1.0, 1.0, 1.0}; int[] asub = {0, 0, 0, 0}; int[] ptrb = {0,1,2,3,5,5}; int[] ptre = {1,2,3,4,5,5}; int[] csub = new int[3]; double[] xx = new double[NUMVAR]; mosek.Env env = null; mosek.Task task = null; try { // create a new environment object env = new mosek.Env (); // if you like, do some pre-initialization here // (e.g. setting up exit or stream callback) // then initialize the environment for use env.init (); // create a task object task = new mosek.Task (env, NUMCON, NUMVAR); task.inputdata(NUMCON,NUMVAR, c,0.0, ptrb, ptre, asub, aval, bkc, blc, buc, bkx, blx, bux); csub[0] = 4; csub[1] = 0; csub[2] = 2; task.appendcone(mosek.conetype.quad, 0.0, /* For future use only, can be set to 0.0 */ csub); csub[0] = 5; csub[1] = 1; csub[2] = 3; task.appendcone(mosek.conetype.quad,0.0,csub); Console.Write ("putintparam"); task.putobjsense(mosek.objsense.minimize); Console.Write ("optimize"); try { task.optimize(); } catch (mosek.Exception e) { Console.WriteLine (e); } Console.Write ("solutionsummary"); task.solutionsummary(mosek.streamtype.msg); Console.Write ("getsolutionslice"); task.getsolutionslice(mosek.soltype.itr, /* Interior solution. */ mosek.solitem.xx, /* Which part of solution. */ 0, /* Index of first variable. */ NUMVAR, /* Index of last variable+1 */ xx); for(int j = 0; j < NUMVAR; ++j) Console.WriteLine ("x[{0}]:{1}", j,xx[j]); } catch (mosek.Exception e) { Console.WriteLine (e); } if (task != null) task.Dispose (); if (env != null) env.Dispose (); } }

5.4.1.2. Source code comments

The only new function introduced in the example is mosek.Task.appendcone, which is called here:

task.appendcone(mosek.conetype.quad, 0.0, /* For future use only, can be set to 0.0 */ csub);

Here mosek.conetype.quad defines the cone type, in this case it is a quadratic cone. The cone parameter 0.0 is currently not used by MOSEK — simply passing 0.0 will work. c c

5.5. Integer optimization

An optimization problem where one or more of the variables are constrained to integer values is denoted an integer optimization problem.

5.5.1. Example: milo1

In this section the example

\begin{math}\nonumber{}\begin{array}{lccl}\nonumber{}\mbox{maximize} & x_{0}+0.64x_{1} &  & \\\nonumber{}\mbox{subject to} & 50x_{0}+31x_{1} & \leq{} & 250,\\\nonumber{} & 3x_{0}-2x_{1} & \geq{} & -4,\\\nonumber{} & x_{0},x_{1}\geq{}0 &  & \mbox{and integer}\end{array}\end{math} (5.5.1)

is used to demonstrate how to solve a problem with integer variables.

5.5.1.1. Source code

The example (5.5.1) is almost identical to a linear optimization problem except for some variables being integer constrained. Therefore, only the specification of the integer constraints requires something new compared to the linear optimization problem discussed previously. In MOSEK these constraints are specified using the function mosek.Task.putvartype as shown in the code:

for(int j=0; j<NUMVAR; ++j) task.putvartype(j,mosek.variabletype.type_int);

The complete source for the example is listed below.

/* Copyright: Copyright (c) 1998-2007 MOSEK ApS, Denmark. All rights reserved. File: mio1.cs Purpose: Demonstrates how to solve a small mixed integer linear optimization problem using the MOSEK C# API. */ using System; public class MsgClass : mosek.Stream { public MsgClass () { /* Construct the object */ } public override void streamCB (string msg) { Console.Write ("{0}",msg); } } public class lo1 { public static void Main () { const int NUMCON = 2; const int NUMVAR = 2; const int NUMANZ = 4; // Since the value infinity is never used, we define // 'infinity' symbolic purposes only double infinity = 0; mosek.boundkey[] bkc = { mosek.boundkey.up, mosek.boundkey.lo }; double[] blc = { -infinity, -4.0 }; double[] buc = { 250.0, infinity }; mosek.boundkey[] bkx = { mosek.boundkey.lo, mosek.boundkey.lo }; double[] blx = { 0.0, 0.0 }; double[] bux = { infinity, infinity }; double[] c = {1.0, 0.64 }; int[] asub = { 0, 1, 0, 1 }; double[] aval = { 50.0, 3.0, 31.0, -2.0 }; int[] ptrb = { 0, 2 }; int[] ptre = { 2, 4 }; double[] xx = new double[NUMVAR]; mosek.Env env = null; mosek.Task task = null; try { // Make mosek environment. env = new mosek.Env (); // Direct the env log stream to the user specified // method env_msg_obj.streamCB MsgClass env_msg_obj = new MsgClass (); env.set_streamCB (mosek.streamtype.log,env_msg_obj); // Initialize the environment. env.init (); // Create a task object linked with the environment env. task = new mosek.Task (env, NUMCON,NUMVAR); // Directs the log task stream to the user specified // method task_msg_obj.streamCB MsgClass task_msg_obj = new MsgClass (); task.set_streamCB (mosek.streamtype.log,task_msg_obj); /*TAG:begin-inputdata*/ task.inputdata(NUMCON,NUMVAR, c, 0.0, ptrb, ptre, asub, aval, bkc, blc, buc, bkx, blx, bux); /* Specify integer variables. */ for(int j=0; j<NUMVAR; ++j) task.putvartype(j,mosek.variabletype.type_int); task.putobjsense(mosek.objsense.maximize); try { task.optimize(); } catch (mosek.Warning w) { Console.WriteLine("Mosek warning:"); Console.WriteLine (w.Code); Console.WriteLine (w); } task.getsolutionslice(mosek.soltype.itg, /* Integer solution. */ mosek.solitem.xx, /* Which part of solution. */ 0, /* Index of first variable. */ NUMVAR, /* Index of last variable+1 */ xx); for(int j = 0; j < NUMVAR; ++j) Console.WriteLine ("x[{0}]:{1}", j,xx[j]); } catch (mosek.Exception e) { Console.WriteLine (e.Code); Console.WriteLine (e); } if (task != null) task.Dispose (); if (env != null) env.Dispose (); } }

5.5.1.2. Code comments

Please note that when mosek.Task.getsolutionslice is called, the integer solution is requested by using mosek.soltype.itg. No dual solution is defined for integer optimization problems.

5.5.2. Specifying an initial solution

Integer optimization problems are generally hard to solve, but the solution time can often be reduced by providing an initial solution for the solver. Solution values can be set using mosek.Task.putsolution (for inputting a whole solution) or mosek.Task.putsolutioni (for inputting solution values related to a single variable or constraint).

It is not necessary to specify the whole solution. By setting the mosek.iparam.mio_construct_sol parameter to mosek.onoffkey.on and inputting values for the integer variables only, will force MOSEK to compute the remaining continuous variable values.

If the specified integer solution is infeasible or incomplete, MOSEK will simply ignore it.

5.5.3. Example: Specifying an integer solution

Consider the problem

\begin{math}\nonumber{}\begin{array}{ll}\nonumber{}\mbox{maximize} & 7x_{0}+10x_{1}+x_{2}+5x_{3}\\\nonumber{}\mbox{subject to} & x_{0}+x_{1}+x_{2}+x_{3}\leq{}2.5\\\nonumber{} & x_{0},x_{1},x_{2}\mathrm{integer},\quad{}x_{0},x_{1},x_{2},x_{3}\geq{}0\end{array}\end{math} (5.5.2)

The following example demonstrates how to optimize the problem using a feasible starting solution generated by selecting the integer values as [[MathCmd 65]].

/* Copyright: Copyright (c) 1998-2007 MOSEK ApS, Denmark. All rights reserved. File: mioinitsol.c Purpose: Demonstrates how to solve a MIP with a start guess. Syntax: mioinitsol mioinitsol.lp */ using System; class msgclass : mosek.Stream { string prefix; public msgclass (string prfx) { prefix = prfx; } public override void streamCB (string msg) { Console.Write ("{0}{1}", prefix,msg); } } public class mioinitsol { public static void Main () { mosek.Env env = null; mosek.Task task = null; // Since the value infinity is never used, we define // 'infinity' symbolic purposes only double infinity = 0; int NUMVAR = 4; int NUMCON = 1; int NUMINTVAR = 3; double[] c = { 7.0, 10.0, 1.0, 5.0 }; mosek.boundkey[] bkc = {mosek.boundkey.up}; double[] blc = {-infinity}; double[] buc = {2.5}; mosek.boundkey[] bkx = {mosek.boundkey.lo, mosek.boundkey.lo, mosek.boundkey.lo, mosek.boundkey.lo}; double[] blx = {0.0, 0.0, 0.0, 0.0}; double[] bux = {infinity, infinity, infinity, infinity}; int[] ptrb = {0, 1, 2, 3}; int[] ptre = {1, 2, 3, 4}; double[] aval = {1.0, 1.0, 1.0, 1.0}; int[] asub = {0, 0, 0, 0 }; int[] intsub = {0, 1, 2}; double[] xx = new double[NUMVAR]; try { // Make mosek environment. env = new mosek.Env (); // Direct the env log stream to the user specified // method env_msg_obj.streamCB env.set_streamCB (mosek.streamtype.log, new msgclass ("[env]")); // Initialize the environment. env.init (); // Create a task object linked with the environment env. task = new mosek.Task (env, NUMCON,NUMVAR); // Directs the log task stream to the user specified // method task_msg_obj.streamCB task.set_streamCB (mosek.streamtype.log, new msgclass ("[task]")); task.inputdata(NUMCON,NUMVAR, c, 0.0, ptrb, ptre, asub, aval, bkc, blc, buc, bkx, blx, bux); for(int j=0 ; j<NUMINTVAR ; ++j) task.putvartype(intsub[j],mosek.variabletype.type_int); task.putobjsense(mosek.objsense.maximize); // Construct an initial feasible solution from the // values of the integer valuse specified task.putintparam(mosek.iparam.mio_construct_sol, mosek.onoffkey.on); // Set status of all variables to unknown task.makesolutionstatusunknown(mosek.soltype.itg); // Assign values 1,1,0 to integer variables task.putsolutioni ( mosek.accmode.var, 0, mosek.soltype.itg, mosek.stakey.supbas, 0.0, 0.0, 0.0, 0.0); task.putsolutioni ( mosek.accmode.var, 1, mosek.soltype.itg, mosek.stakey.supbas, 2.0, 0.0, 0.0, 0.0); task.putsolutioni ( mosek.accmode.var, 2, mosek.soltype.itg, mosek.stakey.supbas, 0.0, 0.0, 0.0, 0.0); try { task.optimize(); } catch (mosek.Warning w) { Console.WriteLine("Mosek warning:"); Console.WriteLine (w.Code); Console.WriteLine (w); } task.getsolutionslice(mosek.soltype.itg, /* Basic solution. */ mosek.solitem.xx, /* Which part of solution. */ 0, /* Index of first variable. */ NUMVAR, /* Index of last variable+1 */ xx); for(int j = 0; j < NUMVAR; ++j) Console.WriteLine ("x[{0}]:{1}", j,xx[j]); } catch (mosek.Exception e) { Console.WriteLine (e.Code); Console.WriteLine (e); } if (task != null) task.Dispose (); if (env != null) env.Dispose (); } }

5.6. Problem modification and reoptimization

Often one might want to solve not just a single optimization problem, but a sequence of problem, each differing only slightly from the previous one. This section demonstrates how to modify and reoptimize an existing problem. The example we study is a simple production planning model.

5.6.1. A production planning problem

A company manufactures three types of products. Suppose the stages of manufacturing can be split into three parts, namely Assembly, Polishing and Packing. In the table below we show the time required for each stage as well as the profit associated with each product.

Product no. Assembly (minutes) Polishing (minutes) Packing (minutes) Profit ($)
0 2 3 2 1.50
1 4 2 3 2.50
2 3 3 2 3.00

With the current resources available, the company has 100,000 minutes of assembly time, 50,000 minutes of polishing time and 60,000 minutes of packing time available per year.

Now the question is how many items of each product the company should produce each year in order to maximize profit?

Denoting the number of items of each type by [[MathCmd 66]] and [[MathCmd 67]], this problem can be formulated as the linear optimization problem:

\begin{math}\nonumber{}\begin{array}{lccccccl}\nonumber{}\mbox{maximize} & 1.5x_{0} & + & 2.5x_{1} & + & 3.0x_{2} &  & \\\nonumber{}\mbox{subject to} & 2x_{0} & + & 4x_{1} & + & 3x_{2} & \leq{} & 100000,\\\nonumber{} & 3x_{0} & + & 2x_{1} & + & 3x_{2} & \leq{} & 50000,\\\nonumber{} & 2x_{0} & + & 3x_{1} & + & 2x_{2} & \leq{} & 60000,\end{array}\end{math} (5.6.1)

and

\begin{math}\nonumber{}x_{0},x_{1},x_{2}\geq{}0.\end{math} (5.6.2)

The following code loads this problem into the optimization task.

// Since the value infinity is never used, we define // 'infinity' symbolic purposes only double infinity = 0; const int NUMCON = 3; const int NUMVAR = 3; const int NUMANZ = 9; double[] c = {1.5, 2.5, 3.0}; mosek.boundkey[] bkc = {mosek.boundkey.up, mosek.boundkey.up, mosek.boundkey.up}; double[] blc = {-infinity, -infinity, -infinity}; double[] buc = {100000, 50000, 60000}; mosek.boundkey[] bkx = {mosek.boundkey.lo, mosek.boundkey.lo, mosek.boundkey.lo}; double[] blx = {0.0, 0.0, 0.0}; double[] bux = {+infinity, +infinity, +infinity}; int[][] asub = new int[NUMVAR][]; asub[0] = new int[] {0, 1, 2}; asub[1] = new int[] {0, 1, 2}; asub[2] = new int[] {0, 1, 2}; double[][] aval = new double[NUMVAR][]; aval[0] = new double[] { 2.0, 3.0, 2.0 }; aval[1] = new double[] { 4.0, 2.0, 3.0 }; aval[2] = new double[] { 3.0, 3.0, 2.0 }; double[] xx = new double[NUMVAR]; mosek.Task task = null; mosek.Env env = null; try { // Create mosek environment. env = new mosek.Env (); // Initialize the environment. env.init (); // Create a task object linked with the environment env. task = new mosek.Task (env, NUMCON,NUMVAR); /* Give MOSEK an estimate on the size of the data to input. This is done to increase the speed of inputting data and is optional.*/ task.putmaxnumvar(NUMVAR); task.putmaxnumcon(NUMCON); task.putmaxnumanz(NUMANZ); /* Append the constraints. */ task.append(mosek.accmode.con,NUMCON); /* Append the variables. */ task.append(mosek.accmode.var,NUMVAR); /* Put C. */ task.putcfix(0.0); for(int j=0; j<NUMVAR; ++j) task.putcj(j,c[j]); /* Put constraint bounds. */ for(int i=0; i<NUMCON; ++i) task.putbound(mosek.accmode.con,i,bkc[i],blc[i],buc[i]); /* Put variable bounds. */ for(int j=0; j<NUMVAR; ++j) task.putbound(mosek.accmode.var,j,bkx[j],blx[j],bux[j]); /* Put A. */ if ( NUMCON>0 ) { for(int j=0; j<NUMVAR; ++j) task.putavec(mosek.accmode.var, j, asub[j], aval[j]); } task.putobjsense(mosek.objsense.maximize); try { task.optimize(); } catch (mosek.Warning w) { Console.WriteLine("Mosek warning:"); Console.WriteLine (w.Code); Console.WriteLine (w); } task.getsolutionslice(mosek.soltype.bas, /* Basic solution. */ mosek.solitem.xx, /* Which part of solution. */ 0, /* Index of first variable. */ NUMVAR, /* Index of last variable+1 */ xx); for(int j = 0; j < NUMVAR; ++j) Console.WriteLine ("x[{0}]:{1}", j,xx[j]);

5.6.2. Changing the A matrix

Suppose we want to change the time required for assembly of product 0 to 3 minutes. This corresponds to setting [[MathCmd 70]], which is done by calling the function mosek.Task.putaij as shown below.

task.putaij(0, 0, 3.0);

The problem now has the form:

\begin{math}\nonumber{}\begin{array}{lccccccl}\nonumber{}\mbox{maximize} & 1.5x_{0} & + & 2.5x_{1} & + & 3.0x_{2} &  & \\\nonumber{}\mbox{subject to} & 3x_{0} & + & 4x_{1} & + & 3x_{2} & \leq{} & 100000,\\\nonumber{} & 3x_{0} & + & 2x_{1} & + & 3x_{2} & \leq{} & 50000,\\\nonumber{} & 2x_{0} & + & 3x_{1} & + & 2x_{2} & \leq{} & 60000,\end{array}\end{math} (5.6.3)

and

\begin{math}\nonumber{}x_{0},x_{1},x_{2}\geq{}0.\end{math} (5.6.4)

After changing the A matrix we can find the new optimal solution by calling mosek.Task.optimize again.

5.6.3. Appending variables

We now want to add a new product with the following data:

Product no. Assembly (minutes) Polishing (minutes) Packing (minutes) Profit ($)
3 4 0 1 1.00

This corresponds to creating a new variable [[MathCmd 73]], appending a new column to the A matrix and setting a new value in the objective. We do this in the following code.

/* Append a new varaible x_3 to the problem */ task.append(mosek.accmode.var,1); /* Get index of new variable, this should be 3 */ int numvar; task.getnumvar(out numvar); /* Set bounds on new varaible */ task.putbound(mosek.accmode.var, numvar-1, mosek.boundkey.lo, 0, +infinity); /* Change objective */ task.putcj(numvar-1,1.0); /* Put new values in the A matrix */ int[] acolsub = new int[] {0, 2}; double[] acolval = new double[] {4.0, 1.0}; task.putavec(mosek.accmode.var, numvar-1, /* column index */ acolsub, acolval);

After this operation the problem looks this way:

\begin{math}\nonumber{}\begin{array}{lccccccccl}\nonumber{}\mbox{maximize} & 1.5x_{0} & + & 2.5x_{1} & + & 3.0x_{2} & + & 1.0x_{3} &  & \\\nonumber{}\mbox{subject to} & 3x_{0} & + & 4x_{1} & + & 3x_{2} & + & 4x_{3} & \leq{} & 100000,\\\nonumber{} & 3x_{0} & + & 2x_{1} & + & 3x_{2} &  &  & \leq{} & 50000,\\\nonumber{} & 2x_{0} & + & 3x_{1} & + & 2x_{2} & + & 1x_{3} & \leq{} & 60000,\end{array}\end{math} (5.6.5)

and

\begin{math}\nonumber{}x_{0},x_{1},x_{2},x_{3}\geq{}0.\end{math} (5.6.6)

5.6.4. Reoptimization

When mosek.Task.optimize is called MOSEK will store the optimal solution internally. After a task has been modified and mosek.Task.optimize is called again the solution will automatically be used to reduce solution time of the new problem, if possible.

In this case an optimal solution to problem (5.6.3) was found and then added a column was added to get (5.6.5). The simplex optimizer is well suited for exploiting an existing primal or dual feasible solution. Hence, the subsequent code instructs MOSEK to choose the simplex optimizer freely when optimizing.

/* Change optimizer to simplex free and reoptimize */ task.putintparam(mosek.iparam.optimizer,mosek.optimizertype.free_simplex); task.optimize();

5.6.5. Appending constraints

Now suppose we want to add a new stage to the production called “Quality control” for which 30000 minutes are available. The time requirement for this stage is shown below:

Product no. Quality control (minutes)
0 1
1 2
2 1
3 1

This corresponds to adding the constraint

\begin{math}\nonumber{}x_{0}+2x_{1}+x_{2}+x_{3}\leq{}30000\end{math} (5.6.7)

to the problem which is done in the following code:

/* Append a new constraint */ task.append(mosek.accmode.con,1); /* Get index of new constraint, this should be 4 */ int numcon; task.getnumcon(out numcon); /* Set bounds on new constraint */ task.putbound( mosek.accmode.con, numcon-1, mosek.boundkey.up, -infinity, 30000); /* Put new values in the A matrix */ int[] arowsub = new int[] {0, 1, 2, 3 }; double[] arowval = new double[] {1.0, 2.0, 1.0, 1.0}; task.putavec(mosek.accmode.con, numcon-1, /* row index */ arowsub, arowval);

5.7. Efficiency considerations

Although MOSEK is implemented to handle memory efficiently, the user may have valuable knowledge about a problem, which could be used to improve the performance of MOSEK. This section discusses some tricks and general advice that hopefully make MOSEK process your problem faster.

Avoid memory fragmentation:

MOSEK stores the optimization problem in internal data structures in the memory. Initially MOSEK will allocate structures of a certain size, and as more items are added to the problem the structures are reallocated. For large problems the same structures may be reallocated many times causing memory fragmentation. One way to avoid this is to give MOSEK an estimated size of your problem using the functions:

None of these functions change the problem, they only give hints to the eventual dimension of the problem. If the problem ends up growing larger than this, the estimates are automatically increased.

Tune the reallocation process:

It is possible to obtain information about how often MOSEK reallocates storage for the A matrix by inspecting mosek.iinfitem.sto_num_a_realloc. A large value indicates that maxnumanz has been reestimated many times and that the initial estimate should be increased.

Do not mix put- and get- functions:

For instance, the functions mosek.Task.putavec and mosek.Task.getavec. MOSEK will queue put- commands internally until a get- function is called. If every put- function call is followed by a get- function call, the queue will have to be flushed often, decreasing efficiency.

In general get- commands should not be called often during problem setup.

Use the LIFO principle when removing constraints and variables:

MOSEK can more efficiently remove constraints and variables with a high index than a small index.

An alternative to removing a constraint or a variable is to fix it at 0, and set all relevant coefficients to 0. Generally this will not have any impact on the optimization speed.

Add more constraints and variables than you need (now):

The cost of adding one constraint or one variable is about the same as adding many of them. Therefore, it may be worthwhile to add many variables instead of one. Initially fix the unused variable at zero, and then later unfix them as needed. Similarly, you can add multiple free constraints and then use them as needed.

Use one environment (env) only:

If possible share the environment (env) between several tasks. For most applications you need to create only a single env.

Do not remove basic variables:

When doing reoptimizations, instead of removing a basic variable it may be more efficient to fix the variable at zero and then remove it when the problem is reoptimized and it has left the basis. This makes it easier for MOSEK to restart the simplex optimizer.

5.7.1. API overhead

The .NET interface is a thin wrapper around a native MOSEK library. The layer between the .NET application and the native MOSEK library is made as thin as possible to minimize the overhead from function calls.

A call to a method in a MOSEK class will result in a call to a public .NET method, which in turn calls the native function, converting data and types as necessary. As data and processes in .NET are kept rigidly apart from the native code, converting data at least implies that a complete copy of the data is created, and calling of native functions (in this case) means calling into an unsafe (relative to the .NET environment) execution context. For larger problems this may mean, that fetching or inputting large chunks of data is less expensive than fetching/inputting the same data as single values.

5.8. Conventions employed in the API

5.8.1. Naming conventions for arguments

In the definition of the MOSEK .NET API a consistent naming convention has been used. This implies that whenever for example numcon is an argument in a function definition it indicates the number of constraints.

In Table 5.2 the variable names used to specify the problem parameters are listed.

.NET name .NET type Dimension Related problem
  parameter
numcon int m
numvar int n
numcone int t
numqonz int [[MathCmd 77]]
qosubi int[] numqonz [[MathCmd 77]]
qosubj int[] numqonz [[MathCmd 77]]
qoval out double numqonz [[MathCmd 77]]
c double[] numvar [[MathCmd 81]]
cfix double [[MathCmd 5]]
numqcnz int [[MathCmd 83]]
qcsubk int[] qcnz [[MathCmd 83]]
qcsubi int[] qcnz [[MathCmd 83]]
qcsubj int[] qcnz [[MathCmd 83]]
qcval out double qcnz [[MathCmd 83]]
aptrb int[] numvar [[MathCmd 88]]
aptre int[] numvar [[MathCmd 88]]
asub int[] aptre[numvar-1] [[MathCmd 88]]
aval double[] aptre[numvar-1] [[MathCmd 88]]
bkc boundkey[] numcon [[MathCmd 92]] and [[MathCmd 93]]
blc double[] numcon [[MathCmd 92]]
buc double[] numcon [[MathCmd 93]]
bkx boundkey[] numvar [[MathCmd 96]] and [[MathCmd 97]]
blx double[] numvar [[MathCmd 96]]
bux double[] numvar [[MathCmd 97]]
Table 5.2: Naming convention used in MOSEK

The relation between the variable names and the problem parameters is as follows:

  • The quadratic terms in the objective:

    \begin{math}\nonumber{}q_{{\mathtt{qosubi[t]},\mathtt{qosubj[t]}}}^{o}=\mathtt{qoval[t]},~t=0,\ldots ,\mathtt{numqonz}-1.\end{math} (5.8.1)
  • The linear terms in the objective:

    \begin{math}\nonumber{}c_{j}=\mathtt{c[j]},~j=0,\ldots ,\mathtt{numvar}-1\end{math} (5.8.2)
  • The fixed term in the objective:

    \begin{math}\nonumber{}c^{f}=\mathtt{cfix}.\end{math} (5.8.3)
  • The quadratic terms in the constraints:

    \begin{math}\nonumber{}q_{{\mathtt{qcsubi[t]},\mathtt{qcsubj[t]}}}^{\mathtt{qcsubk[t]}}=\mathtt{qcval[t]},~t=0,\ldots ,\mathtt{numqcnz}-1.\end{math} (5.8.4)
  • The linear terms in the constraints:

    \begin{math}\nonumber{}\begin{array}{rl}\nonumber{}a_{{\mathtt{asub[t],j}}}=\mathtt{aval[t]}, & t=\mathtt{ptrb[j]},\ldots ,\mathtt{ptre[j]}-1,\\\nonumber{} & j=0,\ldots ,\mathtt{numvar}-1.\end{array}\end{math} (5.8.5)
  • The bounds on the constraints are specified using the variables bkc, blc, and buc. The components of the integer array bkc specify the bound type according to Table 5.3.

    Symbolic constant Lower bound Upper bound
    mosek.boundkey.fx finite identical to the lower bound
    mosek.boundkey.fr minus infinity plus infinity
    mosek.boundkey.lo finite plus infinity
    mosek.boundkey.ra finite finite
    mosek.boundkey.up minus infinity finite
    Table 5.3: Interpretation of the bound keys.

    For instance bkc[2]=mosek.boundkey.lo means that [[MathCmd 18]] and [[MathCmd 19]]. Finally, the numerical values of the bounds are given by

    \begin{math}\nonumber{}l_{k}^{c}=\mathtt{blc[k]},~k=0,\ldots ,\mathtt{numcon}-1\end{math} (5.8.6)

    and

    \begin{math}\nonumber{}u_{k}^{c}=\mathtt{buc[k]},~k=0,\ldots ,\mathtt{numcon}-1.\end{math} (5.8.7)
  • The bounds on the variables are specified using the variables bkx, blx, and bux. The components in the integer array bkx specify the bound type according to Table 5.3. The numerical values for the lower bounds on the variables are given by

    \begin{math}\nonumber{}l_{j}^{x}=\mathtt{blx[j]},~j=0,\ldots ,\mathtt{numvar}-1.\end{math} (5.8.8)

    The numerical values for the upper bounds on the variables are given by

    \begin{math}\nonumber{}u_{j}^{x}=\mathtt{bux[j]},~j=0,\ldots ,\mathtt{numvar}-1.\end{math} (5.8.9)

5.8.1.1. Bounds

A bound on a variable or on a constraint in MOSEK consists of a bound key, as defined in Table 5.3, a lower bound value and an upper bound value. Even if a variable or constraint is bounded only from below, e.g. x0, both bounds are inputted or extracted; the value inputted as upper bound for (x0) is ignored.

5.8.2. Vector formats

Three different vector formats are used in the MOSEK API:

Full vector:

This is simply an array where the first element corresponds to the first item, the second element to the second item etc. For example to get the linear coefficients of the objective in task, one would write

double[] c = new double[numvar]; task.getc(c);

where numvar is the number of variables in the problem.

Vector slice:

A vector slice is a range of values. For example, to get the bounds associated constraint 3 through 10 (both inclusive) one would write

double[] upper_bound = new double[8]; double[] lower_bound = new double[8]; mosek.boundkey[] bound_key = new mosek.boundkey[8]; task.getboundslice(mosek.accmode.con, 2,10, bound_key,lower_bound,upper_bound);

Please note that items in MOSEK are numbered from 0, so that the index of the first item is 0, and the index of the n'th item is n-1.

Sparse vector:

A sparse vector is given as an array of indexes and an array of values. For example, to input a set of bounds associated with constraints number 1, 6, 3, and 9, one might write

int[] bound_index = { 1, 6, 3, 9 }; mosek.boundkey[] bound_key = { mosek.boundkey.fr, mosek.boundkey.lo, mosek.boundkey.up, mosek.boundkey.fx }; double[] upper_bound = { 0.0, -10.0, 0.0, 5.0 }; double[] lower_bound = { 0.0, 0.0, 6.0, 5.0 }; task.putboundlist(mosek.accmode.con, bound_index, bound_key,lower_bound,upper_bound);

Note that the list of indexes need not be ordered.

5.8.3. Matrix formats

The coefficient matrices in a problem are inputted and extracted in a sparse format, either as complete or a partial matrices. Basically there are two different formats for this.

5.8.3.1. Unordered triplets

In unordered triplet format each entry is defined as a row index, a column index and a coefficient. For example, to input the A matrix coefficients for [[MathCmd 111]], [[MathCmd 112]], and [[MathCmd 113]], one would write as follows:

int[] subi = { 1, 3, 5 }; int[] subj = { 2, 3, 4 }; double[] cof = { 1.1, 4.3, 0.2 }; task.putaijlist(subi,subj,cof);

Please note that in some cases (like mosek.Task.putaijlist) only the specified indexes remain modified — all other are unchanged. In other cases (such as mosek.Task.putqconk) the triplet format is used to modify all entries — entries that are not specified are set to 0.

5.8.3.2. Row or column ordered sparse matrix

In a sparse matrix format only the non-zero entries of the matrix are stored. MOSEK uses a sparse matrix format ordered either by rows or columns. In the column-wise format the position of the non-zeros are given as a list of row indexes. In the row-wise format the position of the non-zeros are given as a list of column indexes. Values of the non-zero entries are given in column or row order.

A sparse matrix in column ordered format consists of:

asub:

List of row indexes.

aval:

List of non-zero entries of A ordered by columns.

ptrb:

Where ptrb[j] is the position of the first value/index in aval / asub for column j.

ptre:

Where ptre[j] is the position of the last value/index plus one in aval / asub for column j.

The values of a matrix A with numcol columns are assigned so that for

\begin{displaymath}\nonumber{}j=0,\ldots ,\mathtt{numcol}-1.\end{displaymath}

We define

\begin{math}\nonumber{}\begin{array}{rcl}\nonumber{}a_{{\mathtt{asub}[k],j}}=\mathtt{aval}[k],\quad{}k=\mathtt{ptrb}[j],\ldots ,\mathtt{ptre}[j]-1.\end{array}\end{math} (5.8.10)

Figure 5.1: The matrix A (5.8.11) represented in column ordered sparse matrix format.

As an example consider the matrix

\begin{math}\nonumber{}A=\left[\begin{array}{ccccc}\nonumber{}1.1 &  & 1.3 & 1.4 & \\\nonumber{} & 2.2 &  &  & 2.5\\\nonumber{}3.1 &  &  & 3.4 & \\\nonumber{} &  & 4.4 &  &\end{array}\right].\end{math} (5.8.11)

which can be represented in the column ordered sparse matrix format as

\begin{displaymath}\nonumber{}\begin{array}{lcl}\nonumber{}\mathtt{ptrb} & = & [0,2,3,5,7],\\\nonumber{}\mathtt{ptre} & = & [2,3,5,7,8],\\\nonumber{}\mathtt{asub} & = & [0,2,1,0,3,0,2,1],\\\nonumber{}\mathtt{aval} & = & [1.1,3.1,2.2,1.3,4.4,1.4,3.4,2.5].\end{array}\end{displaymath}

Fig. 5.1 illustrates how the matrix A (5.8.11) is represented in column ordered sparse matrix format.

5.8.3.3. Row ordered sparse matrix

The matrix A (5.8.11) can also be represented in the row ordered sparse matrix format as:

\begin{displaymath}\nonumber{}\begin{array}{lcl}\nonumber{}\mathtt{ptrb} & = & [0,3,5,7],\\\nonumber{}\mathtt{ptre} & = & [3,5,7,8],\\\nonumber{}\mathtt{asub} & = & [0,2,3,1,4,0,3,2],\\\nonumber{}\mathtt{aval} & = & [1.1,1.3,1.4,2.2,2.5,3.1,3.4,4.4].\end{array}\end{displaymath}
Mon Sep 14 15:45:59 2009