5.4.5. Python 的SDP建模与优化

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

5.4.5.1. SDP示例1

首先,需要引入 Python 包:

23from mindoptpy import *

第一步:创建优化模型:

先建立一空的MindOpt模型。

30    # Step 1. Create a model and change the parameters.
31    model = MdoModel()

第二步:SDP模型输入

接着,我们将目标函数改为maximization,并新增 \(\mathbf{X}\) 对称矩阵变量。

34        # Step 2. Input model.
35        # Change to maximization problem.
36        model.set_int_attr(MDO_INT_ATTR.MIN_SENSE, 0)
37        
38        # Add matrix variable. 
39        model.add_sym_mat(3, "X")

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

41        # Input objective coefficients. 
42        model.replace_sym_mat_objs(0, [ 0, 0, 1, 2 ], [ 0, 2, 1, 2], [ -3.0, 1.0, -2.0, -3.0])

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

44        # Input first constraint. 
45        model.add_cons(1.0, 1.0, name="c0")
46        model.replace_sym_mat_elements(0, 0, [ 0, 2, 1, 2 ], [ 0, 0, 1, 2 ], [ 3.0, 1.0, 4.0, 5.0 ])

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

48        # Step 3. Solve the problem and populate the result.
49        model.solve_prob()
50        model.display_results()

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

54            print("Optimizer terminated with an OPTIMAL status (code {0}).".format(status_code))
55            print(" - Primal objective : {:8.6f}".format(model.get_real_attr(MDO_REAL_ATTR.PRIMAL_OBJ_VAL)))

最后,我们利用 mindoptpy.MdoModel.get_real_attr_sym_mat() 来取得 \(\mathbf{X}\) 的变量值。其中, 第一个参数定义 属性值 (附注: mindoptpy.MdoModel.get_real_attr_sym_mat() 为泛用函数,因此必须配合 属性值来定义函数的使用目的;MDO_REAL_ATTR.SYM_MAT_PRIMAL_SOLN 定义目的为 获取(对称矩阵)变量的数值),第二个参数为: 矩阵的索引值 (此处为0),第三第和第四 个参数为 矩阵中需要回传的元素其对应的行和列之索引。返回值soln则为矩阵中对应的元素数值。

57            soln = model.get_real_attr_sym_mat(MDO_REAL_ATTR.SYM_MAT_PRIMAL_SOLN, 0, 
58                [i * 3 + j for i in range(3) for j in range(3)], 
59                [j * 3 + i for i in range(3) for j in range(3)])           

文件链接 mdo_sdo_ex1.py 提供了完整源代码:

 1"""
 2/**
 3 *  Description
 4 *  -----------
 5 *
 6 *  Semidefinite optimization (row-wise input).
 7 *
 8 *  Formulation
 9 *  -----------
10 *
11 *  Maximize
12 *  obj: tr(C X)
13 *
14 *  Subject To
15 *    c0 : tr(A X) = 1
16 *  Matrix
17 *    C = [ -3  0  1 ]  A = [ 3 0 1 ]
18 *        [  0 -2  0 ]      [ 0 4 0 ]
19 *        [  1  0 -3 ]      [ 1 0 5 ]
20 *  End
21 */
22 """
23from mindoptpy import *
24
25
26if __name__ == "__main__":
27
28    MDO_INFINITY = MdoModel.get_infinity()
29
30    # Step 1. Create a model and change the parameters.
31    model = MdoModel()
32
33    try:
34        # Step 2. Input model.
35        # Change to maximization problem.
36        model.set_int_attr(MDO_INT_ATTR.MIN_SENSE, 0)
37        
38        # Add matrix variable. 
39        model.add_sym_mat(3, "X")
40
41        # Input objective coefficients. 
42        model.replace_sym_mat_objs(0, [ 0, 0, 1, 2 ], [ 0, 2, 1, 2], [ -3.0, 1.0, -2.0, -3.0])
43
44        # Input first constraint. 
45        model.add_cons(1.0, 1.0, name="c0")
46        model.replace_sym_mat_elements(0, 0, [ 0, 2, 1, 2 ], [ 0, 0, 1, 2 ], [ 3.0, 1.0, 4.0, 5.0 ])
47       
48        # Step 3. Solve the problem and populate the result.
49        model.solve_prob()
50        model.display_results()
51
52        status_code, status_msg = model.get_status()
53        if status_msg == "OPTIMAL":
54            print("Optimizer terminated with an OPTIMAL status (code {0}).".format(status_code))
55            print(" - Primal objective : {:8.6f}".format(model.get_real_attr(MDO_REAL_ATTR.PRIMAL_OBJ_VAL)))
56            
57            soln = model.get_real_attr_sym_mat(MDO_REAL_ATTR.SYM_MAT_PRIMAL_SOLN, 0, 
58                [i * 3 + j for i in range(3) for j in range(3)], 
59                [j * 3 + i for i in range(3) for j in range(3)])           
60            print("X = ")
61            for i in range(3):
62                print(" (", end="") 
63                for j in range(3):
64                    print(" {0:8.6f}".format(soln[i * 3 + j]), end=""), 
65                print(" )") 
66
67        else:
68            print("Optimizer terminated with a(n) {0} status (code {1}).".format(status_msg, status_code))
69
70    except MdoError as e:
71        print("Received Mindopt exception.")
72        print(" - Code          : {}".format(e.code))
73        print(" - Reason        : {}".format(e.message))
74    except Exception as e:
75        print("Received exception.")
76        print(" - Reason        : {}".format(e))
77    finally:
78        # Step 4. Free the model.
79        model.free_mdl()

5.4.5.2. SDP示例2

首先,需要引入 Python 包:

30from mindoptpy import *

第一步:创建MindOpt模型

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

37    # Step 1. Create a model and change the parameters.
38    model = MdoModel()

第二步:SDP模型输入

接着,我们将目标函数改为maximization,并新增 \(x_0\)\(x_1\) 两个变量,以及 \(\mathbf{X}_0\)\(\mathbf{X}_1\) 两个对称矩阵变量。

41        # Step 2. Input model.
42        # Change to maximization problem.
43        model.set_int_attr(MDO_INT_ATTR.MIN_SENSE, 0)
44        
45        # Add variables.
46        x = []
47        x.append(model.add_var(0.0, MDO_INFINITY, 0.0, None, "x0", False))
48        x.append(model.add_var(0.0, MDO_INFINITY, 0.0, None, "x1", False))
49        # Add matrix variables. 
50        model.add_sym_mats([ 2, 3 ], [ "X0", "X1" ]);

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

52        # Input objective coefficients. 
53        model.replace_sym_mat_objs(0, [ 0, 1, 1 ], [ 0, 0, 1 ], [ 2.0, 1.0, 2.0 ]);

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

54        model.replace_sym_mat_objs(1, [ 0, 0, 1, 2 ], [ 0, 2, 1, 2], [ 3.0, 1.0, 2.0, 3.0]);

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

56        # Input first constraint. 
57        model.add_cons(1.0 * x[0] == 1.0, "c0");
58        model.replace_sym_mat_elements(0, 0, [ 0, 1, 1 ], [ 0, 0, 1 ], [ 3.0, 1.0, 3.0 ]);

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

60        # Input second constraint. 
61        model.add_cons(1.0 * x[1] == 2.0, "c1");
62        model.replace_sym_mat_elements(1, 1, [ 0, 2, 1, 2 ], [ 0, 0, 1, 2 ], [ 3.0, 1.0, 4.0, 5.0 ]);

第三步:求解SDP模型

模型输入后,我们接着用 mindoptpy.MdoModel.solve_prob() 来求解问题,并用 mindoptpy.MdoModel.display_results() 呈现求解结果。

64        # Step 3. Solve the problem and populate the result.
65        model.solve_prob()
66        model.display_results()

第四步: 取得SDP模型的解

我们利用泛用函数 mindoptpy.MdoModel.get_real_attr() 来 取得最优的的目标函数值,并用泛用函数 mindoptpy.MdoModel.get_real_attr_array() 来 取得 \(x_0\)\(x_1\) 两个变量值;此处第一个参数定义了 属性值 (附注: mindoptpy.MdoModel.get_real_attr_array() 为泛用函数, 因此必须配合属性值来定义函数的使用目的;MDO_REAL_ATTR.PRIMAL_SOLN定义目的为获取(线性)变量的数值), 第二和第三个参数分别代表第一个索引和最后一个索引 加上1, 也就是 \(x_j,\) 其中 \(0 \leq j < 2\)

71            print(" - Primal objective : {:8.6f}".format(model.get_real_attr(MDO_REAL_ATTR.PRIMAL_OBJ_VAL)))
72            soln = model.get_real_attr_array(MDO_REAL_ATTR.PRIMAL_SOLN, 0, 2)

最后,我们利用 mindoptpy.MdoModel.get_real_attr_sym_mat() 来取得 \(\mathbf{X}_0\) 的变量值。其中, 第一个参数定义 属性值 (附注: mindoptpy.MdoModel.get_real_attr_sym_mat() 为泛用函数, 因此必须配合属性值来定义函数的使用目的;MDO_REAL_ATTR.SYM_MAT_PRIMAL_SOLN 定义目的为获取(对称矩阵)变量的数值),第二个参数为: 矩阵的索引值 (此处为0), 第三第和第四个参数为 矩阵中需要回传的元素其对应的行和列之索引 。 返回值soln则为矩阵中对应的元素数值。

76            soln = model.get_real_attr_sym_mat(MDO_REAL_ATTR.SYM_MAT_PRIMAL_SOLN, 0, 
77                [i * 2 + j for i in range(2) for j in range(2)], 
78                [j * 2 + i for i in range(2) for j in range(2)])           

文件链接 mdo_sdo_ex2.py 提供了完整源代码:

  1"""
  2/**
  3 *  Description
  4 *  -----------
  5 *
  6 *  Semidefinite optimization (row-wise input).
  7 *
  8 *  Formulation
  9 *  -----------
 10 *
 11 *  Maximize
 12 *  obj: tr(C0 X0)   + tr(C1 X1)    + 0 x0 + 0 x1
 13 *
 14 *  Subject To
 15 *   c0 : tr(A00 X0)                + 1 x0        = 1
 16 *   c1 :              tr(A11 X1)          + 1 x1 = 2
 17 *  Bounds
 18 *    0 <= x0
 19 *    0 <= x1
 20 *  Matrix
 21 *    C0 =  [ 2 1 ]   A00 = [ 3 1 ]
 22 *          [ 1 2 ]         [ 1 3 ]
 23 *
 24 *    C1 = [ 3 0 1 ]  A11 = [ 3 0 1 ]
 25 *         [ 0 2 0 ]        [ 0 4 0 ]
 26 *         [ 1 0 3 ]        [ 1 0 5 ]
 27 *  End
 28 */
 29 """
 30from mindoptpy import *
 31
 32
 33if __name__ == "__main__":
 34
 35    MDO_INFINITY = MdoModel.get_infinity()
 36
 37    # Step 1. Create a model and change the parameters.
 38    model = MdoModel()
 39
 40    try:
 41        # Step 2. Input model.
 42        # Change to maximization problem.
 43        model.set_int_attr(MDO_INT_ATTR.MIN_SENSE, 0)
 44        
 45        # Add variables.
 46        x = []
 47        x.append(model.add_var(0.0, MDO_INFINITY, 0.0, None, "x0", False))
 48        x.append(model.add_var(0.0, MDO_INFINITY, 0.0, None, "x1", False))
 49        # Add matrix variables. 
 50        model.add_sym_mats([ 2, 3 ], [ "X0", "X1" ]);
 51
 52        # Input objective coefficients. 
 53        model.replace_sym_mat_objs(0, [ 0, 1, 1 ], [ 0, 0, 1 ], [ 2.0, 1.0, 2.0 ]);
 54        model.replace_sym_mat_objs(1, [ 0, 0, 1, 2 ], [ 0, 2, 1, 2], [ 3.0, 1.0, 2.0, 3.0]);
 55
 56        # Input first constraint. 
 57        model.add_cons(1.0 * x[0] == 1.0, "c0");
 58        model.replace_sym_mat_elements(0, 0, [ 0, 1, 1 ], [ 0, 0, 1 ], [ 3.0, 1.0, 3.0 ]);
 59
 60        # Input second constraint. 
 61        model.add_cons(1.0 * x[1] == 2.0, "c1");
 62        model.replace_sym_mat_elements(1, 1, [ 0, 2, 1, 2 ], [ 0, 0, 1, 2 ], [ 3.0, 1.0, 4.0, 5.0 ]);
 63       
 64        # Step 3. Solve the problem and populate the result.
 65        model.solve_prob()
 66        model.display_results()
 67
 68        status_code, status_msg = model.get_status()
 69        if status_msg == "OPTIMAL":
 70            print("Optimizer terminated with an OPTIMAL status (code {0}).".format(status_code))
 71            print(" - Primal objective : {:8.6f}".format(model.get_real_attr(MDO_REAL_ATTR.PRIMAL_OBJ_VAL)))
 72            soln = model.get_real_attr_array(MDO_REAL_ATTR.PRIMAL_SOLN, 0, 2)
 73            for index, value in enumerate(soln):
 74                print("x[{0}]={1:8.6f}".format(index, value))
 75            
 76            soln = model.get_real_attr_sym_mat(MDO_REAL_ATTR.SYM_MAT_PRIMAL_SOLN, 0, 
 77                [i * 2 + j for i in range(2) for j in range(2)], 
 78                [j * 2 + i for i in range(2) for j in range(2)])           
 79            print("X[0] = ")
 80            for i in range(2):
 81                print(" (", end="") 
 82                for j in range(2):
 83                    print(" {0:8.6f}".format(soln[i * 2 + j]), end=""), 
 84                print(" )") 
 85
 86            soln = model.get_real_attr_sym_mat(MDO_REAL_ATTR.SYM_MAT_PRIMAL_SOLN, 1, 
 87                [i * 3 + j for i in range(3) for j in range(3)], 
 88                [j * 3 + i for i in range(3) for j in range(3)])
 89            print("X[1] = ")
 90            for i in range(3):
 91                print(" (", end=""), 
 92                for j in range(3):
 93                    print(" {0:8.6f}".format(soln[i * 3 + j]), end=""),
 94                print(" )") 
 95
 96        else:
 97            print("Optimizer terminated with a(n) {0} status (code {1}).".format(status_msg, status_code))
 98
 99    except MdoError as e:
100        print("Received Mindopt exception.")
101        print(" - Code          : {}".format(e.code))
102        print(" - Reason        : {}".format(e.message))
103    except Exception as e:
104        print("Received exception.")
105        print(" - Reason        : {}".format(e))
106    finally:
107        # Step 4. Free the model.
108        model.free_mdl()