6.9 Library of basic functions

This section contains a library of small models of basic functions frequently appearing in optimization models. It is essentially an implementation of the mathematical models from the MOSEK Modeling Cookbook using Optimizer API for .NET. These short code snippets can be seen as illustrative examples, can be copy-pasted to other code, and can even be directly called when assembling optimization models as we show in Sec. 6.9.6 (Model assembly example) (although this may be more suitable for prototyping; also note that additional variables and constraints will be introduced and there is no error checking).

6.9.1 Variable and constraint management

6.9.1.1 Append variables

Adds a number of new variables. Returns the index of the first variable in the sequence.

Listing 6.15 New variables. Click here to download.
    public static int msk_newvar(Task task, int num) {  // free
      int v = task.getnumvar();
      task.appendvars(num);
      for(int i=0; i<num; i++)
        task.putvarbound(v+i, boundkey.fr, -inf, inf);
      return v;
    }
    public static int msk_newvar_fx(Task task, int num, double val) {  // fixed
      int v = task.getnumvar();
      task.appendvars(num);
      for(int i=0; i<num; i++)
        task.putvarbound(v+i, boundkey.fx, val, val);
      return v;
    }
    public static int msk_newvar_bin(Task task, int num) {  // binary
      int v = task.getnumvar();
      task.appendvars(num);
      for(int i=0; i<num; i++) {
        task.putvarbound(v+i, boundkey.ra, 0.0, 1.0);
        task.putvartype(v+i, variabletype.type_int);
      }
      return v;
    }

6.9.1.2 Variable duplication

Declares equality of two variables, or returns an index of a new duplicate of an existing variable.

Listing 6.16 Duplicate variables. Click here to download.
    // x = y
    public static void msk_equal(Task task, int x, int y) {
      int c = msk_newcon(task, 1);
      task.putaij(c, x, 1.0);
      task.putaij(c, y, -1.0);
      task.putconbound(c, boundkey.fx, 0.0, 0.0);
    }
    public static int msk_dup(Task task, int x) {
      int y = msk_newvar(task, 1);
      msk_equal(task, x, y);
      return y;
    }

6.9.1.3 Append constraints

Adds a number of new constraints. Returns the index of the first constraint in the sequence.

Listing 6.17 New constraints. Click here to download.
    public static int msk_newcon(Task task, int num) {
      int c = task.getnumcon();
      task.appendcons(num);
      return c;
    }

6.9.2 Linear operations

6.9.2.1 Absolute value

\(t\geq |x|\)

Listing 6.18 Absolute value. Click here to download.
    // t >= |x|
    public static void msk_abs(Task task, int t, int x) {
      int c = msk_newcon(task, 2);
      task.putaij(c, t, 1.0);
      task.putaij(c, x, 1.0);
      task.putconbound(c, boundkey.lo, 0.0, inf);
      task.putaij(c+1, t, 1.0);
      task.putaij(c+1, x, -1.0);
      task.putconbound(c+1, boundkey.lo, 0.0, inf);
    }

6.9.2.2 1-norm

\(t\geq \sum_i |x_i|\)

Listing 6.19 \(1\)-norm. Click here to download.
    // t >= sum( |x_i| ), x is a list of variables
    public static void msk_norm1(Task task, int t, int[] x) {
      int n = x.Length;
      int u = msk_newvar(task, n);
      for(int i=0; i<n; i++) msk_abs(task, u+i, x[i]);
      int c = msk_newcon(task, 1);
      for(int i=0; i<n; i++) task.putaij(c, u+i, -1.0);
      task.putaij(c, t, 1.0);
      task.putconbound(c, boundkey.lo, 0.0, inf);
    }

6.9.3 Quadratic and power operations

6.9.3.1 Square

\(t\geq x^2\)

Listing 6.20 Square. Click here to download.
    // t >= x^2
    public static void msk_sq(Task task, int t, int x) {
      task.appendcone(conetype.rquad, 0.0, new int[]{msk_newvar_fx(task, 1, 0.5), t, x});
    }

6.9.3.2 2-norm

\(t\geq \sqrt{\sum_i x_i^2}\)

Listing 6.21 \(2\)-norm. Click here to download.
    // t >= sqrt(x_1^2 + ... + x_n^2) where x is a list of variables
    public static void msk_norm2(Task task, int t, int[] x) {
      int[] submem = new int[x.Length+1];
      submem[0] = t;
      for(int i=0; i<x.Length; i++) submem[i+1] = x[i];
      task.appendcone(conetype.quad, 0.0, submem);
    }

6.9.3.3 Powers

\(t\geq |x|^p\), \(p>1\)

Listing 6.22 Power. Click here to download.
    // t >= |x|^p (where p>1)
    public static void msk_pow(Task task, int t, int x, double p) {
      task.appendcone(conetype.ppow, 1.0/p, new int[]{t, msk_newvar_fx(task, 1, 1.0), x});
    }

\(t\geq 1/x^p,\ x>0\), \(p>0\)

Listing 6.23 Power reciprocal. Click here to download.
    // t >= 1/x^p, x>0 (where p>0)
    public static void msk_pow_inv(Task task, int t, int x, double p) {
      task.appendcone(conetype.ppow, 1.0/(1.0+p), new int[]{t, x, msk_newvar_fx(task, 1, 1.0)});
    }

6.9.3.4 p-norm

\(t\geq (\sum_i |x_i|^p)^{1/p}\), \(p>1\)

Listing 6.24 \(p\)-norm. Click here to download.
    // t >= \|x\|_p (where p>1), x is a list of variables
    public static void msk_pnorm(Task task, int t, int[] x, double p) {
      int n = x.Length;
      int r = msk_newvar(task, n);
      for(int i=0; i<n; i++)
        task.appendcone(conetype.ppow, 1.0-1.0/p, new int[]{t, r+i, x[i]});
      int c = msk_newcon(task, 1);
      for(int i=0; i<n; i++)
        task.putaij(c, r+i, -1.0);
      task.putaij(c, t, 1.0);
      task.putconbound(c, boundkey.fx, 0.0, 0.0);
    }

6.9.3.5 Geometric mean

\(t\leq (x_1\cdot\cdots\cdot x_n)^{1/n}\), \(x_i>0\)

Listing 6.25 Geometric mean. Click here to download.
    // |t| <= (x_1...x_n)^(1/n), x_i>=0, x is a list of variables of Length >= 1
    public static void msk_geo_mean(Task task, int t, int n, int[] x) {
      if (n==1) msk_abs(task, x[0], t);
      else {
        int t2 = msk_newvar(task, 1);
        task.appendcone(conetype.ppow, 1.0-1.0/n, new int[]{t2, x[n-1], t});
        msk_geo_mean(task, msk_dup(task, t2), n-1, x);
      }
    }

6.9.4 Exponentials and logarithms

6.9.4.1 log

\(t\leq \log{x},\ x>0\)

Listing 6.26 Logarithm. Click here to download.
    // t <= log(x), x>=0
    public static void msk_log(Task task, int t, int x) {
      task.appendcone(conetype.pexp, 0.0, new int[]{x, msk_newvar_fx(task, 1, 1.0), t});
    }

6.9.4.2 exp

\(t\geq e^{x}\)

Listing 6.27 Exponential. Click here to download.
    // t >= exp(x)
    public static void msk_exp(Task task, int t, int x) {
      task.appendcone(conetype.pexp, 0.0, new int[]{t, msk_newvar_fx(task, 1, 1.0), x});
    }

6.9.4.3 Entropy

\(t\geq x\log{x},\ x>0\)

Listing 6.28 Entropy. Click here to download.
    // t >= x * log(x), x>=0
    public static void msk_ent(Task task, int t, int x) {
      int v = msk_newvar(task, 1);
      int c = msk_newcon(task, 1);
      task.putaij(c, v, 1.0);
      task.putaij(c, t, 1.0);
      task.putconbound(c, boundkey.fx, 0.0, 0.0);
      task.appendcone(conetype.pexp, 0.0, new int[]{msk_newvar_fx(task, 1, 1.0), x, v});
    }

6.9.4.4 Relative entropy

\(t\geq x\log{x/y},\ x,y>0\)

Listing 6.29 Relative entropy. Click here to download.
    // t >= x * log(x/y), x,y>=0
    public static void msk_relent(Task task, int t, int x, int y) {
      int v = msk_newvar(task, 1);
      int c = msk_newcon(task, 1);
      task.putaij(c, v, 1.0);
      task.putaij(c, t, 1.0);
      task.putconbound(c, boundkey.fx, 0.0, 0.0);
      task.appendcone(conetype.pexp, 0.0, new int[]{y, x, v});
    }

6.9.4.5 Log-sum-exp

\(\log{\sum_i e^{x_i}}\leq t\)

Listing 6.30 Log-sum-exp. Click here to download.
    // log( sum_i(exp(x_i)) ) <= t, where x is a list of variables
    public static void msk_logsumexp(Task task, int t, int[] x) {
      int n = x.Length;
      int u = msk_newvar(task, n);
      int z = msk_newvar(task, n);
      for(int i=0; i<n; i++) msk_exp(task, u+i, z+i);
      int c = msk_newcon(task, n);
      for(int i=0; i<n; i++) {
        task.putarow(c+i, new int[]{x[i], t, z+i}, new double[]{1.0, -1.0, -1.0});
        task.putconbound(c+i, boundkey.fx, 0.0, 0.0);
      }
      int s = msk_newcon(task, 1);
      for(int i=0; i<n; i++) task.putaij(s, u+i, 1.0);
      task.putconbound(s, boundkey.up, -inf, 1.0);
    }

6.9.5 Integer Modeling

6.9.5.1 Semicontinuous variable

\(x\in\{0\}\cup[a,b]\), \(b>a>0\)

Listing 6.31 Semicontinuous variable. Click here to download.
    // x = 0 or a <= x <= b
    public static void msk_semicontinuous(Task task, int x, double a, double b) {
      int u = msk_newvar_bin(task, 1);
      int c = msk_newcon(task, 2);
      task.putarow(c, new int[]{x, u}, new double[]{1.0, -a});
      task.putconbound(c, boundkey.lo, 0.0, inf);
      task.putarow(c+1, new int[]{x, u}, new double[]{1.0, -b});
      task.putconbound(c+1, boundkey.up, -inf, 0.0);
    }

6.9.5.2 Indicator variable

\(x\neq 0 \implies t=1\). We assume \(x\) is a priori normalized so \(|x_i|\leq 1\).

Listing 6.32 Indicator variable. Click here to download.
    // x!=0 implies t=1. Assumes that |x|<=1 in advance.
    public static int msk_indicator(Task task, int x) {
      int t = msk_newvar_bin(task, 1);
      msk_abs(task, t, x);
      return t;
    }

6.9.5.3 Logical OR

At least one of the conditions is true.

Listing 6.33 Logical OR. Click here to download.
    // x OR y, where x, y are binary
    public static void msk_logic_or(Task task, int x, int y) {
      int c = msk_newcon(task, 1);
      task.putarow(c, new int[]{x, y}, new double[]{1.0, 1.0});
      task.putconbound(c, boundkey.lo, 1.0, inf);
    }
    // x_1 OR ... OR x_n, where x is sequence of variables
    public static void msk_logic_or_vect(Task task, int[] x) {
      int c = msk_newcon(task, 1);
      for(int i=0; i<x.Length; i++) task.putaij(c, x[i], 1.0);
      task.putconbound(c, boundkey.lo, 1.0, inf);
    }

6.9.5.4 Logical NAND

At most one of the conditions is true (also known as SOS1).

Listing 6.34 Logical NAND. Click here to download.
    // at most one of x_1,...,x_n, where x is a binary vector (SOS1 constraint)
    public static void msk_logic_sos1(Task task, int[] x) {
      int c = msk_newcon(task, 1);
      for(int i=0; i<x.Length; i++) task.putaij(c, x[i], 1.0);
      task.putconbound(c, boundkey.up, -inf, 1.0);
    }
    // NOT(x AND y), where x, y are binary
    public static void msk_logic_nand(Task task, int x, int y) {
      int c = msk_newcon(task, 1);
      task.putarow(c, new int[]{x, y}, new double[]{1.0, 1.0});
      task.putconbound(c, mosek.boundkey.up, -inf, 1.0);
    }

6.9.5.5 Cardinality bound

At most \(k\) of the continuous variables are nonzero. We assume \(x\) is a priori normalized so \(|x_i|\leq 1\).

Listing 6.35 Cardinality bound. Click here to download.
    // At most k of entries in x are nonzero, assuming in advance that |x_i|<=1.
    public static void msk_card(Task task, int[] x, int k) {
      int n = x.Length;
      int t = msk_newvar_bin(task, n);
      for(int i=0; i<n; i++) msk_abs(task, t+i, x[i]);
      int c = msk_newcon(task, 1);
      for(int i=0; i<n; i++) task.putaij(c, t+i, 1.0);
      task.putconbound(c,boundkey.up, -inf, k);
    }

6.9.6 Model assembly example

We now demonstrate how to quickly build a simple optimization model for the problem

(6.21)\[\begin{split}\begin{array}{rl} \maximize & -\sqrt{x^2 + y^2} + \log{y} - x^{1.5}, \\ \st & x\geq y+3, \end{array}\end{split}\]

or equivalently

\[\begin{split}\begin{array}{rl} \maximize & -t_0+t_1-t_2, \\ \st & x\geq y+3, \\ & t_0\geq\sqrt{x^2+y^2}, \\ & t_1\leq\log{y}, \\ & t_2\geq x^{1.5}. \end{array}\end{split}\]
Listing 6.36 Modeling (6.21). Click here to download.
    public static void testExample() {
      Env env = new Env();
      Task task = new Task(env);
      int x = msk_newvar(task, 1);
      int y = msk_newvar(task, 1);
      int t = msk_newvar(task, 3);

      int c = msk_newcon(task, 1);
      task.putarow(c, new int[]{x, y}, new double[]{1.0, -1.0});
      task.putconbound(c, boundkey.lo, 3.0, inf);

      msk_norm2(task, t+0, new int[]{x,y});
      msk_log  (task, t+1, msk_dup(task, y));
      msk_pow  (task, t+2, msk_dup(task, x), 1.5);

      task.putclist(new int[]{t, t+1, t+2}, new double[]{-1.0, 1.0, -1.0});
      task.putobjsense(objsense.maximize);