1

I am new to pyomo and I want to understand why it works with namedtuple the way it works. There is a code sample:

from collections import namedtuple

import pyomo.environ as pyo
Product = namedtuple("Product", "product quantity")
products = pyo.Set(initialize=[Product("product1", 10), Product("product2", 20)])
model = pyo.ConcreteModel()
model.product_quantity = pyo.Var(products, within = pyo.NonNegativeReals)
def rule_product_quantity(model, prod):
    return model.product_quantity[prod] >= prod.quantity
model.ct_product_lim = pyo.Constraint(products, rule = rule_product_quantity)

Which gives me an error:

Traceback (most recent call last):
  File "C:\MyFolder\problem.py", line 10, in <module>
    model.ct_product_lim = pyo.Constraint(products, rule = rule_product_quantity)
  File "C:\Users\Alex\AppData\Local\Programs\Python\Python39\lib\site-packages\pyomo\core\base\block.py", line 568, in __setattr__
    self.add_component(name, val)
  File "C:\Users\Alex\AppData\Local\Programs\Python\Python39\lib\site-packages\pyomo\core\base\block.py", line 1126, in add_component
    val.construct(data)
  File "C:\Users\Alex\AppData\Local\Programs\Python\Python39\lib\site-packages\pyomo\core\base\constraint.py", line 800, in construct
    self._setitem_when_not_present(index, rule(block, index))
  File "C:\Users\Alex\AppData\Local\Programs\Python\Python39\lib\site-packages\pyomo\core\base\initializer.py", line 314, in __call__
    return self._fcn(parent, *idx)
TypeError: rule_product_quantity() takes 2 positional arguments but 3 were given

I suppose that means, pyomo in Constraint automatically unpack namedtuples to the sequence of fields and than sends it to the rule.

But for what purpose?

And what is the best way to make this code work?

1 Answer 1

0

It unpacks all tuples. As far as underlying reason... IDK. But it works quite well for manipulating constraints, etc. that are indexed by multiple sets or combinations of sets.

for your code, I would just plan to catch product and quantity separately in the definition, knowing they are going to be unpacked, as shown in C1 below.

import pyomo.environ as pyo

m = pyo.ConcreteModel()

m.A = pyo.Set(initialize=[1,2,3])
m.B = pyo.Set(initialize=['dog', 'cat'])
m.C = pyo.Set(initialize=['red', 'green'])

m.AB = pyo.Set(initialize=m.A * m.B)
m.ABC = pyo.Set(initialize=m.A * m.B * m.C)

m.x = pyo.Var(m.A, m.B)
m.y = pyo.Var(m.C)
m.z = pyo.Var(m.B, m.C)

# shows unpacking
def C1(m, a, b):
    return m.x[a, b] <= 1
m.C1 = pyo.Constraint(m.AB, rule=C1)

# shows use of multiple/multi-indexed sets
def C2(m, a, b, c):
    return m.x[a, b] <= m.y[c]
m.C2 = pyo.Constraint(m.AB, m.C, rule=C2)

# shows gather of indices
def C3(m, *abc):
    return m.x[abc[:2]] >= m.z[abc[-2:]]
m.C3 = pyo.Constraint(m.ABC, rule=C3)

Not the answer you're looking for? Browse other questions tagged or ask your own question.