-
Notifications
You must be signed in to change notification settings - Fork 559
Model Observer #3695
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
base: main
Are you sure you want to change the base?
Model Observer #3695
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3695 +/- ##
==========================================
+ Coverage 89.27% 89.33% +0.05%
==========================================
Files 896 898 +2
Lines 103687 104291 +604
==========================================
+ Hits 92567 93166 +599
- Misses 11120 11125 +5
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:
|
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.
Overall, this is great. I have lots of questions, but very few should prohibit merging this.
|
|
||
|
|
||
| def handle_var(node, collector): | ||
| collector.variables[id(node)] = node |
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.
This is basically reimplementing a version of ComponentSet. Is there a reason not to re-use that object?
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.
I have found that doing this manually is faster. That is the only reason. I'm not sure if the difference is enough to justify or not.
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.
I'll try to get some numbers.
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.
This seems to be about twice as fast.
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.
OK - but we ought to see why ComponentSet is slower and try to fix it there.
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.
I don't disagree, but I don't think that needs to be part of the PR. We can always change the underlying implementation of this later. None of this is exposed to the user.
| elif v.value != _value: | ||
| vars_to_update.append(v) |
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.
Don't you also have to update a constraint if the var is fixed?
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.
I think this is up to the observer... Worth a discussion.
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.
I'm not sure what the best solution is for this. I think we need more use cases. I vote we leave it for now. Here are a couple of examples that make this challenging:
m.x * (m.y + m.z) == 0
m.x.fix(1)
opt.solve(m)
m.x.value = 2
Right now, this works fine for the existing persistent solver interfaces because changing the value of m.x does not change the "structure" of the constraint (i.e., it remains linear). Essentially, we create a "mutable linear coefficient" and update that coefficient when the value of the variable changes. Furthermore, this does not require reconstructing the constraint in any way. However,
(m.y + m.z) ** m.x == 0
m.x.fix(1)
will currently just result in an exception for both gurobi persistent and highs persistent (scip is fine).
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.
I've been thinking about this more. Maybe we can add some public methods to ModelChangeDetector to provide more information if necessary. For example, we could add a method to get all of the constraints used by a variable (the change detector already has that data).
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.
@jsiirola and I discussed this further and agreed that expanding the interface was a good solution. I added to the docstring explaining that we can expand the interface, but we are waiting to do so until we have a need.
|
Additional question: who is responsible for verifying that the model does not contain any active components that the Solver/Observer doesn't know how to handle? This appears to assume that the responsibility lies with the Solver, but does that mean that the solver needs to also implement it's own observer to make sure that "unallowable" active components aren't added between solves? That makes me think that maybe the responsibility needs to lie in this observer... |
|
Okay, should be good to go, @jsiirola (assuming tests pass). |
|
Okay, I did a fairly substantial refactor of this. The I'm still working on adding more tests, but I think this should be ready for review. |
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.
Add a test for nested Expressions (done)
|
I think I am finally done with this PR. |
Summary/Motivation:
This PR extracts the code for detecting changes in models from the persistent solver interfaces to its own, independent functionality. I will have a separate PR shortly that removes the code from the solver interfaces and updates them to use this code.
Changes proposed in this PR:
observerthat can be used to inform other classes of what changed in a pyomo model.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: