Description
Summary
If I have two indexed sets, and I want to write an expression on them, I have to create a new function:
m = ConcreteModel()
m.time = ContinuousSet(initialize=[1,2,3,4,5])
m.x = Var(m.time)
m.y = Var(m.time)
# Creating an expression to element-wise add m.x and m.y
def _add(m,t):
return m.x[t] + m.y[t]
m.z = Expression(m.time,rule=_add)
It would be nice if there was some syntactic sugar to be able to simply go
m.z = m.x + m.y # Element-wise addition of m.x and m.y, as both are indexed by the same set
This could be combined with spread operators for adding items with different index sets
m.w = m.x + 5 # adds 5 to each element in m.x
Rationale
I'm trying to parse sympy expressions (entered by a user) and add them to a pyomo model.
If m.x and m.y were variables, I can use the sympy2pyomo_expression to convert the string "x + y" to the pyomo expression m.x + m.y
(internally represented as a pyomo.core.expr.SumExpression(m.x,m.y)
). However, I want m.x and m.y to be indexed by time, and the SumExpression
operation is not defined on indexed variables.
I could call sympy2pyomoExpression once for every timestep, where the sympy symbols x and y are mapped to the pyomo variables m.x[t]
and m.y[t]
. However, that doesn't work with more complex scenarios, e.g if I wanted to define a sympy function "sum(x)" which returns a scalar representing the sum of all elements of the indexed variable x.
Description
pyomo.core.expr.numeric_expr.py
supports the types:
class ARG_TYPE(enum.IntEnum):
MUTABLE = -2
ASNUMERIC = -1
INVALID = 0
NATIVE = 1
NPV = 2
PARAM = 3
VAR = 4
MONOMIAL = 5
LINEAR = 6
SUM = 7
OTHER = 8
Not sure what all of these mean, but I think we'd have to add an Indexed Arg type, and then all the functions /mappings to handle arguments in this file. We might have to add some data type to support indexed expressions at a more fundamental level.
Additional information
The "NumericExpressions" in this file seem different to the "Expressions" you add to a model. These "NumericExpressions" seem to be able to be created as a symbol tree internally in a Pyomo Expression. E.g I can do:
internal_expr_tree = SumExpression((1,SumExpression((2,3))))
value(internal_expr_tree) # returns 6
But I can't do
from pyomo.environ import Expression
expr = Expression(rule=1+ Expression(rule = 2 + 3))
value(expr) # Fails with:
# Error: Evaluating the expression of Expression 'ScalarExpression' before the Expression has been constructed (there is currently no value to return).
because those sub-expressions need to be attached to a model and "built" first, e.g
m = ConcreteModel()
m.sub_expr = Expression(rule = 2 + 3)
m.expr = Expression(rule= 1 + m.sub_expr)
value(m.expr) # returns 6
Not sure if this is a feature that is wanted, or if I should implement it in a custom manner just for my use case. However, I am happy to help out if someone can give me some more direction on what to do.