5.4.4. C++的SDP建模与优化

在本节中,我们将使用 MindOpt C++ 语言的 API 来建模以及求解 半定规划问题示例 中的问题。

5.4.4.1. SDP示例1

首先,需要引入头文件:

23#include "MindoptCpp.h"

第一步:创建优化模型:

先建立一空的MindOpt模型。

47    MdoModel model;

第二步:SDP模型输入

接着,我们将目标函数改为maximization,并新增 \(\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);

我们利用 mindopt::MdoModel::replaceSymMatObjs() 来输入目标函数中的 \(\mathbf{C}\) 的非零元素。 其中,第一个参数为 矩阵的索引值 (此处为0),第二个参数为 矩阵中非零元素的个数,第三个和第四个参数分别代表 矩阵中非零元素其对应的行和 列之索引 ,而最后一个参数则为 非零元数值的索引

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

接着,我们输入第一个约束。我们首先输一空约束;接着再利用 mindopt::MdoModel::replaceSymMatElements() 输入约束中的 \(\mathbf{A}\) 的非零元素。其中,第一和第二个参数分别为: 约束的索引值 (此处为0)以及 矩阵的索引值 (此处为0),第三个参数为 矩阵的非零元素的个数,第四个和第五个参数代表 矩阵中非零元素其对应的行和列之索引,而最后一个参数则为 非零元的数值之索引

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);

第三步:求解SDP模型 模型输入后,我们接着用 mindopt::MdoModel::solveProb() 来求解问题,并用 mindopt::MdoModel::displayResults() 呈现求解结果。

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

第四步: 取得SDP模型的解 我们利用泛用函数 mindopt::MdoModel::getRealAttr() 来取得最优的的目标函数值

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;

最后,我们利用 mindopt::MdoModel::getRealAttrSymMat() 来取得 \(\mathbf{X}\) 的变量值。其中, 第一个参数定义 属性值 (附注: mindopt::MdoModel::getRealAttrSymMat() 为泛用函数,因此必须配合 属性值来定义函数的使用目的;MDO_REAL_ATTR::SYM_MAT_PRIMAL_SOLN定义目的为 获取(对称矩阵)变量的数值),第二个参数为: 矩阵的索引值 (此处为0),第三个参数为 矩阵的尺寸

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

文件链接 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 *  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示例2

首先,需要引入头文件:

30#include "MindoptCpp.h"

第一步:创建MindOpt模型

首先,我们必须先建立一空的MindOpt模型。

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

第二步:SDP模型输入

接着,我们将目标函数改为maximization,并新增 \(x_0\)\(x_1\) 两个变量,以及 \(\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);

我们利用 mindopt::MdoModel::replaceSymMatObjs() 来输入目标函数中的 \(\mathbf{C}_0\) 的非零元素。 其中,第一个参数为 矩阵的索引值 (此处为C0_mat_idx),第二个参数为 矩阵的非零元素个数, 第三和第四个参数分别代表 矩阵中非零元素其对应的行和列之索引 , 而最后一个参数则为 非零元的数值索引

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

同理,我们再接着输入目标函数中的 \(\mathbf{C}_1\) 的非零元素。

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

接着,我们输入第一个约束。我们首先输入空约束,接着再利用 mindopt::MdoModel::replaceSymMatElements() 输入约束中的 \(\mathbf{A}_{00}\) 的非零元素。其中,第一和第二个参数分别为: 约束的索引值 (此处为0)以及 矩阵的索引值 (此处为 A00_mat_idx ),第三个参数为 矩阵的非零元素的个数,第四个和第五个参数代表 矩阵中非零元素其对应的行和列之索引,而最后一个参数则为 非零元的数值之索引

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);

同理,我们再接着输入第二个约束。

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);

第三步:求解SDP模型

模型输入后,我们接着用 mindopt::MdoModel::solveProb() 来求解问题,并用 mindopt::MdoModel::displayResults() 呈现求解结果。

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

第四步: 取得SDP模型的解

我们利用泛用函数 mindopt::MdoModel::getRealAttr() 来 取得最优的的目标函数值,并用泛用函数 mindopt::MdoModel::getRealAttrArray() 来 取得 \(x_0\)\(x_1\) 两个变量值;此处第一个参数定义了 属性值 (附注: mindopt::MdoModel::getRealAttrArray() 为泛用函数, 因此必须配合属性值来定义函数的使用目的;MDO_REAL_ATTR::PRIMAL_SOLN定义目的为获取(线性)变量的数值), 第二和第三个参数分别代表第一个索引和最后一个索引 加上1, 也就是 \(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;

最后,我们利用 mindopt::MdoModel::getRealAttrSymMat() 来取得 \(\mathbf{X}_0\)\(\mathbf{X}_1\) 的变量值。其中, 第一个参数定义 属性值 (附注: mindopt::MdoModel::getRealAttrSymMat() 为泛用函数,因此必须配合 属性值来定义函数的使用目的;MDO_REAL_ATTR::SYM_MAT_PRIMAL_SOLN定义目的为 获取(对称矩阵)变量的数值),第二个参数为: 矩阵的索引值 (此处为b),第三个参数为 矩阵的尺寸

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

文件链接 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 *  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}