5.4.3. SDP Modeling and Optimization in C++

In this chapter, we will use MindOpt C++ API to model and solve the problem in Examples of semidefinite programming.

5.4.3.1. SDP Example I

Include the header file:

25#include "MindoptCpp.h"

Step I: Create an optimization model

Create an empty optimization model:

46    MDOEnv env = MDOEnv();
47    MDOModel m = MDOModel(env);

Step II: SDP model input

We use MDOModel::addPsdVar() to create a new semidefinite matrix variable representing \(\mathbf{X}\) in our example and simultaneously set its corresponding coefficient matrix \(\mathbf{C}\) in the objective function.

  • The first argument is the coefficient matrix, which is an instance of the MDOMatrix created using the MDOMatrix::coo() method. Note that the dimensions of the coefficient matrix should match the matrix variables.

  • The second argument is the name of the variable.

54        /* Add variables. */
55        MDOPsdVar psd_var = m.addPsdVar(MDOMatrix::coo(dim_mat, dim_mat, C_nnz, C_nz_indices, C_nz_values), "X0");

Next, we input the first constraint. We use MDOModel::addPsdConstr() to establish a constraint with a semidefinite matrix variable.

  • The first argument is the semidefinite expression in the constraint, i.e., \(\langle \mathbf{A},\mathbf{X} \rangle\). We create it using the initialization method MDOPsdExpr::MDOPsdExpr().

  • The second argument is the type of the constraint, and currently, we only support MDO_EQUAL (‘=’), which represents an equality constraint.

  • The third argument is the right-hand side value of the constraint (in this case, 1.0). The last argument is the name of the constraint.

57        /* Add constraints. */
58        m.addPsdConstr(MDOPsdExpr(psd_var, MDOMatrix::coo(dim_mat, dim_mat, A_nnz, A_nz_indices, A_nz_values)), MDO_EQUAL, 1.0, "C0");

Finally, we use MDOModel::setObjective() to set the linear part of the objective function (in this case, 0), and change the optimization direction to maximization (-1).

60        /* Set objective function. */
61        MDOLinExpr objective = 0;
62        m.setObjective(objective, -1);

Step III: Solve SDP model

Solve the optimization problem via MDOModel::optimize().

67        // Optimize the model
68        m.optimize();

Step IV: Obtain the solution of SDP problem

We use the generic function MDOModel::get() to retrieve the optimal objective function value, i.e., the PrimalObjVal attribute.

69        if (m.get(MDO_IntAttr_Status) == MDO_OPTIMAL)
70        {
71            cout << "Primal objective val: " << m.get(MDO_DoubleAttr_PrimalObjVal) << endl;
72        }
73        else
74        {
75            cout << "No feasible solution." << endl;
76        }

The complete example code is provided in MdoSdoEx1.cpp :

 1/**
 2 *  Description
 3 *  -----------
 4 *
 5 *  Semidefinite optimization (row-wise input).
 6 *
 7 *  Formulation
 8 *  -----------
 9 *
10 *  Maximize 
11 *  obj: tr(C X) 
12 * 
13 *  Subject To
14 *    c0 : tr(A X) = 1
15 *         X is p.s.d.
16 *
17 *  Matrix
18 *    C = [ -3  0  1 ]  A = [ 3 0 1 ]
19 *        [  0 -2  0 ]      [ 0 4 0 ]
20 *        [  1  0 -3 ]      [ 1 0 5 ]
21 *  End
22 */
23
24#include <iostream>
25#include "MindoptCpp.h"
26
27using namespace std;
28
29int main(void)
30{
31    /* Model data. */
32    int    num_mats = 1;
33    int    dim_mat = 3;                                 /* Dimension of the matrix variables. */
34
35    int    C_nnz = 4;
36    int    C_nz_indices[] =  {  0,   2,    4,    8    }; /* Nonzero vectorized index of obj coeff. */
37    double C_nz_values[]  =  { -3.0, 1.0,  -2.0, -3.0 }; /* Nonzero values of obj coeff. */
38
39    int    A_nnz = 4;
40    int    A_nz_indices[] =  { 0,    2,    4,    8    }; /* Nonzero vectorized index of constr coeff. */
41    double A_nz_values[]  =  { 3.0,  1.0,  4.0,  5.0  }; /* Nonzero values of constr coeff. */
42
43    /*------------------------------------------------------------------*/
44    /* Step 1. Create environment and model.                            */
45    /*------------------------------------------------------------------*/
46    MDOEnv env = MDOEnv();
47    MDOModel m = MDOModel(env);
48
49    try 
50    {
51        /*------------------------------------------------------------------*/
52        /* Step 2. Input model.                                             */
53        /*------------------------------------------------------------------*/        
54        /* Add variables. */
55        MDOPsdVar psd_var = m.addPsdVar(MDOMatrix::coo(dim_mat, dim_mat, C_nnz, C_nz_indices, C_nz_values), "X0");
56        
57        /* Add constraints. */
58        m.addPsdConstr(MDOPsdExpr(psd_var, MDOMatrix::coo(dim_mat, dim_mat, A_nnz, A_nz_indices, A_nz_values)), MDO_EQUAL, 1.0, "C0");
59        
60        /* Set objective function. */
61        MDOLinExpr objective = 0;
62        m.setObjective(objective, -1);
63        
64        /*------------------------------------------------------------------*/
65        /* Step 3. Solve the problem and populate optimization result.      */
66        /*------------------------------------------------------------------*/
67        // Optimize the model
68        m.optimize();
69        if (m.get(MDO_IntAttr_Status) == MDO_OPTIMAL)
70        {
71            cout << "Primal objective val: " << m.get(MDO_DoubleAttr_PrimalObjVal) << endl;
72        }
73        else
74        {
75            cout << "No feasible solution." << endl;
76        }
77    } 
78    catch (MDOException &e) 
79    {
80        cout << "Error code = " << e.getErrorCode() << endl;
81        cout << e.getMessage() << endl;
82    } 
83    catch (...) 
84    {
85        cout << "Error during optimization." << endl;
86    }
87
88    return static_cast<int>(MDO_OKAY);
89}

5.4.3.2. SDP Example II

Include the header file:

32#include "MindoptCpp.h"

Step I: Create an optimization model

Create an empty optimization model:

64    MDOEnv env = MDOEnv();
65    MDOModel m = MDOModel(env);

Step II: SDP model input

We use MDOModel::addPsdVar() to create two new semidefinite matrix variables (in this case, \(\mathbf{X}_0, \mathbf{X}_1\)) and simultaneously set their corresponding coefficient matrices (in this case, \(\mathbf{C}_0, \mathbf{C}_1\)) in the objective function.

73        MDOPsdVar psd_var0 = m.addPsdVar(MDOMatrix::coo(dim_mat[0], dim_mat[0], C0_nnz, C0_nz_indices, C0_nz_values), "X0");
74        MDOPsdVar psd_var1 = m.addPsdVar(MDOMatrix::coo(dim_mat[1], dim_mat[1], C1_nnz, C1_nz_indices, C1_nz_values), "X1");

We then use MDOModel::addVar() to create two linear variables representing \(x_0,x_1\) in this example.

  • The first and second arguments are the lower and upper bounds of the variables.

  • The third argument is the coefficient of the variable in the objective function.

  • The fourth argument is the type of the variable, which is continuous variable type (‘C’) in this case. The last argument is the name of the variable.

75        MDOVar    var0     = m.addVar(0.0, MDO_INFINITY, 0.0, 'C', "x0");
76        MDOVar    var1     = m.addVar(0.0, MDO_INFINITY, 0.0, 'C', "x1");        

Next, we create constraints. We use MDOModel::addPsdConstr() to establish constraints with semidefinite matrix variables.

  • The first argument is the semidefinite expression in the constraint. Since the constraint in this example contains both semidefinite variables and linear variables, we construct the semidefinite expression in two steps.

    • Firstly, we create the semidefinite term using the initialization method MDOPsdExpr::MDOPsdExpr().

    • Then, we add the remaining linear term using the MDOPsdExpr::addTerms() method to obtain the final semidefinite expression for the constraint.

  • The second argument is the type of the constraint, and currently, we only support MDO_EQUAL (‘=’), which represents an equality constraint.

  • The third argument is the right-hand side value of the constraint (in this case, 1.0).

  • The last argument is the name of the constraint.

78        /* Add constraints. */
79        MDOPsdExpr psd_expr0 = MDOPsdExpr(psd_var0, MDOMatrix::coo(dim_mat[0], dim_mat[0], A0_nnz, A0_nz_indices, A0_nz_values));
80        const MDOVar row0_vars[] = { var0 };
81        psd_expr0.addTerms(row0_values, row0_vars, 1);
82        m.addPsdConstr(psd_expr0, MDO_EQUAL, 1.0, "C0");
83
84        MDOPsdExpr psd_expr1 = MDOPsdExpr(psd_var1, MDOMatrix::coo(dim_mat[0], dim_mat[0], A1_nnz, A1_nz_indices, A1_nz_values));
85        const MDOVar row1_vars[] = { var1 };
86        psd_expr1.addTerms(row1_values, row1_vars, 1);        
87        m.addPsdConstr(psd_expr1, MDO_EQUAL, 2.0, "C1");

Finally, we use MDOModel::setObjective() to set the linear part of the objective function (in this case, 0), and change the optimization direction to maximization (-1).

89        /* Set objective function. */
90        MDOLinExpr objective = 0;
91        m.setObjective(objective, -1);

Step III: Solve SDP model

Solve the optimization problem via MDOModel::optimize().

96        // Optimize the model
97        m.optimize();

Step IV: Obtain the solution of SDP problem

We use the generic function MDOModel::get() to retrieve the optimal objective function value, i.e., the PrimalObjVal attribute.

 98        if (m.get(MDO_IntAttr_Status) == MDO_OPTIMAL)
 99        {
100            cout << "Primal objective val: " << m.get(MDO_DoubleAttr_PrimalObjVal) << endl;
101        }
102        else
103        {
104            cout << "No feasible solution." << endl;
105        }

The complete example code is provided in MdoSdoEx2.cpp :

  1/**
  2 *  Description
  3 *  -----------
  4 *
  5 *  Semidefinite optimization (row-wise input).
  6 *
  7 *  Formulation
  8 *  -----------
  9 *
 10 *  Maximize
 11 *  obj: tr(C0 X0)   + tr(C0 X1)    + 0 x0 + 0 x1
 12 *
 13 *  Subject To
 14 *   c0 : tr(A00 X0)                + 1 x0        = 1
 15 *   c1 :              tr(A00 X1)          + 1 x1 = 2
 16 *  Bounds
 17 *    0 <= x0
 18 *    0 <= x1
 19 *    X1, X2 are p.s.d.
 20 *
 21 *  Matrix
 22 *    C0 =  [ 2 1 ]   A00 = [ 3 1 ]
 23 *          [ 1 2 ]         [ 1 3 ]
 24 *
 25 *    C0 = [ 3 0 1 ]  A00 = [ 3 0 1 ]
 26 *         [ 0 2 0 ]        [ 0 4 0 ]
 27 *         [ 1 0 3 ]        [ 1 0 5 ]
 28 *  End
 29 */
 30
 31#include <iostream>
 32#include "MindoptCpp.h"
 33
 34using namespace std;
 35
 36int main(void)
 37{
 38    /* Model data. */
 39    int    num_mats = 1;
 40    int    dim_mat[] = { 2, 3 };                          /* Dimension of the matrix variables. */
 41
 42    int    C0_nnz = 3;
 43    int    C0_nz_indices[] =  {  0,   1,   3   };         /* Nonzero vectorized index of obj coeff. */
 44    double C0_nz_values[]  =  {  2.0, 1.0, 2.0 };         /* Nonzero values of obj coeff. */
 45    
 46    int    C1_nnz = 4;
 47    int    C1_nz_indices[] =  {  0,   2,   4,    8   };   /* Nonzero vectorized index of obj coeff. */
 48    double C1_nz_values[]  =  {  3.0, 1.0, 2.0,  3.0 };   /* Nonzero values of obj coeff. */
 49
 50    int    A0_nnz = 3;
 51    int    A0_nz_indices[] =  {  0,   1,   3   };         /* Nonzero vectorized index of constr coeff. */
 52    double A0_nz_values[]  =  {  3.0, 1.0, 3.0 };         /* Nonzero values of constr coeff. */
 53
 54    int    A1_nnz = 4;
 55    int    A1_nz_indices[] =  {  0,   2,   4,    8    };  /* Nonzero vectorized index of constr coeff. */
 56    double A1_nz_values[]  =  {  3.0, 1.0, 4.0,  5.0  };  /* Nonzero values of constr coeff. */
 57    
 58    const double row0_values[]   =  {  1.0 };
 59    const double row1_values[]   =  {  1.0 };
 60
 61    /*------------------------------------------------------------------*/
 62    /* Step 1. Create environment and model.                            */
 63    /*------------------------------------------------------------------*/
 64    MDOEnv env = MDOEnv();
 65    MDOModel m = MDOModel(env);
 66
 67    try 
 68    {
 69        /*------------------------------------------------------------------*/
 70        /* Step 2. Input model.                                             */
 71        /*------------------------------------------------------------------*/ 
 72        /* Add variables. */
 73        MDOPsdVar psd_var0 = m.addPsdVar(MDOMatrix::coo(dim_mat[0], dim_mat[0], C0_nnz, C0_nz_indices, C0_nz_values), "X0");
 74        MDOPsdVar psd_var1 = m.addPsdVar(MDOMatrix::coo(dim_mat[1], dim_mat[1], C1_nnz, C1_nz_indices, C1_nz_values), "X1");
 75        MDOVar    var0     = m.addVar(0.0, MDO_INFINITY, 0.0, 'C', "x0");
 76        MDOVar    var1     = m.addVar(0.0, MDO_INFINITY, 0.0, 'C', "x1");        
 77
 78        /* Add constraints. */
 79        MDOPsdExpr psd_expr0 = MDOPsdExpr(psd_var0, MDOMatrix::coo(dim_mat[0], dim_mat[0], A0_nnz, A0_nz_indices, A0_nz_values));
 80        const MDOVar row0_vars[] = { var0 };
 81        psd_expr0.addTerms(row0_values, row0_vars, 1);
 82        m.addPsdConstr(psd_expr0, MDO_EQUAL, 1.0, "C0");
 83
 84        MDOPsdExpr psd_expr1 = MDOPsdExpr(psd_var1, MDOMatrix::coo(dim_mat[0], dim_mat[0], A1_nnz, A1_nz_indices, A1_nz_values));
 85        const MDOVar row1_vars[] = { var1 };
 86        psd_expr1.addTerms(row1_values, row1_vars, 1);        
 87        m.addPsdConstr(psd_expr1, MDO_EQUAL, 2.0, "C1");
 88        
 89        /* Set objective function. */
 90        MDOLinExpr objective = 0;
 91        m.setObjective(objective, -1);
 92        
 93        /*------------------------------------------------------------------*/
 94        /* Step 3. Solve the problem and populate optimization result.      */
 95        /*------------------------------------------------------------------*/
 96        // Optimize the model
 97        m.optimize();
 98        if (m.get(MDO_IntAttr_Status) == MDO_OPTIMAL)
 99        {
100            cout << "Primal objective val: " << m.get(MDO_DoubleAttr_PrimalObjVal) << endl;
101        }
102        else
103        {
104            cout << "No feasible solution." << endl;
105        }
106    } 
107    catch (MDOException &e) 
108    {
109        cout << "Error code = " << e.getErrorCode() << endl;
110        cout << e.getMessage() << endl;
111    } 
112    catch (...) 
113    {
114        cout << "Error during optimization." << endl;
115    }
116
117    return static_cast<int>(MDO_OKAY);
118}