5 min read

Pyomo 入门:用 Python 实现数学优化建模

在数据科学与工程领域,数学优化是解决资源分配、路径规划、成本最小化等问题的核心工具。而用代码高效表达优化模型,往往是落地的关键。如果你熟悉 Python,那么 Pyomo—— 这款由美国国家可再生能源实验室(NREL)主导开发的优化建模库,或许能成为你的得力助手。本文将基于《Pyomo–Optimization Modeling in Python》(Bynum et al., 2021),带你从基础到实战,掌握 Pyomo 的核心用法。

一、Pyomo 是什么?核心价值与生态

Pyomo 全称为 “Python Optimization Modeling Objects”,是一款开源的 Python 优化建模库,其核心是通过面向对象设计实现数学优化模型的灵活构建(Bynum et al., 2021)。与传统的商业代数建模语言(AML)如 AMPL、GAMS 相比,Pyomo 的优势在于:

  1. Python 原生兼容:无需学习新语言,直接用 Python 语法(列表、字典、函数)定义模型,降低学习成本;
  2. 多求解器支持:可调用 GLPK(线性规划)、IPOPT(非线性规划)、Gurobi(商业求解器)等,甚至通过 NEOS 平台使用远程求解服务(Bynum et al., 2021);
  3. AML 能力:支持用数学表达式直观描述目标函数与约束,媲美专业 AML 工具;
  4. 灵活性:适用于线性、非线性、整数规划等多种问题,支持动态参数调整与复杂数据导入(如 Excel、CSV)。

值得一提的是,Pyomo 并非孤例 —— 同类工具如 C++ 的 FlopC++、Java 的 OptimJ、Julia 的 JuMP,均采用 “编程语言 + 优化建模” 的模式,但 Pyomo 凭借 Python 的生态优势(如 Pandas 数据处理、Matplotlib 可视化),在易用性上更胜一筹(Bynum et al., 2021)。

二、Pyomo 核心:模型类型与关键组件

Pyomo 的核心是 “模型” 与 “组件”—— 模型是容器,组件则是构成优化问题的基本单元。我们先从两种核心模型类型说起,再拆解五大关键组件。

1. 两种模型类型:Concrete vs Abstract

Pyomo 支持两种模型定义方式,核心区别在于数据与模型结构的绑定时机(Bynum et al., 2021):

(1)ConcreteModel:数据先行,即时构建

适用于数据已知的场景,模型结构与数据同步定义,用 Python 原生数据结构(列表、字典)传入参数。

示例:简单线性规划

目标函数:\(\min\ x_1 + 2x_2\)

约束条件:

\(3x_1 + 4x_2 \geq 1\)

\(2x_1 + 5x_2 \geq 2\)

\(x_1, x_2 \geq 0\)

代码实现(修正文档中的语法错误后):

import pyomo.environ as pyo

\# 1. 初始化ConcreteModel

model = pyo.ConcreteModel(name="SimpleLP")

\# 2. 定义变量(Var):非负实数

model.x1 = pyo.Var(within=pyo.NonNegativeReals)

model.x2 = pyo.Var(within=pyo.NonNegativeReals)

\# 3. 定义目标函数(Objective):最小化

model.obj = pyo.Objective(expr=model.x1 + 2 \* model.x2, sense=pyo.minimize)

\# 4. 定义约束(Constraint)

model.con1 = pyo.Constraint(expr=3 \* model.x1 + 4 \* model.x2 >= 1)

model.con2 = pyo.Constraint(expr=2 \* model.x1 + 5 \* model.x2 >= 2)

\# 5. 求解(调用GLPK求解器)

solver = pyo.SolverFactory("glpk")

result = solver.solve(model)

\# 6. 验证结果与输出

pyo.assert\_optimal\_termination(result)  # 确保求解成功

print("最优解:")

print(f"x1 = {pyo.value(model.x1)}")

print(f"x2 = {pyo.value(model.x2)}")

print(f"目标函数值 = {pyo.value(model.obj)}")

(2)AbstractModel:先定结构,后传数据

适用于数据未知或需多次替换数据的场景(如批量求解不同参数的问题)。模型结构先通过 Set(集合)和 Param(参数)抽象定义,数据后续传入(Bynum et al., 2021)。

示例:参数化线性规划

目标函数:\(\min\ \sum_{i \in N} c_i x_i\)

约束条件:\(\sum_{i \in N} a_{ji} x_i \geq b_j\ (\forall j \in M)\)\(x_i \geq 0\)

代码实现:

import pyomo.environ as pyo

\# 1. 初始化AbstractModel(仅定义结构,无数据)

model = pyo.AbstractModel(name="ParametricLP")

\# 2. 定义集合(Set):索引容器

model.N = pyo.Set()  # 变量索引集合(如i=1,2)

model.M = pyo.Set()  # 约束索引集合(如j=1,2)

\# 3. 定义参数(Param):模型的输入数据

model.c = pyo.Param(model.N)  # 目标函数系数(对应c\_i)

model.a = pyo.Param(model.M, model.N)  # 约束系数(对应a\_ji)

model.b = pyo.Param(model.M)  # 约束右侧值(对应b\_j)

\# 4. 定义变量与目标函数(依赖Set/Param,无需具体数据)

model.x = pyo.Var(model.N, within=pyo.NonNegativeReals)

def obj\_rule(mdl):

    return sum(mdl.c\[i] \* mdl.x\[i] for i in mdl.N)  # 列表推导式(Pyomo推荐写法)

model.obj = pyo.Objective(rule=obj\_rule, sense=pyo.minimize)

\# 5. 定义约束(用rule函数复用逻辑)

def con\_rule(mdl, j):  # j为约束索引,来自Set M

    return sum(mdl.a\[j, i] \* mdl.x\[i] for i in mdl.N) >= mdl.b\[j]

model.con = pyo.Constraint(model.M, rule=con\_rule)

\# 6. 传入数据并求解(示例数据)

data = {

    "N": {None: \[1, 2]},  # None表示Set无父索引

    "M": {None: \[1, 2]},

    "c": {None: {1: 1, 2: 2}},

    "a": {None: {(1,1): 3, (1,2): 4, (2,1): 2, (2,2): 5}},

    "b": {None: {1: 1, 2: 2}}

}

instance = model.create\_instance(data)  # 绑定数据生成实例

\# 7. 求解与输出

solver = pyo.SolverFactory("glpk")

result = solver.solve(instance)

pyo.assert\_optimal\_termination(result)

instance.x.pprint()  # 打印所有变量的最优解

2. 五大核心组件详解

无论是 Concrete 还是 Abstract 模型,都依赖以下五大组件构建(Bynum et al., 2021):

组件 作用 关键参数 / 用法
Var 优化变量 within(类型:NonNegativeReals、Binary 等)、bounds(边界)
Objective 目标函数 expr(直接表达式)、rule(函数逻辑)、sense(min/max)
Constraint 约束条件 expr(表达式)、rule(函数复用)、支持 ==/<=/>=
Set 索引集合(管理变量 / 约束的分组) initialize(初始化数据)、支持嵌套(如 Set(model.N)
Param 模型参数(固定输入数据) initialize(初始化)、mutable=True(可动态修改)

关键技巧:当变量 / 约束数量较多时,用 Set索引定义(如 model.x = pyo.Var(model.N, model.M)),避免逐个声明(如 model.x1model.x2),大幅简化代码(Bynum et al., 2021)。

三、实战:仓库选址问题(p-median 问题)

理论之后,我们用 Pyomo 解决一个经典优化问题 ——仓库选址,该问题属于 p-median 问题,目标是在候选地点中选 P 个仓库,最小化总运输成本(Bynum et al., 2021)。

1. 问题描述与建模

  • 输入数据

    • 候选仓库集合 N:Harlingen、Memphis、Ashland;
    • 客户集合 M:NYC、LA、Chicago、Houston;
    • 运输成本 d[n,m]:仓库 n 到客户 m 的单位成本(如 Harlingen 到 NYC 为 1956);
    • 仓库数量限制 P=2
  • 变量定义

    • x[n,m]:客户 m 由仓库 n 服务的比例(0≤x≤1);
    • y[n]:是否选择仓库 n(二进制变量:1 = 选,0 = 不选)。
  • 数学模型

\(\text{s.t.}\begin{matrix} \min & \sum_{n \in N} \sum_{m \in M} d_{n,m} x_{n,m} \\ & \sum_{n \in N} x_{n,m} = 1\ (\forall m \in M) \quad \text{(客户需求全满足)} \\ & x_{n,m} \leq y_{n}\ (\forall n \in N, m \in M) \quad \text{(未选仓库不服务)} \\ & \sum_{n \in N} y_{n} \leq P \quad \text{(仓库数量限制)} \\ & 0 \leq x_{n,m} \leq 1,\ y_n \in \{0,1\} \end{matrix}\)

2. 代码实现:从 Excel 导入数据

实际项目中,数据常存储在 Excel 中。我们用 pandas读取数据,再传入 Pyomo 模型(Bynum et al., 2021):

步骤 1:Excel 数据格式(示例)

NYC LA Chicago Houston
Harlingen 1956 1606 1410 330
Memphis 1096 1792 531 567
Ashland 485 2322 324 1236

步骤 2:Pyomo 代码

import pyomo.environ as pyo

import pandas as pd

\# 1. 从Excel读取数据

df = pd.read\_excel("wl\_data.xlsx", sheet\_name="Delivery Costs", header=0, index\_col=0)

N = list(df.index.map(str))  # 候选仓库集合

M = list(df.columns.map(str))  # 客户集合

d = {(r, c): df.at\[r, c] for r in N for c in M}  # 运输成本字典

P = 2  # 仓库数量限制

\# 2. 定义ConcreteModel

def create\_warehouse\_model(N, M, d, P):

    model = pyo.ConcreteModel(name="WarehouseLocation")

    

    \# 变量:x\[n,m](服务比例)、y\[n](是否选仓库)

    model.x = pyo.Var(N, M, bounds=(0, 1))

    model.y = pyo.Var(N, within=pyo.Binary)

    

    \# 目标函数:最小化总运输成本

    def obj\_rule(mdl):

        return sum(d\[n, m] \* mdl.x\[n, m] for n in N for m in M)

    model.obj = pyo.Objective(rule=obj\_rule, sense=pyo.minimize)

    

    \# 约束1:每个客户需求全满足

    def demand\_rule(mdl, m):

        return sum(mdl.x\[n, m] for n in N) == 1

    model.demand = pyo.Constraint(M, rule=demand\_rule)

    

    \# 约束2:未选仓库不服务

    def warehouse\_active\_rule(mdl, n, m):

        return mdl.x\[n, m] <= mdl.y\[n]

    model.warehouse\_active = pyo.Constraint(N, M, rule=warehouse\_active\_rule)

    

    \# 约束3:仓库数量限制(用mutable Param支持动态修改)

    model.P = pyo.Param(initialize=P, mutable=True)

    def num\_warehouses\_rule(mdl):

        return sum(mdl.y\[n] for n in N) <= mdl.P

    model.num\_warehouses = pyo.Constraint(rule=num\_warehouses\_rule)

    

    return model

\# 3. 求解与结果输出

model = create\_warehouse\_model(N, M, d, P)

solver = pyo.SolverFactory("glpk")

result = solver.solve(model)

\# 验证最优解并打印仓库选择结果

pyo.assert\_optimal\_termination(result)

print("最优仓库选择(y\[n]=1表示选中):")

model.y.pprint()

print(f"最小总运输成本:{pyo.value(model.obj)}")

3. 进阶:动态调整参数 P

若需分析 “仓库数量 P 对成本的影响”,可利用 mutable=TrueParam,无需重新构建模型:

\# 循环测试P=1到5的情况

for p in range(1, 6):

    model.P = p  # 动态修改仓库数量限制

    result = solver.solve(model)

    pyo.assert\_optimal\_termination(result)

    print(f"P={p}时,最小成本:{pyo.value(model.obj)}")

四、求解器与结果验证

Pyomo 本身不实现求解逻辑,而是调用外部求解器。选择合适的求解器是成功的关键(Bynum et al., 2021):

求解器 适用问题类型 特点
GLPK 线性规划(LP)、整数规划(IP) 开源免费,轻量
IPOPT 非线性规划(NLP) 开源,支持二次目标函数
Gurobi 线性、整数、非线性规划 商业求解器,速度快,支持大规模问题

结果验证:用 pyo.assert_optimal_termination(result)确保求解成功(如无可行解或超时会报错);用 model.Var.pprint()pyo.value(变量)查看结果。

五、总结与学习资源

Pyomo 的核心优势在于用 Python 的灵活性实现专业的优化建模,无论是简单的线性规划,还是复杂的整数 / 非线性问题,都能通过清晰的组件化设计高效实现。对于学生、研究者或工程师,它是连接数学优化理论与实际问题的桥梁(Bynum et al., 2021)。

其他学习资源

  1. 核心教材:Bynum, M. L., et al. (2021). Pyomo–Optimization Modeling in Python (3rd ed.). Springer.(书中 90 页前为基础知识,第八章 “Block” 为进阶内容);
  2. 代码仓库:jckantor/ND-Pyomo-Cookbook(GitHub:https://github.com/jckantor/ND-Pyomo-Cookbook),提供大量实战案例;
  3. 官方示例:Pyomo Gallery(NbViewer:https://nbviewer.org/github/Pyomo/PyomoGallery),含运输问题、调度问题等代码;
  4. 求解器文档:IPOPT(https://github.com/coin-or/Ipopt),开源非线性求解器的安装与使用指南。

参考文献

Bynum, M. L., Hackebeil, G. A., Hart, W. E., Laird, C. D., Nicholson, B. L., Siirola, J. D., Watson, J.-P., & Woodruff, D. L. (2021). Pyomo–Optimization modeling in Python (3rd ed., Vol. 67). Springer Science & Business Media.

Kallrath, J. (2012). Modeling Languages in Mathematical Optimization. Berlin, Heidelberg: Springer.

Williams, H. P. (1978). Model Building in Mathematical Programming. New York: Wiley.

jckantor. (2025). ND-Pyomo-Cookbook. GitHub. https://github.com/jckantor/ND-Pyomo-Cookbook

Pyomo. (2025). Transport problem. NbViewer. https://nbviewer.org/github/Pyomo/PyomoGallery/blob/master/transport/transport.ipynb

coin-or. (2025). Ipopt. GitHub. https://github.com/coin-or/Ipopt