7.3. Pyomo 的建模与优化¶
Pyomo 是基于 Python 开发的第三方开源建模语言,它支持对线性规划、混合整数规划、非线性规划等问题建模和分析,并调用其他商用或开源的求解器进行求解。
目前,MindOpt 可对在 Windows/Linux/macOS 平台上通过 Pyomo 建立的 线性规划模型 进行求解。关于 Pyomo 的更多详细内容,请参考 Pyomo 官方文档库。
在本节中,我们将介绍如何使用 Pyomo API 来建立 线性规划问题示例 中的优化问题,并调用 MindOpt 求解。
7.3.1. 安装 Pyomo¶
用户需首先安装 MindOpt。关于 MindOpt 的安装与配置请参考 单机版安装。MindOpt 安装后,用户可以通过以下 pip
的命令方式来安装 Pyomo:
pip install pyomo
关于 Pyomo 的详细安装方式,请参考 Pyomo Installation。
7.3.2. Pyomo 调用接口¶
MindOpt 为Pyomo提供两种不同的插件接口,分别是mindopt_direct和mindopt_persistent。
mindopt_direct是一种简单的接口,它在每次调用求解器时都会重新建立一个MindOpt模型,并通过将pyomo模型中的约束和变量转化为MindOpt模型的约束和变量来进行求解。这种接口适用于简单的优化问题,对于复杂的问题可能会导致一些求解器性能上的损失。
mindopt_persistent是一种更高级的接口,它将模型对象存于内存中,允许多次修改和重复求解。对于需要多次求解相同模型的问题非常有用,可以避免重复创建和销毁模型对象产生的开销,从而提高求解效率。
7.3.2.1. 调用 Pyomo Direct 接口¶
MindOpt 在 Pyomo Direct接口文件( mindopt_pyomo.py
)内定义了 Pyomo 调用 MindOpt 所需的相关接口。此接口继承自 Pyomo 的 DirectSolver
类别,实现细节见安装包内的接口文件:
<MDOHOME>/<VERSION>/<PLATFORM>/lib/pyomo/mindopt_persistent.py
<MDOHOME>/<VERSION>/<PLATFORM>/lib/pyomo/mindopt_pyomo.py
用户在使用时需首先将该接口文件移到当前工作目录中,并在 Python 代码中加载该模块中定义的 MindoptDirect
类:
30from mindopt_pyomo import MindoptDirect
同时,我们需要导入Pyomo库:
25from pyomo.environ import *
26from pyomo.core.base.initializer import Initializer
27from pyomo.opt import SolverFactory
28from pyomo.opt import SolverStatus, TerminationCondition
接着,我们调用 Pyomo API 来建立 线性规划问题示例 中的优化问题。关于 Pyomo API 的详细说明,请参考 Pyomo 官方文档API。
33def ConstraintsRule(model, p):
34 return sum(constraint_coeff[i, p] * model.Variable[i] for i in variable_names)
35
36def fb(model, i):
37 return (var_lb[i], var_ub[i])
38
39# Define variables and constraints.
40variable_names = ['x0', 'x1', 'x2', 'x3']
41var_lb = {'x0':0, 'x1':0, 'x2':0, 'x3':0}
42var_ub = {'x0':10, 'x1':None, 'x2':None, 'x3':None}
43objective_coeff = {'x0': 1, 'x1': 1, 'x2': 1, 'x3': 1}
44constraint_names = ['c0', 'c1']
45constraint_bound = {'c0': 1, 'c1': 1}
46constraint_coeff = {('x0', 'c0'): 1, ('x1', 'c0'): 1, ('x2', 'c0'): 2, ('x3', 'c0'): 3,
47 ('x0', 'c1'): 1, ('x1', 'c1'): -1, ('x2', 'c1'): 0, ('x3', 'c1'): 6}
48
49# Create model.
50model = ConcreteModel(name="ex1")
51
52# Build decision variables.
53model.Set = Set(initialize=variable_names)
54model.Variable = Var(model.Set, within=NonNegativeReals, bounds=fb)
55
56# Objective.
57model.obj = Objective(expr=sum(objective_coeff[var_name] * model.Variable[var_name] for var_name in variable_names), sense=minimize)
58
59# Constraints.
60model.dual = Suffix(direction=Suffix.IMPORT)
61model.cons1 = Constraint(expr = ConstraintsRule(model, 'c0') >= constraint_bound['c0'])
62model.cons2 = Constraint(expr = ConstraintsRule(model, 'c1') == constraint_bound['c1'])
求解前,我们指定使用 MindOpt 求解器,并对求解的相关参数进行设置(求解器参数请查阅 参数):
67# Solve problem by MindOpt solver.
68opt = SolverFactory("mindopt_direct")
69
70# Set options.
71opt.options['Method'] = -1
72opt.options['IPM/PrimalTolerance'] = 1e-10
最后,调用 Pyomo 的求解函数 solve()
进行求解,并获取相关的结果:
74# Solve.
75results = opt.solve(model)
76
77if (results.solver.status == SolverStatus.ok) and (results.solver.termination_condition == TerminationCondition.optimal):
78 print("The solution is optimal and feasible.")
79 model.Variable.display()
80 model.dual.display()
81 model.obj.display()
82elif (results.solver.termination_condition == TerminationCondition.infeasible):
83 print("The model is infeasible.")
84 print("Solver Status: ", results.solver.status)
85else:
86 print("Something else is wrong.")
87 print("Solver Status: ", results.solver.status)
7.3.2.2. 调用 Pyomo Persistent 接口文件¶
MindOpt 在 Pyomo Persistent接口文件( mindopt_persistent.py
)内定义了 Pyomo 调用 MindOpt 所需的相关persistent接口。此接口继承自 Pyomo 的 PersistentSolver
类别,实现细节见安装包内的接口文件:
<MDOHOME>/<VERSION>/<PLATFORM>/lib/pyomo/mindopt_persistent.py
用户在使用时需首先将该接口文件移到当前工作目录中,并在 Python 代码中加载该模块中定义的 MindoptPersistent
类:
from mindopt_persistent import MindoptPersistent
同时,导入Pyomo库:
from pyomo.environ import * from pyomo.core.base.initializer import Initializer from pyomo.opt import SolverFactory from pyomo.opt import SolverStatus, TerminationCondition
接着,我们调用 Pyomo API 来建立优化问题。关于 Pyomo API 的详细说明,请参考 Pyomo 官方文档API。
model = ConcreteModel() products = ['A', 'B'] materials = ['X', 'Y'] model.x = Var(products, domain=NonNegativeReals) model.profit = Objective(expr=10 * model.x['A'] + 15 * model.x['B'], sense=maximize) model.material_x = Constraint(expr=2 * model.x['A'] + model.x['B'] <= 10) model.material_y = Constraint(expr=model.x['A'] + 2 * model.x['B'] <= 6) solver = SolverFactory('mindopt_persistent')
在求解前必须使用set_instance将模型保存在内存中。
solver.set_instance(model) solver.solve(model) print(solver._solver_model.objval)
求解后也可以使用Persistent提供的函数获取模型属性/参数,也可以修改原有模型。
# Retrieving variable attributes var_attr = solver.get_var_attr(model.x['A'], 'lb') print(var_attr) # Setting and retrieving MindOpt parameters solver.set_mindopt_param("MaxTime", 1000) solver.set_mindopt_param("MaxTi*", 100) print(solver.get_mindopt_param_info("MaxTi*"))
7.3.3. 使用 SOS 功能¶
SOS(Special Ordered Sets)约束是一种特殊的约束类型,指的是一组有序变量集合中最多只能有N个变量取非零值。SOS约束在某些优化问题中非常有用,比如在调度问题中,可以用SOS约束来确保每个时间点只能选择一个任务执行。
无论是使用mindopt_direct还是mindopt_persistent接口,都可以使用 MindOpt 的SOS功能。
用户在通过Pyomo建模代码使用SOS功能时,需要首先定义一个SOS集合,将特定变量分组到有序集合中。例如,假设有一类变量y属于同一个SOS组,则可实现如下代码:
from pyomo.environ import * from mindopt_pyomo import MindoptDirect import pyomo.environ as pyo N = 1 model = pyo.ConcreteModel() model.x = pyo.Var([1], domain=pyo.NonNegativeReals, bounds=(0,40)) model.A = pyo.Set(initialize=[1,2,4,6]) model.y = pyo.Var(model.A, domain=pyo.NonNegativeReals, bounds=(0,2)) model.OBJ = pyo.Objective( expr=(1*model.x[1]+ 2*model.y[1]+ 3*model.y[2]+ -0.1*model.y[4]+ 0.5*model.y[6]) ) model.ConstraintYmin = pyo.Constraint( expr = (model.x[1]+ model.y[1]+ model.y[2]+ model.y[6] >= 0.25 ) ) model.mysos = pyo.SOSConstraint( var=model.y, sos=N ) opt = pyo.SolverFactory('mindopt_direct') opt.solve(model, tee=False) print(opt._solver_model.objval)
在上述示例中, SOSConstraint
的参数var用于指定变量属于SOS集合的规则。在此示例中,我们将变量y分组到同一个SOS集合中。
7.3.4. 使用 Callback 功能¶
无论是使用mindopt_direct还是mindopt_persistent接口,用户都可以使用 MindOpt 的回调功能,提供一些启发式决策用以优化求解速度。
但是考虑到需要使用回调功能的模型一般比较复杂,推荐用户使用mindopt_persistent接口。
在Pyomo中使用Mindopt的回调函数功能,首先需要定义一个回调函数来处理特定的事件。
import pyomo.environ as pe from mindopt_persistent import MindoptPersistent m = pe.ConcreteModel() m.x = pe.Var(bounds=(0, 4)) m.y = pe.Var(within=pe.Integers, bounds=(0, None)) m.obj = pe.Objective(expr=2*m.x + m.y) m.cons = pe.ConstraintList() # for the cutting planes def my_callback(model, where): where_names = { 3: "MIPSTART", 4: "MIPSOL", 5: "MIPNODE" } print("where = {}".format(where_names[where])) # Count call times model._calls += 1 # When using the Python SDK, the model passed to the callback is a presolved model. Note that the C SDK uses the original model. # Print the constraint matrix of the presolved model here. The getA() function returns a scipy.sparse.csc_matrix. # The constraint matrix returned by getA() is a reference, and any modifications to the constraint matrix should not be allowed as it may lead to unforeseen issues. print(model.getA()) cur_sols = model.cbGetSolution(model.getVars()) # Print the current best solutions print(cur_sols) rel_sols = model.cbGetNodeRel(model.getVars()) # Print the current relaxation solution print(rel_sols)
在上述示例中,我们定义了一个名为my_callback的回调函数。在此回调函数中,我们使用cbGet方法获取当前预处理模型的一些信息,比如变量的lower bound等。 然后,您需要将回调函数与Mindopt求解器连接起来,并为特定的事件注册回调函数。
opt = pe.SolverFactory('mindopt_persistent') opt.set_instance(m) # Register the callback function opt.set_callback(my_callback) results = opt.solve()
在上述示例中,我们使用SolverFactory创建了一个mindopt_persistent求解器实例,并将回调函数my_callback注册到该求解器中。然后,我们使用solve方法解决模型,并触发相应的回调事件。
7.3.5. 建模示例: mdo_pyomo_lo_ex1¶
文件链接 mdo_pyomo_lo_ex1.py 中提供了完整代码:
1"""
2/**
3 * Description
4 * -----------
5 *
6 * Linear optimization.
7 *
8 * Formulation
9 * -----------
10 *
11 * Minimize
12 * obj: 1 x0 + 1 x1 + 1 x2 + 1 x3
13 * Subject To
14 * c1 : 1 x0 + 1 x1 + 2 x2 + 3 x3 >= 1
15 * c2 : 1 x0 - 1 x2 + 6 x3 == 1
16 * Bounds
17 * 0 <= x0 <= 10
18 * 0 <= x1
19 * 0 <= x2
20 * 0 <= x3
21 * End
22 */
23"""
24
25from pyomo.environ import *
26from pyomo.core.base.initializer import Initializer
27from pyomo.opt import SolverFactory
28from pyomo.opt import SolverStatus, TerminationCondition
29
30from mindopt_pyomo import MindoptDirect
31
32
33def ConstraintsRule(model, p):
34 return sum(constraint_coeff[i, p] * model.Variable[i] for i in variable_names)
35
36def fb(model, i):
37 return (var_lb[i], var_ub[i])
38
39# Define variables and constraints.
40variable_names = ['x0', 'x1', 'x2', 'x3']
41var_lb = {'x0':0, 'x1':0, 'x2':0, 'x3':0}
42var_ub = {'x0':10, 'x1':None, 'x2':None, 'x3':None}
43objective_coeff = {'x0': 1, 'x1': 1, 'x2': 1, 'x3': 1}
44constraint_names = ['c0', 'c1']
45constraint_bound = {'c0': 1, 'c1': 1}
46constraint_coeff = {('x0', 'c0'): 1, ('x1', 'c0'): 1, ('x2', 'c0'): 2, ('x3', 'c0'): 3,
47 ('x0', 'c1'): 1, ('x1', 'c1'): -1, ('x2', 'c1'): 0, ('x3', 'c1'): 6}
48
49# Create model.
50model = ConcreteModel(name="ex1")
51
52# Build decision variables.
53model.Set = Set(initialize=variable_names)
54model.Variable = Var(model.Set, within=NonNegativeReals, bounds=fb)
55
56# Objective.
57model.obj = Objective(expr=sum(objective_coeff[var_name] * model.Variable[var_name] for var_name in variable_names), sense=minimize)
58
59# Constraints.
60model.dual = Suffix(direction=Suffix.IMPORT)
61model.cons1 = Constraint(expr = ConstraintsRule(model, 'c0') >= constraint_bound['c0'])
62model.cons2 = Constraint(expr = ConstraintsRule(model, 'c1') == constraint_bound['c1'])
63
64# Print formulation of model.
65model.pprint()
66
67# Solve problem by MindOpt solver.
68opt = SolverFactory("mindopt_direct")
69
70# Set options.
71opt.options['Method'] = -1
72opt.options['IPM/PrimalTolerance'] = 1e-10
73
74# Solve.
75results = opt.solve(model)
76
77if (results.solver.status == SolverStatus.ok) and (results.solver.termination_condition == TerminationCondition.optimal):
78 print("The solution is optimal and feasible.")
79 model.Variable.display()
80 model.dual.display()
81 model.obj.display()
82elif (results.solver.termination_condition == TerminationCondition.infeasible):
83 print("The model is infeasible.")
84 print("Solver Status: ", results.solver.status)
85else:
86 print("Something else is wrong.")
87 print("Solver Status: ", results.solver.status)
其他 Pyomo 的示例请参考 Pyomo Gallery。