繁体   English   中英

如何在类中使用 Pyomo 装饰器

[英]How to use Pyomo decorator within a class

下面是一个使用装饰器语法的简单 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

我帮不了你,我只是有几个问题:

  • 你知道 @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())

试图回答您的直接问题,这似乎对我有用。 我的解释是,由于您的模型称为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访问。

暂无
暂无

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

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