-
Notifications
You must be signed in to change notification settings - Fork 517
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
pyomo.gdp: Adding gdp.bound_pretransformation
#2824
Conversation
…apping from the original model to the transformed one
…some bugs with the deferred deactivation of constraints
…ore than one variable bounded in the disjunction
…that is, we transform the upper or lower part, but not both
…ing to the tighter of them and the variable bounds when there's nothing better in the GDP hierarchy, adding tests
… relying on the leaves of the GDP tree
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #2824 +/- ##
==========================================
+ Coverage 87.16% 87.19% +0.02%
==========================================
Files 764 765 +1
Lines 88279 88471 +192
==========================================
+ Hits 76949 77138 +189
- Misses 11330 11333 +3
Flags with carried forward coverage won't be shown. Click here to find out more.
☔ View full report in Codecov by Sentry. |
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.
One thing that I think needs to be changed (using include_fixed=False
); I think everything else is optional.
# on v and wait for later | ||
if not is_root: | ||
v_bounds['to_deactivate'].add(constraint) | ||
elif len(list(identify_variables(constraint.body))) == 1: |
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 might want to consider another way to do this: as written, this will always walk the entire expression tree, even if it knows that the constraint can be skipped after the second variable. Maybe something like:
var_gen = identify_variables(constraint.body, include_fixed=False)
try:
next(var_gen)
except StopIteration:
continue
try:
next(var_gen)
continue
except StopIteration:
pass
repn = generate_standard_repn(constraint.body)
# ...
Also note that it is important to include include_fixed=False
, too...
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.
Ah, this makes sense. Is it bad style to just do everything in the except
block of the second try-except? (I'm doing that for now.)
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.
Also, good catch on the fixed Vars! I added a test.
… do anything about tightness, it just finds the non-None bound if there is one
… into gdp-bound-pretransformation
Fixes # .
Summary/Motivation:
Finally, it's here...! This PR adds a "pre-transformation" to pyomo.gdp that finds univariate constraints and transforms them according the the special transformation in the Balas 1988 paper. For example, given a disjunction that looks like this:
we create the transformed constraints,
(where$w_1$ and $w_2$ are the binary variables corresponding to Booleans $W_1$ and $W_2$ ).
This transformation will transform every univariate disjunctive Constraint for which is has enough information (defaulting to the tighter of variable bounds and bounds given in global constraints if the information is not on the disjunctive part of the model itself). All multivariate constraints and any univariate constraints on unbounded variables without corresponding constraints in other Disjuncts will remain active. In addition, this transformation does not go all the way to a MIP. It is intended to be followed by one of the GDP-to-MIP transformations to transform the logical parts of the model and any Constraints this transformation could not transform.
In terms of implementation, the basic idea is that, for each Disjunction, it does a first pass down the GDP hierarchy, keeping track of the tightest bound it finds there for any variables appearing in univariate Constraints. Then, for each of those variables, it attempts to construct two constraints: one for lower bounds and one for upper bounds, by finding the tightest bound at each of the leaf nodes in the hierarchy. It may have to traverse back upwards to find bounds in the ancestors of the leaf, but we cache them when we do this. If ever it cannot find a bound, it bails on the entire constraint. It accepts targets, and if the targets are nested in each other, it will transform from the highest target in the GDP tree (so the nested ones are essentially ignored).
Changes proposed in this PR:
gdp.bound_pretransformation
transformation.GDPTree
class to support traversing through the Disjuncts in a GDP forest, iterating through the leaves, etc.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: