PYOMO:使用集合和参数定义数据集以解决优化问题

Posted

技术标签:

【中文标题】PYOMO:使用集合和参数定义数据集以解决优化问题【英文标题】:PYOMO: Defining dataset using Sets and Parameters for solving an optimization problem 【发布时间】:2021-03-19 21:44:18 【问题描述】:

我正在尝试将一些数据公式化为 PYOMO 模型以解决优化问题。

materials = ['steel', 'alum', 'carbon', 'cheese']

每种材料都有 2 个属性 - 密度和电导率,它们的值定义如下。

density =      'steel' : 1.2,
            'alum'  : 0.8,
            'carbon': 1.8,
            'cheese': 0.7

conductivity = 'steel' : 6.4,
               'alum'  : 3.1,
               'carbon': 4.4,
               'cheese': 0.3

目标函数计算 2 个矩形板的重量,如下所示:

Objective function = Area_1 * thickness_1 * density_1 + Area_2 * thickness_2 * density_2

其中,Area_1、thickness_1、density_1分别为板块1的面积、厚度和密度。

每个板的面积和厚度都是固定的。但是密度值取决于求解器选择的材料以获得最佳结果。该模型还有一个约束定义如下:

(conductivity_1/thickness_1) + (conductivity_2/thickness_2)  => 22

所以,求解器在为板选择密度值时,还必须选择相同材料的电导率值

如果有人能帮助我解决这个问题,我将不胜感激。如果您有不同的想法来解决这个问题,我也欢迎。谢谢。

【问题讨论】:

解决这个问题并不费力。您刚刚从其他问题中回收了我的大部分示例。看看这个网站上的 pyomo dox 或其他几个 pyomo 问题的例子。我已经发布了一些使用双索引的方法,它们可能会帮助您入门。让我们看一下对此的最小功能示例更新。现在你的问题太宽泛了。 一个好的第一步是确定如何以数学方式表示您的问题(您的决策变量是什么,您如何根据决策变量和参数来表示您的约束和目标函数?)跨度> @AirSquid 谢谢你的信息。我会尝试这种方式。 @cookesd 我的决策变量是面积、厚度、电导率和密度。在 4 个变量中,面积和厚度是板的固定值。但是,电导率和密度由求解器确定以获得最佳解。所以当求解器选择一个板的密度值时,它也必须选择相同材料的电导率值。谢谢。 【参考方案1】:

这是一个我认为可以满足您所有问题的示例模型。

一旦您将第二个索引设置为板 P = 1, 2, 3 在这种情况下为 3 个板,那么我们需要对我们的决策变量进行双重索引以表示材料 m 到板 p 的分配。在这个例子中,4 种材料,3 块板。

这里可能有许多其他的约束变体,但我添加的约束总体上回答了您关于电导率的问题。请注意,我还添加了一个约束以确保为每个板分配 1 种且只有 1 种材料。根据模型中的其他约束,您可能需要/可能不需要这个,但它可以很好地防止虚假答案。这也是使用 pyomo 中的函数 - 规则组合的“for each”约束样式的示例。

结果...铝和奶酪三明治...:)

# material selection model

import pyomo.environ as pyo

# data
materials = ['steel', 'alum', 'carbon', 'cheese']

density =      'steel' : 1.2,
                'alum'  : 0.8,
                'carbon': 1.8,
                'cheese': 0.7

conductivity = 'steel' : 40.8,
                'alum'  : 30.1,
                'carbon': 42.4,
                'cheese': 15.3

price =        'steel' : 2.3,
                'alum'  : 3.5,
                'carbon': 5.8,
                'cheese': 6.0

                  # t     area
plate_dims =   1: (10,   150), 
                2: (12.5, 200),
                3: (8,    125)

mdl = pyo.ConcreteModel('material selector')

# SETS (used to index the decision variable and the parameters)
mdl.M = pyo.Set(initialize=materials)
mdl.P = pyo.Set(initialize=plate_dims.keys())

# VARIABLES
mdl.x = pyo.Var(mdl.M, mdl.P, domain=pyo.Binary)  # select material M for plate P

# PARAMETERS
mdl.density =       pyo.Param(mdl.M, initialize=density)
mdl.conductivity =  pyo.Param(mdl.M, initialize=conductivity)
mdl.price =         pyo.Param(mdl.M, initialize=price)
mdl.p_thickness =   pyo.Param(mdl.P, initialize= k:v[0] for k,v in plate_dims.items())
mdl.p_area =        pyo.Param(mdl.P, initialize= k:v[1] for k,v in plate_dims.items())

# OBJ (minimize total density)
mdl.obj = pyo.Objective(expr=sum(mdl.x[m, p] * mdl.p_thickness[p] 
                        * mdl.p_area[p] * mdl.density[m] 
                        for m in mdl.M for p in mdl.P))

# CONSTRAINTS
# minimum conductivity
mdl.c1 = pyo.Constraint(expr=sum(mdl.x[m, p] * mdl.conductivity[m]/mdl.p_thickness[p]
                        for m in mdl.M for p in mdl.P) >= 5.0)

# must populate all plates with 1 material
def c2(model, plate):
    return sum(mdl.x[m, plate] for m in mdl.M) == 1
mdl.c2 = pyo.Constraint(mdl.P, rule=c2)

# solve it
solver = pyo.SolverFactory('glpk')
result = solver.solve(mdl)
mdl.display()

产量:

Model material selector

  Variables:
    x : Size=12, Index=x_index
        Key           : Lower : Value : Upper : Fixed : Stale : Domain
          ('alum', 1) :     0 :   0.0 :     1 : False : False : Binary
          ('alum', 2) :     0 :   0.0 :     1 : False : False : Binary
          ('alum', 3) :     0 :   1.0 :     1 : False : False : Binary
        ('carbon', 1) :     0 :   0.0 :     1 : False : False : Binary
        ('carbon', 2) :     0 :   0.0 :     1 : False : False : Binary
        ('carbon', 3) :     0 :   0.0 :     1 : False : False : Binary
        ('cheese', 1) :     0 :   1.0 :     1 : False : False : Binary
        ('cheese', 2) :     0 :   1.0 :     1 : False : False : Binary
        ('cheese', 3) :     0 :   0.0 :     1 : False : False : Binary
         ('steel', 1) :     0 :   0.0 :     1 : False : False : Binary
         ('steel', 2) :     0 :   0.0 :     1 : False : False : Binary
         ('steel', 3) :     0 :   0.0 :     1 : False : False : Binary

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True : 3600.0

  Constraints:
    c1 : Size=1
        Key  : Lower : Body              : Upper
        None :   5.0 : 6.516500000000001 :  None
    c2 : Size=3
        Key : Lower : Body : Upper
          1 :   1.0 :  1.0 :   1.0
          2 :   1.0 :  1.0 :   1.0
          3 :   1.0 :  1.0 :   1.0

【讨论】:

我有一个关于您使用 Sets 和 Params 的问题。您是否看到在具体模型中使用它们而不是在其他 python 对象(列表、numpy 数组、pandas 数据帧)中使用模型数据有什么好处?我通常使用后者,但我不确定我是否错过了功能 @cookesd 我不知道没有将参数和集合放入模型中会错过太多,我只是认为模型中的故障排除(尤其是集合)更容易,并且使用mdl.pprint() 并查看 everything 很好,并且声明子集和稀疏集 within 更大的集也有利于错误检测。 @RuaGoa 我认为您需要找到一些关于线性规划/整数规划的教程材料或文本,因为您似乎没有 pyomo 问题,而是数学编程的基本问题。这就是它的完成方式,并且使用二进制变量是正确的。写出数学并检查它。求解器引擎将为 x[m, p] 选择 1/0 值以最小化目标。如果您想查看目标的表达式,可以将行 mdl.obj.pprint() 添加到代码中并查看表达式并插入二进制值以查看数学。 @AirSquid 你能解释一下如何打印从目标函数中获得的每个板块的最小值吗? @AirSquid 我对表达式中的“sum()”命令持怀疑态度。它是指所有组合的总和还是仅仅意味着一个数组?

以上是关于PYOMO:使用集合和参数定义数据集以解决优化问题的主要内容,如果未能解决你的问题,请参考以下文章

多目标优化示例 Pyomo

从 pandas DataFrame 在 pyomo 中定义参数

如何在 pyomo 中使用集合和范围集的多级索引?

TypeError PYOMO:基于 pandas 数据框定义约束

在不使用 for 循环的情况下检索 Pyomo 解决方案

如何在 Google Colab 中使用 Pyomo 解决抽象模型