Skip to content
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

Overhaul the Suffix component #3072

Merged
merged 25 commits into from
Jan 29, 2024
Merged

Conversation

jsiirola
Copy link
Member

Fixes # .

Summary/Motivation:

Recent development (in parmest / DoE) highlighted that the set of admissible inputs to Suffix(initialize=...) was more restrictive than the rest of Pyomo. This moves Suffix to use the standard Initializer framework (for consistency / flexibility).

As part of this work, this PR does a general update / refresh of the entire component:

  • remove Suffix APIs that have been deprecated since Pyomo 4.x
  • add AbstractSuffix class to prevent using the Suffix before it has been constructed
  • move to Enums instead of class attributes for Suffix direction / data type.
  • remove (very) redundant code in suffix generator functions
  • improve Suffix code coverage

And some (necessary) general improvements:

  • port _pop_from_kwargs from IndexedComponent to Component
  • improve comparison in ComponentMap.__eq__

Changes proposed in this PR:

  • (see above)

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:

  1. I agree my contributions are submitted under the BSD license.
  2. I represent I am authorized to make the contributions and grant the license. If my employer has rights to intellectual property that includes these contributions, I represent that I have received permission to make contributions and grant the required license on behalf of that employer.

Copy link

codecov bot commented Dec 18, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (f7fa223) 88.26% compared to head (0ce77fb) 88.29%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3072      +/-   ##
==========================================
+ Coverage   88.26%   88.29%   +0.02%     
==========================================
  Files         832      832              
  Lines       92301    92243      -58     
==========================================
- Hits        81474    81447      -27     
+ Misses      10827    10796      -31     
Flag Coverage Δ
linux 86.05% <100.00%> (+0.02%) ⬆️
osx 75.55% <100.00%> (+0.01%) ⬆️
other 86.23% <100.00%> (+0.02%) ⬆️
win 83.45% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@mrmundt mrmundt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few questions and a few changes.

pyomo/common/collections/component_map.py Show resolved Hide resolved
pyomo/common/collections/component_map.py Outdated Show resolved Hide resolved
args = [
arg
for arg in (kwargs.pop(name, notset) for name in namelist)
if arg is not notset
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above: !=?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we do not want to look for equivalence (==) but identity (is): we are looking to collect all arguments that the user specified. This is almost equivalent to

args = []
for key in kwargs:
    if key not in kwargs:
        continue
    arg = kwargs[key]
    if arg is not notset:
        args.append(arg)

dict.get(key, notset) is not notset is a shorthand for testing __contains__ before fetching the value.

Additionally, besides performance (is is faster than ==), the thing we worry about is the values can be Components, and we override __eq__ for a lot of components to perform expression generation -- which we don't want to trigger here.

yield name, suffix
def suffix_generator(a_block, datatype=NOTSET, direction=NOTSET, active=None):
_iter = a_block.component_map(Suffix, active=active).items()
if direction is not NOTSET:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above: !=?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we are testing if the optional argument direction was provided by the user -- that is, is it ANYTHING other than the default value from the function specification. is is faster, more appropriate, and avoids possible side effects due to __eq__ overrides.

pyomo/core/base/suffix.py Outdated Show resolved Hide resolved
pyomo/core/base/suffix.py Outdated Show resolved Hide resolved
pyomo/core/base/suffix.py Show resolved Hide resolved
@jsiirola jsiirola requested a review from mrmundt December 21, 2023 19:02
Copy link
Contributor

@michaelbynum michaelbynum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one small concern.

return set((type(val), id(val)) for val in self) == set(
(type(val), id(val)) for val in other
)
return all(id(key) in self._data for key in other)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about keys in self but not other?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is covered by the test above (if len(self) != len(other)): if their lengths are the same, then you only have to test one side (because neither can have duplicates). I will break that test apart to make it a little more clear

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh! Got it!

@blnicho blnicho self-requested a review January 23, 2024 19:57
@blnicho blnicho merged commit a02be9d into Pyomo:main Jan 29, 2024
33 checks passed
@jsiirola jsiirola deleted the suffix-class-update branch January 29, 2024 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants