5.3.3. C++ 的QP建模和优化

在本节中,我们将使用 MindOpt C++ API,以按行输入的形式来建模以及求解 二次规划问题示例 中的问题。

首先,引入头文件:

24#include "MindoptCpp.h"

并创建优化模型:

31    /*------------------------------------------------------------------*/
32    /* Step 1. Create environment and model.                            */
33    /*------------------------------------------------------------------*/
34    MDOEnv env = MDOEnv();
35    MDOModel model = MDOModel(env);

接下来,我们通过 MDOModel::set() 将目标函数设置为 最小化,并调用 MDOModel::addVar() 来添加四个优化变量,定义其下界、上界、名称和类型(有关 MDOModel::set()MDOModel::addVar() 的详细使用方式,请参考 C++ API):

42        /* Change to minimization problem. */
43        model.set(MDO_IntAttr_ModelSense, MDO_MINIMIZE);
44
45        /* Add variables. */
46        std::vector<MDOVar> x;
47        x.push_back(model.addVar(0.0, 10.0,         0.0, MDO_CONTINUOUS, "x0"));
48        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, MDO_CONTINUOUS, "x1"));
49        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, MDO_CONTINUOUS, "x2"));
50        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, MDO_CONTINUOUS, "x3"));

接着,我们开始添加线性约束:

52        /* Add constraints. */
53        model.addConstr(1.0 * x[0] + 1.0 * x[1] + 2.0 * x[2] + 3.0 * x[3], MDO_GREATER_EQUAL, 1.0, "c0");
54        model.addConstr(1.0 * x[0] - 1.0 * x[2] + 6.0 * x[3], MDO_EQUAL, 1.0, "c1");

然后,我们创建一个二次表达式 MDOQuadExpr, 再调用 MDOQuadExpr::addTerms 来设置目标函数线性部分。 obj_idx 表示线性部分的索引,obj_val 表示与 obj_idx 中的索引相对应的非零系数值,obj_nnz 代表线性部分的非零元的个数。

56        /*Create a QuadExpr. */
57        MDOQuadExpr obj = MDOQuadExpr(0.0);
58
59        /* Add objective linear term: 1 x0 + 1 x1 + 1 x2 + 1 x3 */
60        int    obj_nnz   = 4;
61        MDOVar obj_idx[] = { x[0], x[1], x[2], x[3] };
62        double obj_val[] = { 1.0,  1.0,  1.0,  1.0} ;
63        obj.addTerms(obj_val, obj_idx, obj_nnz);

然后,调用 MDOQuadExpr::addTerms 来设置目标的二次项系数 \(Q\)。 其中,qo_values 表示要添加的二次项的系数,qo_col1qo_col2 表示与qo_values相对应的二次项的第一个变量和第二个变量,qo_nnz 表示二次项中的非零元个数。

64        /* Add objective quadratic term: 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] */
65        int    qo_nnz      = 5;
66        MDOVar qo_col1[]   = { x[0], x[1], x[2], x[3], x[0] };
67        MDOVar qo_col2[]   = { x[0], x[1], x[2], x[3], x[1] };
68        double qo_values[] = { 0.5,  0.5,  0.5,  0.5,  0.5 };
69        obj.addTerms(qo_values, qo_col1, qo_col2, qo_nnz);

最后,我们调用 MDOModel::setObjective 设定优化目标与方向。

72        model.setObjective(obj, MDO_MINIMIZE);

问题输入完成后,调用 MDOModel::optimize() 求解优化问题:

78        model.optimize();

示例 MdoQoEx1.cpp 提供了完整源代码:

  1/**
  2 *  Description
  3 *  -----------
  4 *
  5 *  Linear optimization (row-wise input).
  6 *
  7 *  Formulation
  8 *  -----------
  9 *
 10 *  Minimize
 11 *    obj: 1 x0 + 1 x1 + 1 x2 + 1 x3 
 12 *         + 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1]
 13 *  Subject To
 14 *   c0 : 1 x0 + 1 x1 + 2 x2 + 3 x3 >= 1
 15 *   c1 : 1 x0 - 1 x2 + 6 x3 = 1
 16 *  Bounds
 17 *    0 <= x0 <= 10
 18 *    0 <= x1
 19 *    0 <= x2
 20 *    0 <= x3
 21 *  End
 22 */
 23#include <iostream>
 24#include "MindoptCpp.h"
 25#include <vector>
 26
 27using namespace std;
 28
 29int main(void)
 30{
 31    /*------------------------------------------------------------------*/
 32    /* Step 1. Create environment and model.                            */
 33    /*------------------------------------------------------------------*/
 34    MDOEnv env = MDOEnv();
 35    MDOModel model = MDOModel(env);
 36    
 37    try 
 38    {
 39        /*------------------------------------------------------------------*/
 40        /* Step 2. Input model.                                             */
 41        /*------------------------------------------------------------------*/
 42        /* Change to minimization problem. */
 43        model.set(MDO_IntAttr_ModelSense, MDO_MINIMIZE);
 44
 45        /* Add variables. */
 46        std::vector<MDOVar> x;
 47        x.push_back(model.addVar(0.0, 10.0,         0.0, MDO_CONTINUOUS, "x0"));
 48        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, MDO_CONTINUOUS, "x1"));
 49        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, MDO_CONTINUOUS, "x2"));
 50        x.push_back(model.addVar(0.0, MDO_INFINITY, 0.0, MDO_CONTINUOUS, "x3"));
 51
 52        /* Add constraints. */
 53        model.addConstr(1.0 * x[0] + 1.0 * x[1] + 2.0 * x[2] + 3.0 * x[3], MDO_GREATER_EQUAL, 1.0, "c0");
 54        model.addConstr(1.0 * x[0] - 1.0 * x[2] + 6.0 * x[3], MDO_EQUAL, 1.0, "c1");
 55        
 56        /*Create a QuadExpr. */
 57        MDOQuadExpr obj = MDOQuadExpr(0.0);
 58
 59        /* Add objective linear term: 1 x0 + 1 x1 + 1 x2 + 1 x3 */
 60        int    obj_nnz   = 4;
 61        MDOVar obj_idx[] = { x[0], x[1], x[2], x[3] };
 62        double obj_val[] = { 1.0,  1.0,  1.0,  1.0} ;
 63        obj.addTerms(obj_val, obj_idx, obj_nnz);
 64
 65        /* Add objective quadratic term: 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] */
 66        int    qo_nnz      = 5;
 67        MDOVar qo_col1[]   = { x[0], x[1], x[2], x[3], x[0] };
 68        MDOVar qo_col2[]   = { x[0], x[1], x[2], x[3], x[1] };
 69        double qo_values[] = { 0.5,  0.5,  0.5,  0.5,  0.5 };
 70        obj.addTerms(qo_values, qo_col1, qo_col2, qo_nnz);
 71
 72        model.setObjective(obj, MDO_MINIMIZE);
 73
 74        /*------------------------------------------------------------------*/
 75        /* Step 3. Solve the problem and populate optimization result.                */
 76        /*------------------------------------------------------------------*/
 77        /* Solve the problem. */
 78        model.optimize();
 79
 80        if(model.get(MDO_IntAttr_Status) == MDO_OPTIMAL)
 81        {
 82            cout << "Optimal objective value is: " << model.get(MDO_DoubleAttr_ObjVal) << endl;
 83            cout << "Decision variables:" << endl;
 84            int i = 0;
 85            for (auto v : x)
 86            {
 87                cout << "x[" << i++ << "] = " << v.get(MDO_DoubleAttr_X) << endl;
 88            }
 89        }
 90        else
 91        {
 92            cout<< "No feasible solution." << endl;
 93        }
 94        
 95    }
 96    catch (MDOException& e) 
 97    { 
 98        std::cout << "Error code = " << e.getErrorCode() << std::endl;
 99        std::cout << e.getMessage() << std::endl;
100    } 
101    catch (...) 
102    { 
103        std::cout << "Error during optimization." << std::endl;
104    }
105    
106    return static_cast<int>(MDO_OKAY);
107}