5.4.6. C# 的QCP建模和优化

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

首先,创建优化模型:

33            // Create model
34            MDOEnv env = new MDOEnv(); 
35            MDOModel model = new MDOModel(env); 
36            model.Set(MDO.StringAttr.ModelName, "QCP_01");

接下来,我们通过 MDOModel.Set 将目标函数设置为 最小化,并调用 MDOModel.AddVar 来添加四个优化变量,定义其下界、上界、名称和类型,以及其在目标函数中线性项的系数(有关 MDOModel.SetMDOModel.AddVar 的详细使用方式,请参考 C# API):

40                // Change to minimization problem.
41                model.Set(MDO.IntAttr.ModelSense, MDO.MINIMIZE);
42
43                // Add variables.
44                MDOVar[] x = new MDOVar[4];
45                x[0] = model.AddVar(0.0,         10.0, 0.0, 'C', "x0");
46                x[1] = model.AddVar(0.0, MDO.INFINITY, 0.0, 'C', "x1");
47                x[2] = model.AddVar(0.0, MDO.INFINITY, 0.0, 'C', "x2");
48                x[3] = model.AddVar(0.0, MDO.INFINITY, 0.0, 'C', "x3");

接着我们开始设置二次目标函数。我们创建一个二次表达式 MDOQuadExpr, 再调用 MDOQuadExpr.AddTerms 来设置目标函数线性部分。 obj_idx 表示线性部分的索引,obj_val 表示与 obj_idx 中的索引相对应的非零系数值。

44                MDOVar[] x = new MDOVar[4];
45                x[0] = model.AddVar(0.0,         10.0, 0.0, 'C', "x0");
46                x[1] = model.AddVar(0.0, MDO.INFINITY, 0.0, 'C', "x1");
47                x[2] = model.AddVar(0.0, MDO.INFINITY, 0.0, 'C', "x2");
75                /* Quadratic part in the second constraint: 1/2 [x1^2] */
76                int      qc2_nnz    = 1;
77                MDOVar[] qc2_col1   = new MDOVar[] { x[1] };
78                MDOVar[] qc2_col2   = new MDOVar[] { x[1] };

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

55                /* Quadratic part in the objective: 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] */
56                int      qo_nnz    = 5;
57                MDOVar[] qo_col1   = new MDOVar[] { x[0], x[1], x[2], x[3], x[0] };
58                MDOVar[] qo_col2   = new MDOVar[] { x[0], x[1], x[2], x[3], x[1] };
59                double[] qo_values = new double[] { 0.5,  0.5,  0.5,  0.5,  0.5 };
85                // Add quadratic part in objective: 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] 
86                obj.AddTerms(qo_values, qo_col1, qo_col2);

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

88                model.SetObjective(obj, MDO.MINIMIZE);

现在我们开始添加二次约束。构建二次约束中的二次表达式的方式与二次目标函数类似。

61                /* Linear part in the first constraint: 1 x0 + 1 x1 + 2 x2 + 3 x3 */
62                int      c1_nnz = 4; 
63                MDOVar[] c1_idx = new MDOVar[] { x[0], x[1], x[2], x[3] };
64                double[] c1_val = new double[] { 1.0,  1.0,  2.0,  3.0 };
65                /* Quadratic part in the first constraint: - 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] */
66                int      qc1_nnz    = 5;
67                MDOVar[] qc1_col1   = new MDOVar[] { x[0], x[1], x[2], x[3], x[0] };
68                MDOVar[] qc1_col2   = new MDOVar[] { x[0], x[1], x[2], x[3], x[1] };
69                double[] qc1_values = new double[] { -0.5, -0.5, -0.5, -0.5, -0.5 };
91                MDOQuadExpr c1 = new MDOQuadExpr();
92                c1.AddTerms(c1_val, c1_idx);
93                c1.AddTerms(qc1_values, qc1_col1, qc1_col2);
71                /* Linear part in the second constraint: 1 x0 - 1 x2 + 6 x3 */
72                int      c2_nnz = 3;
73                MDOVar[] c2_idx = new MDOVar[] { x[0], x[2], x[3] };
74                double[] c2_val = new double[] { 1.0,  -1.0, 6.0 };
75                /* Quadratic part in the second constraint: 1/2 [x1^2] */
76                int      qc2_nnz    = 1;
77                MDOVar[] qc2_col1   = new MDOVar[] { x[1] };
78                MDOVar[] qc2_col2   = new MDOVar[] { x[1] };
79                double[] qc2_values = new double[] { 0.5 };  
97                MDOQuadExpr c2 = new MDOQuadExpr();
98                c2.AddTerms(c2_val, c2_idx);
99                c2.AddTerms(qc2_values, qc2_col1, qc2_col2);

然后,我们调用 MDOModel::AddQConstr 将二次约束添加至模型中。

94                model.AddQConstr(c1, MDO.GREATER_EQUAL, 1.0, "c0");
100                model.AddQConstr(c2, MDO.LESS_EQUAL, 1.0, "c1");

问题输入完成后,调用 MDOModel.Optimize 求解优化问题:

102                // Solve the problem and populate optimization result.
103                model.Optimize();

示例 MdoQcoEx1.cs 提供了完整源代码:

  1/**
  2 *  Description
  3 *  -----------
  4 *
  5 *  Quadratically constrained quadratic 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 *
 14 *  Subject To
 15 *   c0 : 1 x0 + 1 x1 + 2 x2 + 3 x3 - 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] >= 1
 16 *   c1 : 1 x0 - 1 x2 + 6 x3 + 1/2 [x1^2] <= 1
 17 *  Bounds
 18 *    0 <= x0 <= 10
 19 *    0 <= x1
 20 *    0 <= x2
 21 *    0 <= x3
 22 *  End
 23 */
 24
 25using Mindopt;
 26
 27namespace Example
 28{
 29    public class MdoQcoEx1
 30    {
 31        public static void Main(string[] args)
 32        {
 33            // Create model
 34            MDOEnv env = new MDOEnv(); 
 35            MDOModel model = new MDOModel(env); 
 36            model.Set(MDO.StringAttr.ModelName, "QCP_01");
 37
 38            try
 39            {
 40                // Change to minimization problem.
 41                model.Set(MDO.IntAttr.ModelSense, MDO.MINIMIZE);
 42
 43                // Add variables.
 44                MDOVar[] x = new MDOVar[4];
 45                x[0] = model.AddVar(0.0,         10.0, 0.0, 'C', "x0");
 46                x[1] = model.AddVar(0.0, MDO.INFINITY, 0.0, 'C', "x1");
 47                x[2] = model.AddVar(0.0, MDO.INFINITY, 0.0, 'C', "x2");
 48                x[3] = model.AddVar(0.0, MDO.INFINITY, 0.0, 'C', "x3");
 49
 50                /* Prepare model data. */
 51                /* Linear part in the objective: 1 x0 + 1 x1 + 1 x2 + 1 x3 */
 52                int      obj_nnz = 4;
 53                MDOVar[] obj_idx = new MDOVar[] { x[0], x[1], x[2], x[3] };
 54                double[] obj_val = new double[] { 1.0,  1.0,  1.0,  1.0 };
 55                /* Quadratic part in the objective: 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] */
 56                int      qo_nnz    = 5;
 57                MDOVar[] qo_col1   = new MDOVar[] { x[0], x[1], x[2], x[3], x[0] };
 58                MDOVar[] qo_col2   = new MDOVar[] { x[0], x[1], x[2], x[3], x[1] };
 59                double[] qo_values = new double[] { 0.5,  0.5,  0.5,  0.5,  0.5 };
 60
 61                /* Linear part in the first constraint: 1 x0 + 1 x1 + 2 x2 + 3 x3 */
 62                int      c1_nnz = 4; 
 63                MDOVar[] c1_idx = new MDOVar[] { x[0], x[1], x[2], x[3] };
 64                double[] c1_val = new double[] { 1.0,  1.0,  2.0,  3.0 };
 65                /* Quadratic part in the first constraint: - 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] */
 66                int      qc1_nnz    = 5;
 67                MDOVar[] qc1_col1   = new MDOVar[] { x[0], x[1], x[2], x[3], x[0] };
 68                MDOVar[] qc1_col2   = new MDOVar[] { x[0], x[1], x[2], x[3], x[1] };
 69                double[] qc1_values = new double[] { -0.5, -0.5, -0.5, -0.5, -0.5 };
 70
 71                /* Linear part in the second constraint: 1 x0 - 1 x2 + 6 x3 */
 72                int      c2_nnz = 3;
 73                MDOVar[] c2_idx = new MDOVar[] { x[0], x[2], x[3] };
 74                double[] c2_val = new double[] { 1.0,  -1.0, 6.0 };
 75                /* Quadratic part in the second constraint: 1/2 [x1^2] */
 76                int      qc2_nnz    = 1;
 77                MDOVar[] qc2_col1   = new MDOVar[] { x[1] };
 78                MDOVar[] qc2_col2   = new MDOVar[] { x[1] };
 79                double[] qc2_values = new double[] { 0.5 };  
 80
 81                // Create a QuadExpr for quadratic objective
 82                MDOQuadExpr obj = new MDOQuadExpr();
 83                // Add objective linear term: 1 x0 + 1 x1 + 1 x2 + 1 x3 
 84                obj.AddTerms(obj_val, obj_idx);
 85                // Add quadratic part in objective: 1/2 [ x0^2 + x1^2 + x2^2 + x3^2 + x0 x1] 
 86                obj.AddTerms(qo_values, qo_col1, qo_col2);
 87
 88                model.SetObjective(obj, MDO.MINIMIZE);
 89
 90                // Add 1st quadratic constraint. 
 91                MDOQuadExpr c1 = new MDOQuadExpr();
 92                c1.AddTerms(c1_val, c1_idx);
 93                c1.AddTerms(qc1_values, qc1_col1, qc1_col2);
 94                model.AddQConstr(c1, MDO.GREATER_EQUAL, 1.0, "c0");
 95
 96                // Add 2nd quadratic constraint. 
 97                MDOQuadExpr c2 = new MDOQuadExpr();
 98                c2.AddTerms(c2_val, c2_idx);
 99                c2.AddTerms(qc2_values, qc2_col1, qc2_col2);
100                model.AddQConstr(c2, MDO.LESS_EQUAL, 1.0, "c1");
101        
102                // Solve the problem and populate optimization result.
103                model.Optimize();
104
105                if (model.Get(MDO.IntAttr.Status) == MDO.Status.OPTIMAL)
106                {
107                    Console.WriteLine($"Optimal objective value is: {model.Get(MDO.DoubleAttr.ObjVal)}");
108                    Console.WriteLine("Decision variables: ");
109                    for (int i = 0; i < 4; i++)
110                        Console.WriteLine( $"x[{i}] = {x[i].Get(MDO.DoubleAttr.X)}");
111                }
112                else
113                {
114                    Console.WriteLine("No feasible solution.");
115                }
116            }
117            catch (Exception e)
118            { 
119                Console.WriteLine("Exception during optimization");
120                Console.WriteLine(e.Message);
121            }
122            finally
123            { 
124                model.Dispose();
125                env.Dispose();
126            }
127        }
128    }
129}