5.6.11. C# 的 MISOCP 建模和优化

在本节中,我们将使用 MindOpt C# API 来建模和求解 MIQCP示例2 中的混合整数二阶锥规划问题。

引入 Mindopt 命名空间:

25 */
26using Mindopt;

创建优化环境和模型:

34        {
35            // Create environment and model.
36            MDOEnv env = new MDOEnv();
37            MDOModel model = new MDOModel(env);

接下来,我们将求解方向设置为 最小化。然后,调用 MDOModel.AddVar 添加五个优化变量。它们的下界、上界、类型、名称以及在目标函数中的线性项系数都将作为参数直接传入。

备注

在调用 MDOModel.AddVar 时,将类型设置为 'I' 代表这个变量是整形变量。

43                // Change to minimization problem.
44                // Add variables. The linear objective coefficients are specified directly.
45                MDOVar[] x = new MDOVar[5];
46                x[0] = model.AddVar(0.0, 10.0, 1.0, 'I', "x0");
47                x[1] = model.AddVar(0.0, MDO.INFINITY, 2.0, 'I', "x1");
48                x[2] = model.AddVar(0.0, MDO.INFINITY, 1.0, 'I', "x2");
49                x[3] = model.AddVar(0.0, MDO.INFINITY, 1.0, 'C', "x3");

备注

由于线性目标系数在创建变量时已直接传入,因此 无需单独调用 MDOModel.SetObjective

现在,我们添加线性约束。对于 C# 接口,这需要为每个约束创建一个 MDOExpr 对象,并向其添加项。

  • 约束 c0: \(x_0 + x_1 + 2x_2 + 3x_3 \geq 1\)

  • 约束 c1: \(x_0 - x_2 + 6x_3 = 1\)

使用 MDOModel.AddConstr 将它们添加到模型中:

53                // Add linear constraint c0: 1 x0 + 1 x1 + 2 x2 + 3 x3 >= 1
54                MDOExpr c0 = new MDOExpr();
55                c0.AddTerms(new double[] { 1.0, 1.0, 2.0, 3.0 }, new MDOVar[] { x[0], x[1], x[2], x[3] });
56                model.AddConstr(c0, MDO.GREATER_EQUAL, 1.0, "c0");
57
58                // Add linear constraint c1: 1 x0 - 1 x2 + 6 x3 = 1
59                MDOExpr c1 = new MDOExpr();
60                c1.AddTerms(new double[] { 1.0, -1.0, 6.0 }, new MDOVar[] { x[0], x[2], x[3] });

最后,我们添加二次(二阶锥)约束:

  • c2: \(x_1^2 + x_2^2 - x_4^2 \leq 0\)

这通过创建一个 MDOQuadExpr 对象并添加二次项来完成。然后使用 MDOModel.AddQConstr 将该约束添加到模型中。

63                // Add second-order cone constraint c2: x_1^2 + x_2^2 - x_4^2 <= 0
64                MDOQuadExpr c2 = new MDOQuadExpr();
65                // This constraint has no linear part. We only add the quadratic terms.
66                c2.AddTerms(
67                    new double[] { 1.0, 1.0, -1.0 },      // values
68                    new MDOVar[] { x[1], x[2], x[4] },     // first variable indices
69                    new MDOVar[] { x[1], x[2], x[4] }      // second variable indices
70                );

模型构建完成后,我们调用 MDOModel.Optimize 来求解问题:

74                // Step 2. Solve the problem.

求解后,我们检查求解状态,并获取最优目标值和变量取值。

82                // exist, making it necessary to check the SolCount property.
83                if (model.Get(MDO.IntAttr.Status) == MDO.Status.OPTIMAL || model.Get(MDO.IntAttr.Status) == MDO.Status.SUB_OPTIMAL ||
84                    model.Get(MDO.IntAttr.SolCount) > 0)
85                {
86                    Console.WriteLine($"Optimal objective value is: {model.Get(MDO.DoubleAttr.ObjVal)}");
87                    Console.WriteLine("Decision variables: ");
88                    for (int i = 0; i < 5; i++)
89                    {
90                        Console.WriteLine($"x[{i}] = {x[i].Get(MDO.DoubleAttr.X)}");
91                    }

最后,我们释放模型和环境以回收资源。

110            {
111                // Step 4. Free the model.
112                model.Dispose();

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

  1/**
  2 *  Description
  3 *  -----------
  4 *
  5 *  Formulation
  6 *  -----------
  7 *
  8  Minimize
  9 *    obj: 1 x0 + 2 x1 + 1 x2 + 1 x3 + 0.5 x_4
 10 *
 11 *
 12 *  Subject To
 13 *   c0 : 1 x0 + 1 x1 + 2 x2 + 3 x3 >= 1
 14 *   c1 : 1 x0 - 1 x2 + 6 x3  = 1
 15 *   c2 : x_1^2 + x_2^2 - x_4^2 <= 0
 16 *  Bounds
 17 *    0 <= x0 <= 10
 18 *    0 <= x1
 19 *    0 <= x2
 20 *    0 <= x3
 21 *    0 <= x4
 22 *  Integer
 23 *  x0, x1, x2
 24 *  End
 25 */
 26using Mindopt;
 27using System;
 28
 29namespace Example
 30{
 31    public class MdoMISOCPEx1
 32    {
 33        public static void Main(string[] args)
 34        {
 35            // Create environment and model.
 36            MDOEnv env = new MDOEnv();
 37            MDOModel model = new MDOModel(env);
 38            model.Set(MDO.StringAttr.ModelName, "MISOCPEx1");
 39
 40            try
 41            {
 42                // Step 1. Input model.
 43                // Change to minimization problem.
 44                model.Set(MDO.IntAttr.ModelSense, MDO.MINIMIZE);
 45
 46                // Add variables. The linear objective coefficients are specified directly.
 47                MDOVar[] x = new MDOVar[5];
 48                x[0] = model.AddVar(0.0, 10.0, 1.0, 'I', "x0");
 49                x[1] = model.AddVar(0.0, MDO.INFINITY, 2.0, 'I', "x1");
 50                x[2] = model.AddVar(0.0, MDO.INFINITY, 1.0, 'I', "x2");
 51                x[3] = model.AddVar(0.0, MDO.INFINITY, 1.0, 'C', "x3");
 52                x[4] = model.AddVar(0.0, MDO.INFINITY, 0.5, 'C', "x4");
 53
 54                // Add linear constraint c0: 1 x0 + 1 x1 + 2 x2 + 3 x3 >= 1
 55                MDOExpr c0 = new MDOExpr();
 56                c0.AddTerms(new double[] { 1.0, 1.0, 2.0, 3.0 }, new MDOVar[] { x[0], x[1], x[2], x[3] });
 57                model.AddConstr(c0, MDO.GREATER_EQUAL, 1.0, "c0");
 58
 59                // Add linear constraint c1: 1 x0 - 1 x2 + 6 x3 = 1
 60                MDOExpr c1 = new MDOExpr();
 61                c1.AddTerms(new double[] { 1.0, -1.0, 6.0 }, new MDOVar[] { x[0], x[2], x[3] });
 62                model.AddConstr(c1, MDO.EQUAL, 1.0, "c1");
 63
 64                // Add second-order cone constraint c2: x_1^2 + x_2^2 - x_4^2 <= 0
 65                MDOQuadExpr c2 = new MDOQuadExpr();
 66                // This constraint has no linear part. We only add the quadratic terms.
 67                c2.AddTerms(
 68                    new double[] { 1.0, 1.0, -1.0 },      // values
 69                    new MDOVar[] { x[1], x[2], x[4] },     // first variable indices
 70                    new MDOVar[] { x[1], x[2], x[4] }      // second variable indices
 71                );
 72                model.AddQConstr(c2, MDO.LESS_EQUAL, 0.0, "c2");
 73
 74                // Step 2. Solve the problem.
 75                model.Optimize();
 76
 77                // Step 3. Retrieve model status and solution.
 78                // For MIP(MILP,MIQP, MIQCP) problems, if the solving process
 79                // terminates early due to reasons such as timeout or interruption,
 80                // the model status will indicate termination by timeout (or
 81                // interruption, etc.). However, suboptimal solutions may still
 82                // exist, making it necessary to check the SolCount property.
 83                if (model.Get(MDO.IntAttr.Status) == MDO.Status.OPTIMAL || model.Get(MDO.IntAttr.Status) == MDO.Status.SUB_OPTIMAL ||
 84                    model.Get(MDO.IntAttr.SolCount) > 0)
 85                {
 86                    Console.WriteLine($"Optimal objective value is: {model.Get(MDO.DoubleAttr.ObjVal)}");
 87                    Console.WriteLine("Decision variables: ");
 88                    for (int i = 0; i < 5; i++)
 89                    {
 90                        Console.WriteLine($"x[{i}] = {x[i].Get(MDO.DoubleAttr.X)}");
 91                    }
 92                }
 93                else
 94                {
 95                    Console.WriteLine("No feasible solution.");
 96                }
 97            }
 98            catch (MDOException e)
 99            {
100                Console.WriteLine("Received Mindopt exception.");
101                Console.WriteLine(" - Code          : " + e.ErrorCode);
102                Console.WriteLine(" - Reason        : " + e.Message);
103            }
104            catch (Exception e)
105            {
106                Console.WriteLine("Received exception.");
107                Console.WriteLine(" - Reason        : " + e.Message);
108            }
109            finally
110            {
111                // Step 4. Free the model.
112                model.Dispose();
113                env.Dispose();
114            }
115        }
116    }
117}