Constraint: only store the original expression (not lower/body/upper)#3293
Constraint: only store the original expression (not lower/body/upper)#3293blnicho merged 26 commits intoPyomo:mainfrom
Conversation
(something caused gdpopt to set variables to int instead of floats)
Codecov ReportAttention: Patch coverage is
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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
emma58
left a comment
There was a problem hiding this comment.
This mostly looks good (nice sneaky FBBT additions!), but I have quibbles with the naming of normalize_constraint.
pyomo/core/base/constraint.py
Outdated
| body = value(self.body, exception=exception) | ||
| return body | ||
|
|
||
| def normalize_constraint(self): |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
to_bounded_expression()?
There was a problem hiding this comment.
Oooo, I think I like that one!
| # | ||
| # Normalize the incoming expressions, if we can | ||
| # |
There was a problem hiding this comment.
This is a very beautiful block of deleted code! :D
| self.assertEqual(model.c.lower, None) | ||
| self.assertEqual(model.c.lower, float('-inf')) |
There was a problem hiding this comment.
Is this change backwards compatible? Or were we testing for the existence of a bug?
There was a problem hiding this comment.
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.
mrmundt
left a comment
There was a problem hiding this comment.
I had one minor nitpick about a missed close-parenthesis but otherwise, I'm happy with it!
blnicho
left a comment
There was a problem hiding this comment.
I suspect that this section in pyomo.contrib.sensitivity_toolbox can also be simplified with this change:
|
|
||
| def rule(model): | ||
| return (float('inf'), model.x) | ||
| model = self.create_model(abstract=True).create_instance() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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() |
There was a problem hiding this comment.
Same question about just using ConcreteModel.
There was a problem hiding this comment.
Same as above: we certainly could change these tests, but I didn't to minimize (unnecessary) changes to old code.
Co-authored-by: Bethany Nicholson <[email protected]>
Co-authored-by: Bethany Nicholson <[email protected]>
|
@blnicho: I went ahead and updated the logic in |
|
@jsiirola I don't see the changes to |
…nly' into constraint-store-expr-only
Yup. Well - no. I ran |
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, andupper). 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 newnormalize_constraint()method (which is more efficient, as it avoids duplicate work and unnecessary calls toas_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:
_lower,_body, and_upperConstraintData attributes.argsas the only public API for getting at an expression's argumentsexpr(and notbody) to detect changed expressionscompute_bounds_on_expressionLegal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: