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

Constraint: only store the original expression (not lower/body/upper) #3293

Merged
merged 26 commits into from
Jul 30, 2024

Conversation

jsiirola
Copy link
Member

Fixes # .

Summary/Motivation:

This PR changes the internal data store for Constraint to only store the original (relational) expression and not break it apart into (lower, body, and upper). Those attributes are preserved as properties (and the expression rearrangement happens dynamically when they are called). Most clients who need the expression "standardized" should use the new normalize_constraint() method (which is more efficient, as it avoids duplicate work and unnecessary calls to as_numeric()).

While this would appear to be strictly cosmetic, this change is the first part of the templatized LP writer.

Changes proposed in this PR:

  • remove _lower, _body, and _upper ConstraintData attributes
  • enforce the use of .args as the only public API for getting at an expression's arguments
  • update persistent interfaces to look at the expr (and not body ) to detect changed expressions
  • update FBBT to use the same relational expression handling logic as compute_bounds_on_expression

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.

@blnicho blnicho requested a review from emma58 June 25, 2024 19:00
@blnicho blnicho self-requested a review July 2, 2024 19:08
Copy link

codecov bot commented Jul 10, 2024

Codecov Report

Attention: Patch coverage is 90.84967% with 14 lines in your changes missing coverage. Please review.

Project coverage is 88.32%. Comparing base (92a9d3b) to head (2529557).
Report is 4 commits behind head on main.

Files Patch % Lines
pyomo/gdp/plugins/bilinear.py 0.00% 4 Missing ⚠️
pyomo/core/base/constraint.py 94.73% 3 Missing ⚠️
pyomo/contrib/fbbt/fbbt.py 96.29% 2 Missing ⚠️
pyomo/gdp/plugins/cuttingplane.py 50.00% 2 Missing ⚠️
pyomo/contrib/appsi/base.py 75.00% 1 Missing ⚠️
pyomo/contrib/solver/persistent.py 80.00% 1 Missing ⚠️
pyomo/core/kernel/constraint.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3293      +/-   ##
==========================================
- Coverage   88.49%   88.32%   -0.18%     
==========================================
  Files         868      868              
  Lines       98436    98419      -17     
==========================================
- Hits        87115    86928     -187     
- Misses      11321    11491     +170     
Flag Coverage Δ
linux 85.76% <90.84%> (-0.57%) ⬇️
osx 75.29% <89.54%> (-0.35%) ⬇️
other 86.31% <90.84%> (-0.21%) ⬇️
win 83.84% <90.84%> (+0.01%) ⬆️

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

@emma58 emma58 left a comment

Choose a reason for hiding this comment

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

This mostly looks good (nice sneaky FBBT additions!), but I have quibbles with the naming of normalize_constraint.

body = value(self.body, exception=exception)
return body

def normalize_constraint(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I like the name of this method... 'Normalize' makes me think of means of 1 and stuff... Could it be tupelize_constraint or something?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not too keen on tuplize_constraint (it doesn't feel very descriptive); but I agree that normalize_constraint is probably worse. What about standardize_constraint or to_range_constraint (the latter is a bit misleading as it does not return a RangedExpression)?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmmm. I actually kind of like to_range_constraint... Maybe to_tuple (also not super descriptive, but since we accept Constraint expressions in that form, it kinda makes sense)? I don't love standardize_constraint because of potential standard form connotations, but I do like it a lot better than normalize_constraint!

Copy link
Member Author

Choose a reason for hiding this comment

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

to_bounded_expression()?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oooo, I think I like that one!

Copy link
Member Author

Choose a reason for hiding this comment

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

Implemented!

Comment on lines -425 to -427
#
# Normalize the incoming expressions, if we can
#
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a very beautiful block of deleted code! :D

pyomo/core/kernel/constraint.py Outdated Show resolved Hide resolved
Comment on lines -137 to +171
self.assertEqual(model.c.lower, None)
self.assertEqual(model.c.lower, float('-inf'))
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change backwards compatible? Or were we testing for the existence of a bug?

Copy link
Member Author

Choose a reason for hiding this comment

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

We were testing for the existence of a bug: By (current) convention, lower returns the expression for the lower bound and lb returns the value (or None if not specified or -inf). The problem was that in the old world order, some bounds processing was done "early" in set_value -- including some of the conversion from +/-inf to None. We now defer that processing until the user asks for the value. So, in this test, since the user provided the "expression" float('-inf') for the lower bound, the "correct" thing to do is return that expression from lower.

@jsiirola jsiirola requested review from emma58 and mrmundt July 24, 2024 16:58
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.

I had one minor nitpick about a missed close-parenthesis but otherwise, I'm happy with it!

Copy link
Member

@blnicho blnicho left a comment

Choose a reason for hiding this comment

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

I suspect that this section in pyomo.contrib.sensitivity_toolbox can also be simplified with this change:

# Constraint must be a ranged inequality, break into


def rule(model):
return (float('inf'), model.x)
model = self.create_model(abstract=True).create_instance()
Copy link
Member

Choose a reason for hiding this comment

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

Why not change this to just create a ConcreteModel? Are there behaviors we expect with AbstractModels that are no longer being tested because of this change?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we could just switch this over to a ConcreteModel. I left it as it was mostly to minimize changes in old code / tests. The biggest reason for the change here was to remove the test for an exception on model creation, as this PR defers the bounds sanity checking until later (model compilation step and not model construction step).


model.c = Constraint(rule=rule)
self.assertRaises(ValueError, model.create_instance)
model = self.create_model(abstract=True).create_instance()
Copy link
Member

Choose a reason for hiding this comment

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

Same question about just using ConcreteModel.

Copy link
Member Author

Choose a reason for hiding this comment

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

Same as above: we certainly could change these tests, but I didn't to minimize (unnecessary) changes to old code.

pyomo/core/base/constraint.py Outdated Show resolved Hide resolved
pyomo/core/base/constraint.py Outdated Show resolved Hide resolved
jsiirola and others added 4 commits July 29, 2024 07:46
Co-authored-by: Bethany Nicholson <blnicho@users.noreply.github.com>
Co-authored-by: Bethany Nicholson <blnicho@users.noreply.github.com>
@jsiirola
Copy link
Member Author

@blnicho: I went ahead and updated the logic in sens.py, but did not rework the use of abstract models in the old Constraint tests

@blnicho
Copy link
Member

blnicho commented Jul 29, 2024

@jsiirola I don't see the changes to sens.py. Did you forget to push?

@jsiirola
Copy link
Member Author

@jsiirola I don't see the changes to sens.py. Did you forget to push?

Yup. Well - no. I ran git push but it was rejected because I had clicked the "update branch" button and I didn't notice. .

@blnicho blnicho merged commit f46da7f into Pyomo:main Jul 30, 2024
32 checks passed
@jsiirola jsiirola deleted the constraint-store-expr-only branch August 2, 2024 01:12
jsiirola added a commit to jsiirola/pyomo that referenced this pull request Aug 13, 2024
bknueven pushed a commit to bknueven/pyomo that referenced this pull request Aug 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

4 participants