-
Notifications
You must be signed in to change notification settings - Fork 518
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
Support validate / filter for IndexedSet components using the index #3338
Conversation
…s to the index. Original pyomo tests pass.
Valsetindexprior
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3338 +/- ##
=======================================
Coverage 88.53% 88.53%
=======================================
Files 868 868
Lines 98495 98550 +55
=======================================
+ Hits 87199 87248 +49
- Misses 11296 11302 +6
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
pyomo/core/base/set.py
Outdated
if self._validate.__class__ is ParameterizedIndexedCallInitializer: | ||
# TBD [JDS: 8/2024]: should we deprecate the "expanded | ||
# tuple" version of the validate callback for scalar sets? | ||
# It is widely used and we can (reasonably reliably) map to | ||
# the expected behavior... | ||
orig_fcn = self._validate._fcn | ||
self._validate = ParameterizedScalarCallInitializer( | ||
lambda m, v: orig_fcn(m, *v), True | ||
) | ||
|
||
if self._filter.__class__ is ParameterizedIndexedCallInitializer: | ||
# TBD [JDS: 8/2024]: should we deprecate the "expanded | ||
# tuple" version of the filter callback for scalar sets? | ||
# It is widely used and we can (reasonably reliably) map to | ||
# the expected behavior... | ||
orig_fcn = self._filter._fcn | ||
self._filter = ParameterizedScalarCallInitializer( | ||
lambda m, v: orig_fcn(m, *v), True | ||
) | ||
|
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.
Note that this code (silently) preserves the old API for scalar Set objects. However, that old API is
- difficult to reconcile with the "newer" API for IndexedSets
- widely used in Pyomo models in the wild
I think I'd like to put in a deprecation warning here, but that would generate a LOT of noise (basically, it would generate deprecation warnings for every model that uses filter=
or validate=
).
Thoughts (@blnicho, @mrmundt, @emma58, @michaelbynum, @whart222)?
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 mind disturbing users in the wild, but I want the deprecation warnings to be helpful / tell them how to change over their models. What is the new, preferred method for doing this? Either we detail that directly in the deprecation message or we make a new page on RTD that gives more detailed instructions.
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.
We had a long discussion at the Dev call today and decided that we should start issuing deprecation warnings for ScalarSets now.
…pecting expanded values
% (value, self.name) | ||
) | ||
yield value | ||
flag = fcn(block, idx, value) |
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.
Should the order of idx
and value
be switched?
flag = fcn(block, idx, *value) | ||
if flag: | ||
deprecation_warning( | ||
f"{self.__class__.__name__} {self.name}: '{mode}=' " | ||
"callback signature matched (block, *value, *index). " | ||
"Please update the callback to match the signature " | ||
"(block, value, *index).", | ||
version='6.7.4.dev0', |
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.
Same question here about the ordering of idx
and value
. The deprecation warning matches the argument ordering I was expecting but the call to fcn
does not. Is there magic happening in the initializer function call logic to swap argument ordering?
def __call__(self, parent, idx, *args): | ||
if idx.__class__ is tuple: | ||
return self._fcn(parent, *args, *idx) | ||
else: | ||
return self._fcn(parent, *args, idx) |
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 think I'm answering my own question by noticing that the argument ordering is indeed swapped here.
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.
@jsiirola there isn't really anything for you to resolve in my comments/questions, I just wanted to document my initial confusion. I am curious why the argument ordering and subsequent reordering was necessary. I'm guessing it had to do with having more control over the format of the index being passed in.
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.
Yeah - this turned into a bit of a mess (and yes, it is confusing that they were reversed). The calls you are seeing here are all internal to the Initializer methods, so the user is never aware of them. In this case, because the "Parameterized" initializer was new, the old fcn
API was just (block, index)
. Parameterized was adding "value", so it made sense for the extra arg to come at the end. This was further confused by the historical behavior where we would expand the value arg and pass it to the user-provided callback (leading things like the messy deprecation paths around set.py
lines 1482-1520), and leaving things like this seemed to require less code gymnastics to implement the deprecation paths and make things compatible with the rest of the Initializer system.
Fixes #2655; supersedes #3304
Summary/Motivation:
IndexedSet
components supported thevalidate
andfilter
arguments, but those callbacks didn't provide the index. This prevented index-specific behavior, which is inconsistent to other indexed components. This PR builds on (and supersedes) #3304 to provide the index to the callbacks for bothvalidate
andfilter
. This implementation extends the standardInitializer()
logic to support parameterized callbacks.More history
We have historically supported filters on multidimensional scalar sets that look like this:
This starts to break down if the Set itself is indexed:
And can become ambiguous if the IndexedSet index or the individual SetData objects are jagged sets.
To remove the ambiguity, this PR moves to a new paradigm where the "extra data" is not expanded when passed to the function:
Changes proposed in this PR:
validate
andfilter
argumentsvalidate
andfilter
arguments when used on IndexedSet objects (TBD: discussion as to if we should also deprecate that API for scalar Set objects)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: