【问题标题】:How to use Pyomo decorator within a class如何在类中使用 Pyomo 装饰器
【发布时间】:2020-03-23 00:44:14
【问题描述】:

下面是一个使用装饰器语法的简单 Pyomo 脚本 - 我想了解如何在类中使用这种语法 - 在本例中是 Model

非类版本

from pyomo.environ import *

import random

random.seed(1000)

model = AbstractModel()

model.N = Param(within=PositiveIntegers)
model.P = Param(within=RangeSet(1, model.N))
model.M = Param(within=PositiveIntegers)

model.Locations = RangeSet(1, model.N)
model.Customers = RangeSet(1, model.M)

model.d = Param(
    model.Locations,
    model.Customers,
    initialize=lambda n, m, model: random.uniform(1.0, 2.0),
    within=Reals,
)

model.x = Var(model.Locations, model.Customers, bounds=(0.0, 1.0))
model.y = Var(model.Locations, within=Binary)


@model.Objective()
def obj(model):
    return sum(
        model.d[n, m] * model.x[n, m] for n in model.Locations for m in model.Customers
    )


@model.Constraint(model.Customers)
def single_x(model, m):
    return (sum(model.x[n, m] for n in model.Locations), 1.0)


@model.Constraint(model.Locations, model.Customers)
def bound_y(model, n, m):
    return model.x[n, m] - model.y[n] <= 0.0


@model.Constraint()
def num_facilities(model):
    return sum(model.y[n] for n in model.Locations) == model.P

类中的装饰器版本不起作用:

from pyomo.environ import *

import random

random.seed(1000)


class Model:
    def __init__(self):
        self.model = AbstractModel()

        self.model.N = Param(within=PositiveIntegers)
        self.model.P = Param(within=RangeSet(1, self.model.N))
        self.model.M = Param(within=PositiveIntegers)

        self.model.Locations = RangeSet(1, self.model.N)
        self.model.Customers = RangeSet(1, self.model.M)

        self.model.d = Param(
            self.model.Locations,
            self.model.Customers,
            initialize=lambda n, m, model: random.uniform(1.0, 2.0),
            within=Reals,
        )

        self.model.x = Var(
            self.model.Locations, self.model.Customers, bounds=(0.0, 1.0)
        )
        self.model.y = Var(self.model.Locations, within=Binary)

    @model.Objective()
    def obj(model):
        return sum(
            model.d[n, m] * model.x[n, m]
            for n in model.Locations
            for m in model.Customers
        )

    @model.Constraint(model.Customers)
    def single_x(model, m):
        return (sum(model.x[n, m] for n in model.Locations), 1.0)

    @model.Constraint(model.Locations, model.Customers)
    def bound_y(model, n, m):
        return model.x[n, m] - model.y[n] <= 0.0

    @model.Constraint()
    def num_facilities(model):
        return sum(model.y[n] for n in model.Locations) == model.P

【问题讨论】:

    标签: python decorator pyomo


    【解决方案1】:

    试图回答您的直接问题,这似乎对我有用。我的解释是,既然你的模型被称为self.model,那么装饰器也应该匹配它。

    请注意,我使用s 作为约束方法定义中的第一个参数只是为了查看它是否有效,但它也可以是model 或任何您想调用的名称。

    class Model:
        def __init__(self):
            self.model = pyo.AbstractModel()
    
            self.model.N = pyo.Param(initialize=5, within=pyo.PositiveIntegers)
            self.model.P = pyo.Param(initialize=3, within=pyo.RangeSet(1, self.model.N))
            self.model.M = pyo.Param(initialize=3, within=pyo.PositiveIntegers)
    
            self.model.Locations = pyo.RangeSet(1, self.model.N)
            self.model.Customers = pyo.RangeSet(1, self.model.M)
    
            self.model.d = pyo.Param(
                self.model.Locations,
                self.model.Customers,
                initialize=lambda n, m, model: random.uniform(1.0, 2.0),
                within=pyo.Reals,
            )
    
            self.model.x = pyo.Var(
                self.model.Locations, self.model.Customers, bounds=(0.0, 1.0)
            )
            self.model.y = pyo.Var(self.model.Locations, within=pyo.Binary)
    
            @self.model.Objective()
            def obj(s):
                return sum(
                    s.d[n, m] * s.x[n, m]
                    for n in s.Locations
                    for m in s.Customers
                )
    
            @self.model.Constraint(self.model.Customers)
            def single_x(s, m):
                return (sum(s.x[n, m] for n in s.Locations), 1.0)
    
            @self.model.Constraint(self.model.Locations, self.model.Customers)
            def bound_y(s, n, m):
                return s.x[n, m] - s.y[n] <= 0.0
    
            @self.model.Constraint()
            def num_facilities(s):
                return sum(s.y[n] for n in s.Locations) == s.P
    

    然后您就可以使用model = Model() 来实例化模型,尽管很烦人(至少对我而言),您的所有 Pyomo 模型组件都将在属性 model.model 内(例如,model.model.P)。

    我之前为使命名更清晰所做的是从 AbstractModel 继承(尽管其他答案表明这可能不是好的做法):

    from pyomo.core.base.PyomoModel import AbstractModel
    
    class Model(AbstractModel):
        def __init__(self):
            AbstractModel.__init__(self)
    
            self.N = pyo.Param(initialize=5, within=pyo.PositiveIntegers)
            self.P = pyo.Param(initialize=3, within=pyo.RangeSet(1, self.N))
            self.M = pyo.Param(initialize=3, within=pyo.PositiveIntegers)
    
            self.Locations = pyo.RangeSet(1, self.N)
            self.Customers = pyo.RangeSet(1, self.M)
    
            self.d = pyo.Param(
                self.Locations,
                self.Customers,
                initialize=lambda n, m, model: random.uniform(1.0, 2.0),
                within=pyo.Reals,
            )
    
            self.x = pyo.Var(
                self.Locations, self.Customers, bounds=(0.0, 1.0)
            )
            self.y = pyo.Var(self.Locations, within=pyo.Binary)
    
            @self.Objective()
            def obj(s):
                return sum(
                    s.d[n, m] * s.x[n, m]
                    for n in s.Locations
                    for m in s.Customers
                )
    
            @self.Constraint(self.Customers)
            def single_x(s, m):
                return (sum(s.x[n, m] for n in s.Locations), 1.0)
    
            @self.Constraint(self.Locations, self.Customers)
            def bound_y(s, n, m):
                return s.x[n, m] - s.y[n] <= 0.0
    
            @self.Constraint()
            def num_facilities(s):
                return sum(s.y[n] for n in s.Locations) == s.P
    

    在这种情况下,您仍将实例化为 model = Model(),但您的 Pyomo 模型组件可以作为 model.P 访问。

    【讨论】:

      【解决方案2】:

      我无法为您提供帮助,我只有几个问题:

      • 您知道@model.Objective() 的使用(对于约束等)是否记录在某处?我不知道它存在,而且很棒
      • 为什么你希望你的“函数规则”成为类的方法?您不能将它们定义为 __init__ 方法中的函数吗?

      我想我首先缺少的是使用类的好处。 如果您只是想以某种方式包装模型构造,那么更好的方法是使用函数:

      def create_model():
          model = AbstractModel()
      
          ...
      
          @model.Constraint()
          def some_rule_function(model):
              ...
      
          ...
      
          return model
      

      编辑:如果您真的想将所有内容包装到一个类中:

      class Model:
          def __init__(self, model):
               self.model = model
      
          # alternative constructor:
          # def __init__(self):
          #     self.model = create_model()
      
          def construct(self, data):
              # get concrete model
              self.model = self.model.create_instance(data)
      
          def run(self, solver, **kwargs):
              with pe.SolverFactory(solver) as solver:
                  solver.solve(self.model, **kwargs)
      
          def construct_and_run(self, data, solver, **kwargs):
              self.construct(data)
              self.data(solver, **kwargs)
      
          # other behavior you want to add to the class
      

      示例用法:

      model = Model(create_model())
      

      【讨论】:

      • 没有太多关于使用 pyomo 装饰器的文档,但是这个示例来自 pyomo github 存储库上的 pyomo/examples/pyomo/p-median/decorated_pmedian.py。使用类的想法是为模型提供一种包装器。我认为我的问题超出了 pyomo 的范围,而是关于如何在课堂上使用装饰器的一般指导。在您的示例中,create_model 方法可以成为 Model 类本身的一部分吗?
      • 你有两种选择:一种是继承AbstractModel(我不推荐),另一种是让你的类的__init__方法做上面create_model做的事情,和最后仍然设置self.model = model
      • 对不起,我没有真正理解逻辑 - 我的想法是我会将完整的模型、约束等包装到一个类中,这样我就可以绕过所有数据来实例化它并调用run 方法。这将创建一个抽象级别。
      • 我对上面的例子有点偶然,但我不是 100% 确定我理解你想要实现的目标。包含所有组件的“类”是AbstractModelConcreteModel,包括所有组件(您可以通过model.component_name 访问它们)。因此,您可以使用 runmain 函数先创建模型,然后再求解它。
      猜你喜欢
      • 2011-11-20
      • 1970-01-01
      • 2017-02-17
      • 2022-08-15
      • 1970-01-01
      • 2020-11-30
      • 2013-09-26
      • 1970-01-01
      • 2019-02-03
      相关资源
      最近更新 更多