-
Notifications
You must be signed in to change notification settings - Fork 558
Model Observer #3695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Model Observer #3695
Changes from all commits
f2066ea
897c0ef
97aeb31
a69d5e6
9763e9a
ff635b8
db0fda4
149ab06
42c8cc8
ccb6de4
d718e9a
23ba4d9
14f928b
d7b9918
c313fe5
a424cfb
33f831a
5f3f403
79e1b47
7275176
823e15a
73258f5
5a8a5a6
22c9169
321755a
f8cfc33
2adefbc
df26c4e
2dc566e
6b2ffbd
e68ee73
82e40b2
ae7e031
5778688
cd1c4ef
3f30893
4f7e93e
ac9d952
dc19b17
bb60959
99ac089
213d353
065f43e
ddf0a39
643f546
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# ___________________________________________________________________________ | ||
# | ||
# Pyomo: Python Optimization Modeling Objects | ||
# Copyright (c) 2008-2025 | ||
# National Technology and Engineering Solutions of Sandia, LLC | ||
# Under the terms of Contract DE-NA0003525 with National Technology and | ||
# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain | ||
# rights in this software. | ||
# This software is distributed under the 3-clause BSD License. | ||
# ___________________________________________________________________________ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# ___________________________________________________________________________ | ||
# | ||
# Pyomo: Python Optimization Modeling Objects | ||
# Copyright (c) 2008-2025 | ||
# National Technology and Engineering Solutions of Sandia, LLC | ||
# Under the terms of Contract DE-NA0003525 with National Technology and | ||
# Engineering Solutions of Sandia, LLC, the U.S. Government retains certain | ||
# rights in this software. | ||
# This software is distributed under the 3-clause BSD License. | ||
# ___________________________________________________________________________ | ||
|
||
from pyomo.core.expr.visitor import StreamBasedExpressionVisitor | ||
from pyomo.core.expr.numeric_expr import ( | ||
ExternalFunctionExpression, | ||
NegationExpression, | ||
PowExpression, | ||
MaxExpression, | ||
MinExpression, | ||
ProductExpression, | ||
MonomialTermExpression, | ||
DivisionExpression, | ||
SumExpression, | ||
Expr_ifExpression, | ||
UnaryFunctionExpression, | ||
AbsExpression, | ||
) | ||
from pyomo.core.expr.relational_expr import ( | ||
RangedExpression, | ||
InequalityExpression, | ||
EqualityExpression, | ||
) | ||
from pyomo.core.base.var import VarData, ScalarVar | ||
from pyomo.core.base.param import ParamData, ScalarParam | ||
from pyomo.core.base.expression import ExpressionData, ScalarExpression | ||
from pyomo.repn.util import ExitNodeDispatcher | ||
|
||
|
||
def handle_var(node, collector): | ||
collector.variables[id(node)] = node | ||
return None | ||
|
||
|
||
def handle_param(node, collector): | ||
collector.params[id(node)] = node | ||
return None | ||
|
||
|
||
def handle_named_expression(node, collector): | ||
collector.named_expressions[id(node)] = node | ||
return None | ||
michaelbynum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def handle_external_function(node, collector): | ||
collector.external_functions[id(node)] = node | ||
return None | ||
|
||
|
||
def handle_skip(node, collector): | ||
return None | ||
|
||
|
||
collector_handlers = ExitNodeDispatcher() | ||
collector_handlers[VarData] = handle_var | ||
collector_handlers[ParamData] = handle_param | ||
collector_handlers[ExpressionData] = handle_named_expression | ||
collector_handlers[ScalarExpression] = handle_named_expression | ||
collector_handlers[ExternalFunctionExpression] = handle_external_function | ||
collector_handlers[NegationExpression] = handle_skip | ||
collector_handlers[PowExpression] = handle_skip | ||
collector_handlers[MaxExpression] = handle_skip | ||
collector_handlers[MinExpression] = handle_skip | ||
collector_handlers[ProductExpression] = handle_skip | ||
collector_handlers[MonomialTermExpression] = handle_skip | ||
collector_handlers[DivisionExpression] = handle_skip | ||
collector_handlers[SumExpression] = handle_skip | ||
collector_handlers[Expr_ifExpression] = handle_skip | ||
collector_handlers[UnaryFunctionExpression] = handle_skip | ||
collector_handlers[AbsExpression] = handle_skip | ||
collector_handlers[RangedExpression] = handle_skip | ||
collector_handlers[InequalityExpression] = handle_skip | ||
collector_handlers[EqualityExpression] = handle_skip | ||
collector_handlers[int] = handle_skip | ||
collector_handlers[float] = handle_skip | ||
|
||
|
||
class _ComponentFromExprCollector(StreamBasedExpressionVisitor): | ||
michaelbynum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def __init__(self, **kwds): | ||
self.named_expressions = {} | ||
self.variables = {} | ||
self.params = {} | ||
self.external_functions = {} | ||
super().__init__(**kwds) | ||
|
||
def exitNode(self, node, data): | ||
return collector_handlers[node.__class__](node, self) | ||
|
||
def beforeChild(self, node, child, child_idx): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I mentioned this in a previous comment, so repeating here so it is not lost for future development / reference) For performance reasons, it may be good to add a BeforeChildDispatcher as well (especially because this collector is specifically looking for leaf nodes). That said, this improvement can be deferred to a subsequent PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to do this as a separate PR. |
||
if id(child) in self.named_expressions: | ||
return False, None | ||
return True, None | ||
|
||
|
||
_visitor = _ComponentFromExprCollector() | ||
|
||
|
||
def collect_components_from_expr(expr): | ||
_visitor.__init__() | ||
_visitor.walk_expression(expr) | ||
return ( | ||
list(_visitor.named_expressions.values()), | ||
list(_visitor.variables.values()), | ||
list(_visitor.params.values()), | ||
list(_visitor.external_functions.values()), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is basically reimplementing a version of
ComponentSet
. Is there a reason not to re-use that object?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have found that doing this manually is faster. That is the only reason. I'm not sure if the difference is enough to justify or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try to get some numbers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be about twice as fast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK - but we ought to see why ComponentSet is slower and try to fix it there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't disagree, but I don't think that needs to be part of the PR. We can always change the underlying implementation of this later. None of this is exposed to the user.