5.4.4. SDP modeling and optimization in C++ language

In this section, we will use the API of MindOpt C++ language to model and solve the problem in Examples of SDP Problems.

5.4.4.1. SDP Example 1

First, import the header file:

23#include "MindoptCpp.h"

Step 1: Create a Model

Create an empty MindOpt model.

47    MdoModel model;

Step 2: Input SDP Model

Change to maximization problem and add a symmetric matrix variable \(\mathbf{X}\).

51        /*------------------------------------------------------------------*/
52        /* Step 2. Input model.                                             */
53        /*------------------------------------------------------------------*/
54        /* Change to maximization problem. */
55        model.setIntAttr(MDO_INT_ATTR::MIN_SENSE, MDO_NO);
56
57        /* Add matrix variable. */
58        model.addSymMat(dim_mat, mat_name);

Use mindopt::MdoModel::replaceSymMatObjs() to input the non-zero elements of \(\mathbf{C}\). The first parameter is the index value of the matrix (here is 0). The second parameter represents the number of non-zero elements of the matrix. The third and fourth parameters represent the index of the row and column corresponding to the non-zero elements in the matrix. The last parameter is the index of the value of the non-zero element.

60        /* Input objective coefficients. */
61        model.replaceSymMatObjs(0, C_size, C_row_indices, C_col_indices, C_values);

Next, write the first constraint. We first write an empty constraint, then use mindopt::MdoModel::replaceSymMatElements() to input non-zero elements of \(\mathbf{A}\). The first and the second parameters are the index value of the constraint (here is 0) and the index value of the matrix (here is 0). The third parameter represents the number of non-zero elements of the matrix. The fourth and fifth parameters represent the index of the row and column corresponding to the non-zero elements in the matrix. The last parameter is the index of the value of the non-zero element.

63        /* Input first constraint. */
64        model.addCons(1.0, 1.0, "c0");
65        model.replaceSymMatElements(
66            0, 0, A_size, A_row_indices_in_cone, A_col_indices_in_cone, A_values_in_cone);

Step 3: Solve the SDP model

When the model is input, use the generic function mindopt::MdoModel::solveProb() to solve the problem and use Mdo_displayResults() to present the solution results.

71        /* Solve the problem. */
72        model.solveProb();
73        model.displayResults();

第四步: 取得SDP模型的解

Use the function mindopt::MdoModel::getRealAttr() to obtain the optimal objective function value.

82            std::cout << "Optimizer terminated with an OPTIMAL status" << std::endl;
83            std::cout << std::setprecision(6) << std::cout.width(8) << std::fixed << std::scientific <<
84                " - Primal objective : " << model.getRealAttr(MDO_REAL_ATTR::PRIMAL_OBJ_VAL) << std::endl;

Finally, use mindopt::MdoModel::getRealAttrSymMat() to obtain \(\mathbf{X}\). The first parameter defines the attribute value (note: mindopt::MdoModel::getRealAttrSymMat() is a generic function, so it is declared with the attribute value and MDO_REAL_ATTR::SYM_MAT_PRIMAL_SOLN defines the purpose to obtain the value of the (symmetric matrix) variable. ) The second parameter is the index value of the matrix (here is 0). The third parameter is the size of the matrix.

85            auto soln = model.getRealAttrSymMat(
86                MDO_REAL_ATTR::SYM_MAT_PRIMAL_SOLN, 0, dim_mat * dim_mat, NULL, NULL);

Please refer to the linked file MdoSdoEx1.cpp for the complete code.

  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 *  Matrix
 16 *    C = [ -3  0  1 ]  A = [ 3 0 1 ]
 17 *        [  0 -2  0 ]      [ 0 4 0 ]
 18 *        [  1  0 -3 ]      [ 1 0 5 ]
 19 *  End
 20 */
 21#include <iostream>
 22#include <iomanip>
 23#include "MindoptCpp.h"
 24
 25using namespace mindopt;
 26
 27int main(void)
 28{
 29    const std::string mat_name = "X";
 30    const int    num_mats = 1;
 31    const int    dim_mat = 3; /* Dimension of the matrix variables. */
 32
 33    const int    C_size = 4;
 34    const int    C_row_indices[] =  {  0,   0,    1,    2   }; /* Row index of a matrix variable. */
 35    const int    C_col_indices[] =  {  0,   2,    1,    2   }; /* Column index of a matrix variable. */
 36    const double C_values[]      =  { -3.0, 1.0, -2.0, -3.0 }; /* Values of a matrix variable. */
 37
 38    const int    A_size = 4;
 39    const int    A_row_indices_in_cone[] = { 0, 2, 1, 2 };    /* Row index in a matrix variable. */
 40    const int    A_col_indices_in_cone[] = { 0, 0, 1, 2 };    /* Column index in a matrix variable. */
 41    const double A_values_in_cone[] = { 3.0, 1.0, 4.0, 5.0 }; /* Values of a matrix variable. */
 42       
 43    /*------------------------------------------------------------------*/
 44    /* Step 1. Create a model and change the parameters.                */
 45    /*------------------------------------------------------------------*/
 46    /* Create an empty model. */
 47    MdoModel model;
 48
 49    try 
 50    {
 51        /*------------------------------------------------------------------*/
 52        /* Step 2. Input model.                                             */
 53        /*------------------------------------------------------------------*/
 54        /* Change to maximization problem. */
 55        model.setIntAttr(MDO_INT_ATTR::MIN_SENSE, MDO_NO);
 56
 57        /* Add matrix variable. */
 58        model.addSymMat(dim_mat, mat_name);
 59
 60        /* Input objective coefficients. */
 61        model.replaceSymMatObjs(0, C_size, C_row_indices, C_col_indices, C_values);
 62
 63        /* Input first constraint. */
 64        model.addCons(1.0, 1.0, "c0");
 65        model.replaceSymMatElements(
 66            0, 0, A_size, A_row_indices_in_cone, A_col_indices_in_cone, A_values_in_cone);
 67
 68        /*------------------------------------------------------------------*/
 69        /* Step 3. Solve the problem and populate the result.               */
 70        /*------------------------------------------------------------------*/
 71        /* Solve the problem. */
 72        model.solveProb();
 73        model.displayResults();
 74
 75        switch (model.getStatus())
 76        {
 77        case MDO_UNKNOWN:
 78            std::cout <<"Optimizer terminated with an UNKNOWN status." << std::endl;
 79            break;
 80        case MDO_OPTIMAL:
 81        {
 82            std::cout << "Optimizer terminated with an OPTIMAL status" << std::endl;
 83            std::cout << std::setprecision(6) << std::cout.width(8) << std::fixed << std::scientific <<
 84                " - Primal objective : " << model.getRealAttr(MDO_REAL_ATTR::PRIMAL_OBJ_VAL) << std::endl;
 85            auto soln = model.getRealAttrSymMat(
 86                MDO_REAL_ATTR::SYM_MAT_PRIMAL_SOLN, 0, dim_mat * dim_mat, NULL, NULL);
 87            std::cout << "X = " << std::endl;
 88            for (auto i = 0; i < dim_mat; ++i)
 89            {
 90                std::cout << "  (";
 91                for (auto j = 0; j < dim_mat; ++j)
 92                {
 93                    std::cout << " " << soln[i * dim_mat + j];
 94                }
 95                std::cout << " )" << std::endl;
 96            }
 97            break;
 98        }
 99        case MDO_INFEASIBLE:
100            std::cout << "Optimizer terminated with an INFEASIBLE status." << std::endl;
101            break;
102        case MDO_UNBOUNDED:
103            std::cout << "Optimizer terminated with an UNBOUNDED status." << std::endl;
104            break;
105        case MDO_INF_OR_UBD:
106            std::cout << "Optimizer terminated with an INFEASIBLE or UNBOUNDED status." << std::endl;
107            break;
108        }
109    }
110    catch (MdoException & e)
111    {
112        std::cerr << "===================================" << std::endl;
113        std::cerr << "Error   : code <" << e.getResult() << ">" << std::endl;
114        std::cerr << "Reason  : " << model.explainResult(e.getResult()) << std::endl;
115        std::cerr << "===================================" << std::endl;
116
117        return static_cast<int>(e.getResult());
118    }
119     
120    return static_cast<int>(MDO_OKAY);
121}

5.4.4.2. SDP Example 2

First, import the header file:

30#include "MindoptCpp.h"

Step 1: Create a Model

Create an empty MindOpt model.

67    /* Create an empty model. */
68    MdoModel model;

Step 2: Input SDP Model

Change to maximization problem and add two scalar variables \(x_0\)\(x_1\) and two symmetric matrix variables \(\mathbf{X}_0\), \(\mathbf{X}_1\).

72        /*------------------------------------------------------------------*/
73        /* Step 2. Input model.                                             */
74        /*------------------------------------------------------------------*/
75        /* Change to maximization problem. */
76        model.setIntAttr(MDO_INT_ATTR::MIN_SENSE, MDO_NO);
77
78        /* Add variables. */
79        std::vector<MdoVar> x;
80        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, "x0", MDO_NO));
81        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, "x1", MDO_NO));
82
83        /* Add matrix variables. */
84        model.addSymMats(num_mats, dim_mats, mat_names);

Use mindopt::MdoModel::replaceSymMatObjs() to input the non-zero elements of \(\mathbf{C}_0\). The first parameter is the index value of the matrix (here is C0_mat_idx). The second parameter represents the number of non-zero elements of the matrix. The third and fourth parameters represent the index of the row and column corresponding to the non-zero elements in the matrix. The last parameter is the index of the value of the non-zero element.

86        /* Input objective coefficients. */
87        model.replaceSymMatObjs(C0_mat_idx, C0_size, C0_row_indices, C0_col_indices, C0_values);

Similarly, we then input the non-zero elements of \(\mathbf{C}_1\) in the objective function.

88        model.replaceSymMatObjs(C1_mat_idx, C1_size, C1_row_indices, C1_col_indices, C1_values);

Next, write the first constraint. We first write an empty constraint, then use mindopt::MdoModel::replaceSymMatElements() to input non-zero elements of \(\mathbf{A}_{00}\). The first and second parameters are the index value of the constraint (here is 0) and the index value of the matrix (here is A00_mat_idx). The third parameter represents the number of non-zero elements of the matrix. The fourth and fifth parameters represent the index of the row and column corresponding to the non-zero elements in the matrix. The last parameter is the index of the value of the non-zero element.

90        /* Input first constraint. */
91        model.addCons(1.0 * x[0] == 1.0, "c0");
92        model.replaceSymMatElements(
93            0, A00_mat_idx, A00_size, A00_row_indices_in_cone, A00_col_indices_in_cone, A00_values_in_cone);

In the same way, write the second constraint.

95        /* Input second constraint. */
96        model.addCons(1.0 * x[1] == 2.0, "c1");
97        model.replaceSymMatElements(
98            1, A11_mat_idx, A11_size, A11_row_indices_in_cone, A11_col_indices_in_cone, A11_values_in_cone);

Step3: Solve the SDP model

When the model is input, use the generic function mindopt::MdoModel::solveProb() to solve the problem and use the function mindopt::MdoModel::displayResults() to present the solution results.

100        /*------------------------------------------------------------------*/
101        /* Step 3. Solve the problem and populate the result.               */
102        /*------------------------------------------------------------------*/
103        /* Solve the problem. */
104        model.solveProb();
105        model.displayResults();

Step 4: Obtain the solutions of the SDP model

We use the function mindopt::MdoModel::getRealAttr() to get the optimal objective function value and the function mindopt::MdoModel::getRealAttrArray() to obtain the variables \(x_0\)\(x_1\). The first parameter is the attribute value (note: mindopt::MdoModel::getRealAttrArray() is a generic function, so it is declared with the attribute value and MDO_REAL_ATTR::PRIMAL_SOLN defines the purpose to obtain the values of scalar variables. ) The second and third parameters represent the first index and the last index plus 1, that is, \(x_j\), \(0 \leq j < 2\).

115            std::cout << std::setprecision(6) << std::cout.width(8) << std::fixed << std::scientific <<
116                " - Primal objective : " << model.getRealAttr(MDO_REAL_ATTR::PRIMAL_OBJ_VAL) << std::endl;

Finally, use mindopt::MdoModel::getRealAttrSymMat() to obtain \(\mathbf{X}_0\) and \(\mathbf{X}_1\). The first parameter defines the attribute value (note: mindopt::MdoModel::getRealAttrSymMat() is a generic function, so it is declared with the attribute value and MDO_REAL_ATTR::SYM_MAT_PRIMAL_SOLN defines the purpose to obtain the value of the (symmetric matrix) variable. ) The second parameter is the index value of the matrix (here is b). The third parameter is the size of the matrix.

124                auto soln = model.getRealAttrSymMat(
125                    MDO_REAL_ATTR::SYM_MAT_PRIMAL_SOLN, b, dim_mats[b] * dim_mats[b], NULL, NULL);

Please refer to the linked file`MdoSdoEx2.cpp <../../codes/MdoSdoEx2.cpp>`_ for the complete code.

  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 *  Matrix
 20 *    C0 =  [ 2 1 ]   A00 = [ 3 1 ]
 21 *          [ 1 2 ]         [ 1 3 ]
 22 *
 23 *    C0 = [ 3 0 1 ]  A00 = [ 3 0 1 ]
 24 *         [ 0 2 0 ]        [ 0 4 0 ]
 25 *         [ 1 0 3 ]        [ 1 0 5 ]
 26 *  End
 27 */
 28#include <iostream>
 29#include <iomanip>
 30#include "MindoptCpp.h"
 31
 32using namespace mindopt;
 33
 34int main(void)
 35{
 36    const int         num_mats = 2;
 37    const int         dim_mats[] = { 2, 3 }; /* Dimension of the matrix variables. */
 38    const std::string mat_names[] = { "X0", "X1" };
 39
 40    const int    C0_size = 3;
 41    const int    C0_mat_idx = 0;
 42    const int    C0_row_indices[] = { 0,   1,   1 }; /* Row index of a matrix variable. */
 43    const int    C0_col_indices[] = { 0,   0,   1 }; /* Column index of a matrix variable. */
 44    const double C0_values[] = { 2.0, 1.0, 2.0 }; /* Values of a matrix variable. */
 45
 46    const int    C1_size = 4;
 47    const int    C1_mat_idx = 1;
 48    const int    C1_row_indices[] = { 0,   0,   1,   2 }; /* Row index of a matrix variable. */
 49    const int    C1_col_indices[] = { 0,   2,   1,   2 }; /* Column index of a matrix variable. */
 50    const double C1_values[] = { 3.0, 1.0, 2.0, 3.0 }; /* Values of a matrix variable. */
 51
 52    const int    A00_size = 3;
 53    const int    A00_mat_idx = 0;
 54    const int    A00_row_indices_in_cone[] = { 0, 1, 1 }; /* Row index in a matrix variable. */
 55    const int    A00_col_indices_in_cone[] = { 0, 0, 1 }; /* Column index in a matrix variable. */
 56    const double A00_values_in_cone[] = { 3.0, 1.0, 3.0 }; /* Values of a matrix variable. */
 57
 58    const int    A11_size = 4;
 59    const int    A11_mat_idx = 1;
 60    const int    A11_row_indices_in_cone[] = { 0, 2, 1, 2 }; /* Row index in a matrix variable. */
 61    const int    A11_col_indices_in_cone[] = { 0, 0, 1, 2 }; /* Column index in a matrix variable. */
 62    const double A11_values_in_cone[] = { 3.0, 1.0, 4.0, 5.0 }; /* Values of a matrix variable. */
 63 
 64    /*------------------------------------------------------------------*/
 65    /* Step 1. Create a model and change the parameters.                */
 66    /*------------------------------------------------------------------*/
 67    /* Create an empty model. */
 68    MdoModel model;
 69
 70    try 
 71    {
 72        /*------------------------------------------------------------------*/
 73        /* Step 2. Input model.                                             */
 74        /*------------------------------------------------------------------*/
 75        /* Change to maximization problem. */
 76        model.setIntAttr(MDO_INT_ATTR::MIN_SENSE, MDO_NO);
 77
 78        /* Add variables. */
 79        std::vector<MdoVar> x;
 80        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, "x0", MDO_NO));
 81        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, "x1", MDO_NO));
 82
 83        /* Add matrix variables. */
 84        model.addSymMats(num_mats, dim_mats, mat_names);
 85
 86        /* Input objective coefficients. */
 87        model.replaceSymMatObjs(C0_mat_idx, C0_size, C0_row_indices, C0_col_indices, C0_values);
 88        model.replaceSymMatObjs(C1_mat_idx, C1_size, C1_row_indices, C1_col_indices, C1_values);
 89
 90        /* Input first constraint. */
 91        model.addCons(1.0 * x[0] == 1.0, "c0");
 92        model.replaceSymMatElements(
 93            0, A00_mat_idx, A00_size, A00_row_indices_in_cone, A00_col_indices_in_cone, A00_values_in_cone);
 94
 95        /* Input second constraint. */
 96        model.addCons(1.0 * x[1] == 2.0, "c1");
 97        model.replaceSymMatElements(
 98            1, A11_mat_idx, A11_size, A11_row_indices_in_cone, A11_col_indices_in_cone, A11_values_in_cone);
 99
100        /*------------------------------------------------------------------*/
101        /* Step 3. Solve the problem and populate the result.               */
102        /*------------------------------------------------------------------*/
103        /* Solve the problem. */
104        model.solveProb();
105        model.displayResults();
106
107        switch (model.getStatus())
108        {
109        case MDO_UNKNOWN:
110            std::cout <<"Optimizer terminated with an UNKNOWN status." << std::endl;
111            break;
112        case MDO_OPTIMAL:
113        {
114            std::cout << "Optimizer terminated with an OPTIMAL status" << std::endl;
115            std::cout << std::setprecision(6) << std::cout.width(8) << std::fixed << std::scientific <<
116                " - Primal objective : " << model.getRealAttr(MDO_REAL_ATTR::PRIMAL_OBJ_VAL) << std::endl;
117            auto soln = model.getRealAttrArray(MDO_REAL_ATTR::PRIMAL_SOLN, 0, 2);
118            for (auto j = 0; j < 2; ++j)
119            {
120                std::cout << "x[" << j << "] = " << soln[j] << std::endl;
121            }
122            for (auto b = 0; b < num_mats; ++b)
123            {
124                auto soln = model.getRealAttrSymMat(
125                    MDO_REAL_ATTR::SYM_MAT_PRIMAL_SOLN, b, dim_mats[b] * dim_mats[b], NULL, NULL);
126                std::cout << "X[" << b<< "] = " << std::endl;
127                for (auto i = 0; i < dim_mats[b]; ++i)
128                {
129                    std::cout << "  (";
130                    for (auto j = 0; j < dim_mats[b]; ++j)
131                    {
132                        std::cout << " " << soln[i * dim_mats[b] + j];
133                    }
134                    std::cout << " )" << std::endl;
135                }
136            }
137            break;
138        }
139        case MDO_INFEASIBLE:
140            std::cout << "Optimizer terminated with an INFEASIBLE status." << std::endl;
141            break;
142        case MDO_UNBOUNDED:
143            std::cout << "Optimizer terminated with an UNBOUNDED status." << std::endl;
144            break;
145        case MDO_INF_OR_UBD:
146            std::cout << "Optimizer terminated with an INFEASIBLE or UNBOUNDED status." << std::endl;
147            break;
148        }
149    }
150    catch (MdoException & e)
151    {
152        std::cerr << "===================================" << std::endl;
153        std::cerr << "Error   : code <" << e.getResult() << ">" << std::endl;
154        std::cerr << "Reason  : " << model.explainResult(e.getResult()) << std::endl;
155        std::cerr << "===================================" << std::endl;
156
157        return static_cast<int>(e.getResult());
158    }
159
160    return static_cast<int>(MDO_OKAY);
161}