Factorable Programming for PWL Approximations#3821
Conversation
…dd auxiliary variables
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3821 +/- ##
==========================================
+ Coverage 90.10% 90.11% +0.01%
==========================================
Files 904 905 +1
Lines 107113 107424 +311
==========================================
+ Hits 96512 96808 +296
- Misses 10601 10616 +15
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…sive_substitution
jsiirola
left a comment
There was a problem hiding this comment.
Some minor comments, but really a pretty big design question.
I think I will advocate refactoring this to avoid the node_to_var_map and degree_map entirely and restrict/repurpose the use of the substitution_map to only managing Expression objects (and maybe caching 1/x?). The issue is that as written, those maps will end up with one entry for every node of every expression of the entire model. Even for modest models, that will be huge. The other reason to not make the dict is that the only time things will be looked up in the maps will be when processing the parent node of the node in context. I think a simpler / more efficient approach will be to return a tuple of (node, degree, varset) from exitNode.
I am still working through verifying the logic in the addition/product/division/pow handlers.
|
Thanks for the review, @jsiirola. I agree with most of your feedback. However, I am going to push back on the refactor for the following reasons:
I'll push fixes for your other comments shortly. |
jsiirola
left a comment
There was a problem hiding this comment.
OK. I don't think I found any real problems with this code. I still feel strongly that the handlers should return tuples of (node, degree, varlist) and not rely on huge dictionaries to pass that information up the tree, but that can be implemented in a subsequent PR.
If you have a chance, it would be nice to resolve the unreachable code in the product handler before merging.
|
|
||
| for con in constraints: | ||
| lower, body, upper = con.to_bounded_expression(evaluate_bounds=True) | ||
| new_body = visitor.walk_expression(body) |
There was a problem hiding this comment.
This makes sense, as the walker always rebuilds the expression, even if nothing is being substituted out. In the future, we should consider revisiting this design and borrow ideas from the ExpressionReplacementVisitor and only recreate the (parts of the) expressions that we have to in order to effect the substitutions.
| if arg1_nvars > 1 or visitor.aggressive_substitution: | ||
| arg1 = visitor.create_aux_var(arg1) | ||
| arg1_vars = (arg1,) | ||
| arg1_nvars = 1 | ||
| arg1_degree = 1 | ||
|
|
||
| if arg2_nvars > 1 or visitor.aggressive_substitution: | ||
| arg2 = visitor.create_aux_var(arg2) | ||
| arg2_vars = (arg2,) | ||
| arg2_nvars = 1 | ||
| arg2_degree = 1 |
There was a problem hiding this comment.
This gets repeated a lot. Should it be a helper function?
There was a problem hiding this comment.
I'm not sure there is anything for a helper function to do here? This is really just creating local variables. All of the "work" is already in visitor.create_aux_var.
Summary/Motivation:
The purpose of this module/transformation is to convert any nonlinear model
to the following form:
By doing so, each nonlinear function is only a function of one or two variables.
If this transformation is used prior to the nonlinear_to_pwl transformation,
it can, in some cases, significantly reduce the complexity of the PWL approximation.
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: