-
Notifications
You must be signed in to change notification settings - Fork 525
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
Allow initialization of indexed sets via a function returning a dict #3363
base: main
Are you sure you want to change the base?
Conversation
On my system, this is failing the style and unit tests, but that seems to be due to problems in the repository before I started. This pull request adds one more successful test and does not increase the number of failed tests. |
@mfripp - Please install the latest version of |
Thanks @mrmundt , I've done that. Looks like I accidentally used black 22.10.0 from elsewhere on my system instead of the newer one (24.8.0) I had just installed. The newer one seems happier with the Pyomo codebase overall and reformatted a few lines in this pull request, which I've now resubmitted. |
@mrmundt, it looks like this pull request passed all tests except "Jenkins CI / test (pull request)". When I click for details on that, I get a message that "pyomo-jenkins.sandia.gov took too long to respond". Is there some way I can run this test locally or something I can fix to get this unblocked? |
@mfripp our Jenkins infrastructure is currently down so you can ignore the failure for now. We'll rerun the tests when we get Jenkins running again. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #3363 +/- ##
=======================================
Coverage 88.63% 88.63%
=======================================
Files 879 879
Lines 100221 100223 +2
=======================================
+ Hits 88827 88829 +2
Misses 11394 11394
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
The PR finished all the tests successfully after @jsiirola tagged it as |
@mfripp - We talked about this PR last week in our weekly dev call, and we are waiting on some feedback from one of our core devs who is on travel. Thanks for submitting it! We'll hopefully have some comments for you soon. |
Thanks! Hope it’s able to go through. |
Fixes # .
Summary/Motivation:
It is possible to construct
Expression
(and possiblyConstraint
) objects by passing a rule function that constructs a dictionary containing all the elements needed to initialize theExpression
. Pyomo notices that this function only accepts one argument, marks it as constant==True and then laterIndexedComponent._construct_from_rule_using_setitem
calls the function, accepts the dict, and uses that to build the object. However, if you try something similar with indexed sets, it does not initialize correctly from the dictionary and doesn't give an error message. This pull request fixes theSet()
definition to allow initialization via a function that produces a dictionary or dict-like object.The examples below use this shared setup:
Here is the current behavior. Pyomo unexpectedly uses the keys of the dict as the return value for any index in
m.C
:Here is the new behavior after this change. Now
m.C
is the same asm.B
, which is the behavior you would expect if the rule function returns the same dictionary as was used to initializem.B
.The new initialization method allows more efficient and more readable code when initializing indexed sets. For example, my project often produces indexed sets from simple sets. The most readable approach to this is along these lines:
The Pyomo documentation shows another approach with similar performance:
Both of the above become very slow if there are thousands of Nodes and millions of Arcs, since they scan all Arcs for every Node. The Pyomo documentation recommends this to improve performance:
This requires users to understand
BuildActions
and to know that elements can be added to Sets after they are constructed, which is a different pattern from other components.The new functionality in this pull request enables a simpler way to efficiently construct indexed sets:
In the future, this could enable efficient construction of indexed sets via other methods that expect a component to be initialized from a function, e.g.,
(Currently this last example doesn't work because the
model.Set()
decorator passes the function toSet()
viarule
instead ofinitialize
.)Changes proposed in this PR:
Set.construct()
to call the rule function once and then use its result to initialize the indexed set ifself.is_indexed() and type(self._init_values._init) is ScalarCallInitializer
.self._init_values.constant()
and then checking whetherself._init_values._init
uses a function as its initializer. But there doesn't seem to be a clean way to do that, and in all these cases it looks likeself._init_values._init
is of typeScalarCallInitializer
. The submitted test will catch if this changes.pyomo.core.tests.unit.test_sets.TestSetErrors.test_setargs6
to verify that the code above produces the second behavior, not the first.Legal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: