Pyomo接口

Pyomo 是基于Python编程语言的开源优化建模语言, 它提供了丰富的优化相关功能,并已被许多研究人员用于解决复杂的实际应用问题,感兴趣的用户可以查看 谁在使用Pyomo? 了解更多信息。本章介绍了如何在 Pyomo环境下使用杉数求解器。

安装说明

在Pyomo环境下调用杉数求解器进行求解之前,用户需要正确安装与配置Pyomo和杉数求解器。 Pyomo目前支持Python 2.7和3.6-3.10版本,用户可以从 Anaconda发行版 或者 Python官方发行版 下载并安装Python。我们推荐用户 安装Anaconda发行版,因为它对Python新手使用更加友好与方便。

使用conda安装

我们推荐安装了Anaconda发行版Python的用户使用它自带的 conda 工具安装Pyomo, 在Windows的命令行或者Linux和MacOS上的终端中执行下述命令即可:

conda install -c conda-forge pyomo

Pyomo也集成了一些可选的第三方Python包扩展其优化相关功能,可通过如下命令进行安装:

conda install -c conda-forge pyomo.extras

使用pip安装

用户也可以通过标准的 pip 工具安装Pyomo,在Windows的命令行或者Linux和MacOS的终端中 执行下述命令即可:

pip install pyomo

如果用户在安装Pyomo过程中遇到了任何问题,可以查阅 如何安装Pyomo 以了解更多信息。 关于安装与配置杉数求解器,请用户查看文档中的 如何安装杉数求解器 章节 了解详细步骤。

使用示例

我们将通过求解 AMPL接口-使用示例 章节中描述的例子来介绍 如何在Pyomo中调用杉数求解器进行优化求解。如果用户想了解更详细的Pyomo使用说明,可以参考 Pyomo官方文档 进行学习。

抽象模型

Pyomo主要提供了两种方式对其支持的各种问题类型进行建模,本部分将介绍使用抽象模型求解上述问题的方式。

使用Pyomo对上述问题进行建模与求解的源代码 pydiet_abstract.py 如下, 详见 代码 9 :

代码 9 pydiet_abstract.py
 1# The code is adopted from:
 2#
 3# https://github.com/Pyomo/pyomo/blob/master/examples/pyomo/amplbook2/diet.py
 4#
 5# with some modification by developer of the Cardinal Optimizer
 6
 7from pyomo.core import *
 8
 9model = AbstractModel()
10
11model.NUTR = Set()
12model.FOOD = Set()
13
14model.cost  = Param(model.FOOD, within=NonNegativeReals)
15model.f_min = Param(model.FOOD, within=NonNegativeReals)
16
17model.f_max = Param(model.FOOD)
18model.n_min = Param(model.NUTR, within=NonNegativeReals)
19model.n_max = Param(model.NUTR)
20model.amt   = Param(model.NUTR, model.FOOD, within=NonNegativeReals)
21
22def Buy_bounds(model, i):
23  return (model.f_min[i], model.f_max[i])
24model.Buy = Var(model.FOOD, bounds=Buy_bounds)
25
26def Objective_rule(model):
27  return sum_product(model.cost, model.Buy)
28model.totalcost = Objective(rule=Objective_rule, sense=minimize)
29
30def Diet_rule(model, i):
31  expr = 0
32
33  for j in model.FOOD:
34    expr = expr + model.amt[i, j] * model.Buy[j]
35
36  return (model.n_min[i], expr, model.n_max[i])
37model.Diet = Constraint(model.NUTR, rule=Diet_rule)

该问题的数据文件 pydiet_abstract.dat代码 10 :

代码 10 pydiet_abstract.dat
 1# The data is adopted from:
 2# 
 3# https://github.com/Pyomo/pyomo/blob/master/examples/pyomo/amplbook2/diet.dat
 4#
 5# with some modification by developer of the Cardinal Optimizer
 6
 7data;
 8
 9set NUTR := A B1 B2 C ;
10set FOOD := BEEF CHK FISH HAM MCH MTL SPG TUR ;
11
12param:   cost  f_min  f_max :=
13  BEEF   3.19    0     100
14  CHK    2.59    0     100
15  FISH   2.29    0     100
16  HAM    2.89    0     100
17  MCH    1.89    0     100
18  MTL    1.99    0     100
19  SPG    1.99    0     100
20  TUR    2.49    0     100 ;
21
22param:   n_min  n_max :=
23   A      700   10000
24   C      700   10000
25   B1     700   10000
26   B2     700   10000 ;
27
28param amt (tr):
29           A    C   B1   B2 :=
30   BEEF   60   20   10   15
31   CHK     8    0   20   20
32   FISH    8   10   15   10
33   HAM    40   40   35   10
34   MCH    15   35   15   15
35   MTL    70   30   15   15
36   SPG    25   50   25   15
37   TUR    60   20   15   10 ;

通过在Windows的命令行或者Linux和Mac的终端中输入下述命令即可实现在Pyomo中调用杉数求解器 进行求解:

pyomo solve --solver=coptampl pydiet_abstract.py pydiet_abstract.dat

求解过程中,Pyomo在屏幕中输出如下信息:

[    0.00] Setting up Pyomo environment
[    0.00] Applying Pyomo preprocessing actions
[    0.00] Creating model
[    0.01] Applying solver
[    0.05] Processing results
    Number of solutions: 1
    Solution Information
      Gap: None
      Status: optimal
      Function Value: 88.19999999999999
    Solver results file: results.yml
[    0.05] Applying Pyomo postprocessing actions
[    0.05] Pyomo Finished

求解完成后,Pyomo将求解结果输出到如下 results.yml 中:

# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem: 
- Lower bound: -inf
  Upper bound: inf
  Number of objectives: 1
  Number of constraints: 4
  Number of variables: 8
  Sense: unknown
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver: 
- Status: ok
  Message: COPT-AMPL\x3a optimal solution; objective 88.2, iterations 1
  Termination condition: optimal
  Id: 0
  Error rc: 0
  Time: 0.03171110153198242
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution: 
- number of solutions: 1
  number of solutions displayed: 1
- Gap: None
  Status: optimal
  Message: COPT-AMPL\x3a optimal solution; objective 88.2, iterations 1
  Objective:
    totalcost:
      Value: 88.19999999999999
  Variable:
    Buy[MCH]:
      Value: 46.666666666666664
  Constraint: No values

分析结果知,杉数求解器找到了最优解约为88.2个单位,此时应当购买约46.67个单位的食物MCH。

具象模型

除了使用抽象模型进行建模,Pyomo还支持具象模型建模方式,本部分将介绍使用具象模型对上述问题进行 建模并求解。

具象模型可以使用 "Direct""Persistent" 接口方式进行求解。该方式依赖杉数求解器的 Pyomo插件文件 copt_pyomo.py,文件位于安装包的 "lib/pyomo" 子文件夹。使用该插件 需要将 copt_pyomo.py 文件复制到待求解Python程序的相同目录下,且已正确安装了相应 Python版本的杉数求解器Python接口。

使用Pyomo建模求解的源代码 pydiet_concrete.py代码 11:

代码 11 pydiet_concrete.py
  1# The code is adopted from:
  2#
  3# https://github.com/Pyomo/pyomo/blob/master/examples/pyomo/amplbook2/diet.py
  4#
  5# with some modification by developer of the Cardinal Optimizer
  6
  7from __future__ import print_function, division
  8
  9import pyomo.environ as pyo
 10import pyomo.opt as pyopt
 11
 12from copt_pyomo import *
 13
 14# Nutrition set
 15NUTR = ["A", "C", "B1", "B2"]
 16# Food set
 17FOOD = ["BEEF", "CHK", "FISH", "HAM", "MCH", "MTL", "SPG", "TUR"]
 18
 19# Price of foods
 20cost = {"BEEF": 3.19, "CHK": 2.59, "FISH": 2.29, "HAM": 2.89, "MCH": 1.89,
 21        "MTL":  1.99, "SPG": 1.99, "TUR":  2.49}
 22# Nutrition of foods
 23amt = {"BEEF": {"A": 60, "C": 20, "B1": 10, "B2": 15},
 24       "CHK":  {"A": 8,  "C": 0,  "B1": 20, "B2": 20},
 25       "FISH": {"A": 8,  "C": 10, "B1": 15, "B2": 10},
 26       "HAM":  {"A": 40, "C": 40, "B1": 35, "B2": 10},
 27       "MCH":  {"A": 15, "C": 35, "B1": 15, "B2": 15},
 28       "MTL":  {"A": 70, "C": 30, "B1": 15, "B2": 15},
 29       "SPG":  {"A": 25, "C": 50, "B1": 25, "B2": 15},
 30       "TUR":  {"A": 60, "C": 20, "B1": 15, "B2": 10}}
 31
 32# The "diet problem" using ConcreteModel
 33model = pyo.ConcreteModel()
 34
 35model.NUTR = pyo.Set(initialize=NUTR)
 36model.FOOD = pyo.Set(initialize=FOOD)
 37
 38model.cost = pyo.Param(model.FOOD, initialize=cost)
 39
 40def amt_rule(model, i, j):
 41  return amt[i][j]
 42model.amt  = pyo.Param(model.FOOD, model.NUTR, initialize=amt_rule)
 43
 44model.f_min = pyo.Param(model.FOOD, default=0)
 45model.f_max = pyo.Param(model.FOOD, default=100)
 46
 47model.n_min = pyo.Param(model.NUTR, default=700)
 48model.n_max = pyo.Param(model.NUTR, default=10000)
 49
 50def Buy_bounds(model, i):
 51  return (model.f_min[i], model.f_max[i])
 52model.buy = pyo.Var(model.FOOD, bounds=Buy_bounds)
 53
 54def Objective_rule(model):
 55  return pyo.sum_product(model.cost, model.buy)
 56model.totalcost = pyo.Objective(rule=Objective_rule, sense=pyo.minimize)
 57
 58def Diet_rule(model, j):
 59  expr = 0
 60
 61  for i in model.FOOD:
 62    expr = expr + model.amt[i, j] * model.buy[i]
 63
 64  return (model.n_min[j], expr, model.n_max[j])
 65model.Diet = pyo.Constraint(model.NUTR, rule=Diet_rule)
 66
 67# Reduced costs of variables
 68model.rc = pyo.Suffix(direction=pyo.Suffix.IMPORT)
 69
 70# Activities and duals of constraints
 71model.slack = pyo.Suffix(direction=pyo.Suffix.IMPORT)
 72model.dual = pyo.Suffix(direction=pyo.Suffix.IMPORT)
 73
 74# Use 'copt_direct' solver to solve the problem
 75solver = pyopt.SolverFactory('copt_direct')
 76
 77# Use 'copt_persistent' solver to solve the problem
 78# solver = pyopt.SolverFactory('copt_persistent')
 79# solver.set_instance(model)
 80
 81results = solver.solve(model, tee=True)
 82
 83# Check result
 84print("")
 85if results.solver.status == pyopt.SolverStatus.ok and \
 86   results.solver.termination_condition == pyopt.TerminationCondition.optimal:
 87  print("Optimal solution found")
 88else:
 89  print("Something unexpected happened: ", str(results.solver))
 90
 91print("")
 92print("Optimal objective value:")
 93print("  totalcost: {0:6f}".format(pyo.value(model.totalcost)))
 94
 95print("")
 96print("Variables solution:")
 97for i in FOOD:
 98  print("  buy[{0:4s}] = {1:9.6f} (rc: {2:9.6f})".format(i, \
 99                                                  pyo.value(model.buy[i]), \
100                                                  model.rc[model.buy[i]]))
101
102print("")
103print("Constraint solution:")
104for i in NUTR:
105  print("  diet[{0:2s}] = {1:12.6f} (dual: {2:9.6f})".format(i, \
106                                                      model.slack[model.Diet[i]], \
107                                                      model.dual[model.Diet[i]]))

然后,在Windows的命令行或者Linux和MacOS的终端中执行下述命令进行求解:

python pydiet_concrete.py

求解完成后,屏幕输出如下内容:

Optimal solution found
Objective:
  totalcost: 88.200000
Variables:
  buy[BEEF] = 0.000000
  buy[CHK ] = 0.000000
  buy[FISH] = 0.000000
  buy[HAM ] = 0.000000
  buy[MCH ] = 46.666667
  buy[MTL ] = 0.000000
  buy[SPG ] = 0.000000
  buy[TUR ] = 0.000000

结果显示,杉数求解器找到了最优解约88.2个单位,此时应当购买约46.67个单位的食物MCH。