简体   繁体   English

如何在类中使用 Pyomo 装饰器

[英]How to use Pyomo decorator within a class

Below is a simple Pyomo script using the decorator syntax - I would like to understand how to use this syntax within a class - in this case inside Model .下面是一个使用装饰器语法的简单 Pyomo 脚本——我想了解如何在类中使用这种语法——在本例中是在Model中。

None-class version无类版

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

Decorator version within a class that doesn't work:类中的装饰器版本不起作用:

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

I'm not able to help you on this, I just have a few qustions:我帮不了你,我只是有几个问题:

  • do you know if the use of @model.Objective() (same for Constraint etc) is documented somewhere?你知道 @model.Objective() 的使用(与约束等相同)是否记录在某处吗? I didn't know it existed, and it's awesome我不知道它存在,而且它很棒
  • why do you want your "function rules" to be methods of the class?为什么你希望你的“功能规则”成为类的方法 couldn't you defined them as functions within the __init__ method?您不能将它们定义为__init__方法中的函数吗?

I guess what I'm missing is the benefit of using a class in the first place.我想我首先缺少的是使用类的好处。 If you are just trying to wrap the model construction somehow, then a better approach is using a function:如果您只是想以某种方式包装模型构造,那么更好的方法是使用函数:

def create_model():
    model = AbstractModel()

    ...

    @model.Constraint()
    def some_rule_function(model):
        ...

    ...

    return model

EDIT : if you really want to wrap everything into a class:编辑:如果您真的想将所有内容包装到一个类中:

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

example usage:用法示例:

model = Model(create_model())

Trying to answer your direct question, here's something that seems to work for me.试图回答您的直接问题,这似乎对我有用。 My interpretation is that since your model is called self.model , the decorators should also match that.我的解释是,由于您的模型称为self.model ,因此装饰器也应该与之匹配。

Note that I used s as the first argument in the constraint method definitions just to see if it worked, but it could also be model or whatever you want to call it.请注意,我将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

You would then be able to instantiate the model with model = Model() , though annoyingly (at least to me), all your Pyomo model components will be within the attribute model.model (eg, model.model.P ).然后你就可以用model = Model()实例化模型,尽管很烦人(至少对我来说),你所有的 Pyomo 模型组件都将在属性model.model (例如, model.model.P )。

What I've done before to make the naming cleaner is to inherit from AbstractModel (though the other answer suggests that may not be good practice):为了使命名更清晰,我之前所做的是从 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

In this case, you still instantiate as model = Model() but your Pyomo model components can be accessed as model.P .在这种情况下,您仍然实例化为model = Model()但您的 Pyomo 模型组件可以作为model.P访问。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM