7.1. MindOpt APL 的建模与优化

MindOpt建模语言(MindOpt Algebraic Programming Language, MindOpt APL, 简称为MAPL)是 MindOpt 团队研发的一种通用代数建模语言,可帮助用户高效地构建数学模型创建与求解器调用求解的链路。其特点包括:

  • 语法简洁且自然,贴近数学语言。用MAPL描述数学规划模型与用数学公式进行描述非常类似。

  • 支持线性规划、整数规划、0-1规划、二次规划、非线性规划、多目标规划、约束规划等多类问题的建模与预处理。

  • 支持在MindOpt Solver等多种优化求解器之间进行切换并完成优化求解,结果比对方便。

7.1.1. 在云上建模求解平台上使用 MindOpt APL

MindOpt云上建模求解平台(MindOpt Studio) 是一个供优化技术学习者、开发者使用的开发平台。在该平台上,您可以查看丰富的数学建模案例,并在web端的Notebook中开发自己的优化模型。

云上建模求解平台提供已部署好的最新版本的 MindOpt APL 建模语言以及 MindOpt 求解器,无需安装,打开即用。平台具备完善的项目管理、机器资源管理、文件上传和下载等多项功能,为建模优化技术的学习及运筹优化项目的协作开发提供了很好的支持。

您可以通过以下链接获取更多云上建模平台的使用帮助并查看 MindOpt APL 建模语言案例:

7.1.2. 在本地安装使用MindOpt APL

您可以 联系我们,申请下载 MindOpt APL 的本地版本。我们目前提供适用于Linux, macOS等平台的安装包,相关细节请参考 本地下载和安装MindOpt APL 的本地安装包中已内置了最新版 MindOpt 求解器,因此无需另行下载安装。

7.1.3. 使用 MindOpt APL 设置 MindOpt 的求解参数

MindOpt 优化求解器内提供了大量参数供用户设置,例如选择不同的求解算法,设置线程数等。在 MindOpt APL 中,这些求解器参数以字符串的形式传入,其基本示例如下:

option filepath path/to/your/model/folder;     # specify the path to the model file (use `./` as default)
option solverpath path/to/your/solver/folder;  # specify the path to the solver (use `./` and the installed solvers' folder as default)
option solver mindopt;                         # specify the solver to use
option mindopt_options 'print=1 method=-1 num_threads=8'; # specify the settings for the chosen solver

关于 MindOpt 的全部可配置参数可参考 参数 一节。

7.1.4. MindOpt APL 建模求解示例

本节中,我们以数学规划当中经典的 Diet 问题 为例,介绍在MindOpt Studio上使用 MindOpt APL 建模并调用 MindOpt 求解器求解该问题的整体流程。有关 MindOpt APL 语法的详细介绍,请参考 MindOpt APL v2.0 语法手册

7.1.4.1. 问题描述:

食谱中有八种食物, 分别是牛肉(BEEF), 鸡肉(CHK), 鱼肉(FISH), 火腿(HAM), 芝士通心粉(MCH), 肉饼(MTL), 意大利面 (SPG), 火鸡肉 (TUR)。人们每天要摄入的四种维生素,分别是维生素A, C, B1, B。已知每克食物所含有的维生素含量如图所示:

Nutrition of foods

Food

A

C

B1

B2

BEEF

60

20

10

15

CHK

8

0

20

20

FISH

8

10

15

10

HAM

40

40

35

10

MCH

15

35

15

15

MTL

70

30

15

15

SPG

25

50

25

15

TUR

60

20

15

10

并且已知采购每克食物所花费的金额如下:

Prices of foods

Food

Price

BEEF

3.19

CHK

2.59

FISH

2.29

HAM

2.89

MCH

1.89

MTL

1.99

SPG

1.99

TUR

2.49

假设每人每天对每种食物的摄入量不得超过100克,对每种维生素的摄入量不得少于700 但不能超过10000。请问我们该如何做出食物摄入的决策,以使得在满足以上要求的前提下,能够最小化购买食物的开销?

该问题的代数数学模型如下:

\[\begin{split}\begin{eqnarray} &\min & \sum_{j \in J} \mbox{cost}_j \times \mbox{buy}_j \\ &\mbox{s.t.} & \mbox{n_min}_i \leq \sum_{j \in J} \mbox{amt}_{i,j} \times \mbox{buy}_j \leq \mbox{n_max}_i, \forall i \in I, \\ & & \mbox{f_min}_j \leq \mbox{buy}_j \leq \mbox{f_max}_j, \forall j \in J. \end{eqnarray}\end{split}\]

其中

  • \(\mbox{buy}\) 为决策变量,

  • \(\mbox{f_min}\)\(\mbox{f_max}\) 分别为 \(\mbox{buy}\) 的下界和上界,

  • \(\mbox{cost}\) 是目标函数中的系数,

  • \(\mbox{amt}\) 是约束矩阵,

  • \(\mbox{n_min}\)\(\mbox{n_max}\) 分别为是约束的下界和上界。

7.1.4.2. MindOpt APL 建模

接下来,我们在MindOpt Studio上使用 MindOpt APL 对上述问题进行建模。首先进入 云上建模平台 上方菜单的”项目”,点击右上角的”创建新项目”创建一个名称为Diet的项目。

../_images/yunopt-project.png

在新项目界面,点击 Notebook 图标,进入如下所示的IDE开发平台。在开发平台的左上角,点击 + 按钮打开 Launcher 窗口。新建一个以 MindOpt APL 为内核的Notebook(图中箭头所指向)后会自动生成一个.ipynb文件。Notebook的内核Kernel可以在编辑器右上角进行切换。

../_images/yunopt-ide.png

接下来,您可通过两种方式对上述问题进行建模:

  • 方式一:您可以直接在Notebook输入块中键入如下代码。请注意,MindOpt APL 每条命令的结尾要以分号结束。运行代码可点击Notebook窗口上方的三角按钮,也可以按Ctrl+Enter或者Shift+Enter组合键。

clear model;    # clear previous model

# diet.mapl
# modified based on the 'diet example' in Chapter 2 of the book "AMPL: A Modeling Language for Mathematical
# Programming"

set NUTR := { "A", "B1", "B2", "C" };
set FOOD := {"BEEF", "CHK", "FISH", "HAM", "MCH", "MTL", "SPG", "TUR"} ;
set F:= {"cost", "f_min", "f_max"};
set N:= {"n_min", "n_max"};

param data1[FOOD * F] :=
        | "cost"  , "f_min" , "f_max" |
|"BEEF" |  3.19   ,  0      ,  100    |
|"CHK"  |  2.59   ,  0      ,  100    |
|"FISH" |  2.29   ,  0      ,  100    |
|"HAM"  |  2.89   ,  0      ,  100    |
|"MCH"  |  1.89   ,  0      ,  100    |
|"MTL"  |  1.99   ,  0      ,  100    |
|"SPG"  |  1.99   ,  0      ,  100    |
|"TUR"  |  2.49   ,  0      ,  100    |;

param data2[NUTR * N] :=
      | "n_min", "n_max"|
|"A"  |  700,     10000 |
|"C"  |  700,     10000 |
|"B1" |  700,     10000 |
|"B2" |  700,     10000 |;

param amt[FOOD * NUTR] :=
        | "A",  "C",  "B1",  "B2"|
|"BEEF" |  60,   20,   10,    15 |
|"CHK"  |  8,    0,    20,    20 |
|"FISH" |  8,    10,   15,    10 |
|"HAM"  |  0,    40,   35,    10 |
|"MCH"  |  15,   35,   0,     15 |
|"MTL"  |  70,   30,   15,    0  |
|"SPG"  |  0,    50,   25,    15 |
|"TUR"  |  60,   0,    15,    0  |;

var x[j in FOOD] >= data1[j, "f_min"] <= data1[j, "f_max"];

minimize Total_Cost:  sum {j in FOOD}  data1[j, "cost"] * x[j];

subto Diet: forall {i in NUTR }
        data2[i, "n_min"] <= sum {j in FOOD} amt[j, i] * x[j] <= data2[i, "n_max"];

print NUTR;
print N;

运行上述代码后得到如下输出,表示问题的建模已经完成。

List: |1|{<"A">,<"B1">,<"B2">,<"C">}
List: |1|{<"n_min">,<"n_max">}
  • 方式二:除了上述方式外,您也可以将建模代码单独保存为一个文件diet.mapl,而后在Notebook中输入下面的命令

model <file_path>/diet.mapl;

运行后将会得到同样的结果。在处理复杂模型时,采用该方式建模往往更加方便。

7.1.4.3. 调用 MindOpt 求解

接下来,我们使用 MindOpt 求解这个数学模型。在Notebook的代码块中输入如下命令

option solver mindopt;   # specify the solver (with MindOpt set as default)
solve;                   # solve the problem

上述命令会调用平台上预装的 MindOpt 求解器。有关 MindOpt APL 建模语言同 MindOpt 求解器间交互指令的更多细节,可参考 命令行指令

求解结果显示如下:

Running mindoptampl
wantsol=1
MindOpt Version 0.24.1 (Build date: 20230423)
Copyright (c) 2020-2023 Alibaba Cloud.

Start license validation (current time : 07-JUL-2023 11:20:32).
License validation terminated. Time : 0.007s

Only one thread allowed -- optimize with Simplex method.
Model summary.
 - Num. variables     : 8
 - Num. constraints   : 4
 - Num. nonzeros      : 25
 - Bound range        : [1.0e+02,1.0e+04]
 - Objective range    : [1.9e+00,3.2e+00]
 - Matrix range       : [8.0e+00,7.0e+01]

Presolver started.
Presolver terminated. Time : 0.002s

Simplex method started.
Model fingerprint: iVWY5d2diV2dvd3Y

    Iteration       Objective       Dual Inf.     Primal Inf.     Time
            0     0.00000e+00      0.0000e+00      2.6250e+02     0.02s
            4     1.01011e+02      0.0000e+00      0.0000e+00     0.02s
Postsolver started.
Simplex method terminated. Time : 0.009s


OPTIMAL; objective 101.01
4 simplex iterations

Completed.

同时,在建模文件的同目录下会生成 .nl.sol 两个文件。 .nl 文件是建模的问题模型文件,.sol 文件则存储了求解结果。

../_images/yunopt-result2.png

最后,我们执行 display; 命令

print "-----------------Display---------------";
display;        # show the optimization result

print "Minimal Cost:";
print  sum {<j> in FOOD} data1[j, "cost"] * x[j];

得到如下输出结果:

-----------------Display---------------
Primal Solution:
x@BEEF   = 0.000000000000000E+00
x@CHK    = 2.497123130034522E+01
x@FISH   = 0.000000000000000E+00
x@HAM    = 0.000000000000000E+00
x@MCH    = 8.538550057537401E+00
x@MTL    = 5.316455696202531E+00
x@SPG    = 4.833141542002300E+00
x@TUR    = 0.000000000000000E+00

Dual Solution:
Diet_1   = 2.438506904487917E-02
Diet_2   = 1.852876869965478E-02
Diet_3   = 1.012172036823936E-01
Diet_4   = 1.704545454545448E-04

Minimal Cost:
101.0110471806674

其中Primal Solution是决策变量的取值,后面的Dual Solution是对偶解的值。对于该问题,上述求解结果表明,建议吃的食物是鸡肉(CHK), 芝士通心粉(MCH), 肉饼(MTL), 意大利面(SPG)。