Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

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
like image 918
Michael Avatar asked Dec 05 '25 16:12

Michael


1 Answers

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? 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?

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())
like image 128
Giorgio Balestrieri Avatar answered Dec 07 '25 05:12

Giorgio Balestrieri



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!