-
Notifications
You must be signed in to change notification settings - Fork 143
Graph replace #66
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
Graph replace #66
Conversation
94ae188
to
c4b00d6
Compare
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 like this a lot, but maybe we can improve the condation_subset
function a bit, or at least make it easier to understand what it actually does?
I would also slightly refactor |
97fc000
to
a54c53d
Compare
6de138d
to
f009a76
Compare
I've found that there are realistic cases where one replacement depends on another one. So far fg does these replacements inplace #74 and it is unsafe to apply subsequent replaces. This is the reason I have several passes in the The question what should user expect if some of the replacements were not applied? |
Ready for review |
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #66 +/- ##
==========================================
+ Coverage 74.22% 74.28% +0.05%
==========================================
Files 174 176 +2
Lines 48734 48991 +257
Branches 10367 10414 +47
==========================================
+ Hits 36175 36392 +217
- Misses 10272 10293 +21
- Partials 2287 2306 +19
|
@ricardoV94 @aseyboldt anything else left on the PR? |
I'm still not 100% sure about the helper function. Can we at least make it private, in case we want to further specify/change its exact behavior? |
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 really like out this is shaping. There are a bunch of places in pymc and pytensor I can see the use for this already.
Left some small suggestions and questions.
I don't see the reason for that. It's an alternative to |
pytensor/graph/basic.py
Outdated
def variable_is_in_ancestors( | ||
node: Variable, check: Union[Variable, Collection[Variable]] | ||
) -> bool: | ||
"""Determine if `node` is in the graph given by any in check. | ||
Parameters | ||
---------- | ||
node: Variable | ||
Node to check | ||
check: Collection[Variable] | ||
Nodes to check dependency on | ||
Returns | ||
------- | ||
bool | ||
""" | ||
if not isinstance(check, Collection): | ||
check = {check} | ||
else: | ||
check = set(check) | ||
return any(interim in check for interim in ancestors([node], blockers=check)) |
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 would perhaps inline this function, I think it's simple enough for users to figure out. We can add it later if there's a demand for it.
I am not totally set, so feel free to insist in keeping it
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 would like to merge this with is_in_ancestors
so it is more polymorphic
NodeT can be either Variable or Apply
is_in_ancestors(node[NodeT], search: Union[List[NodeT], NodeT]) -> bool
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.
What I was saying is I don't think we need this helper function at all. Ancestors gives you an iterable and you check if it is in it. I don't see why we need a user-facing function for the one-liner
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.
Okay, we already had one for nodes, so that opens a valid precedent for variables. I think we should keep separate. There's a subtle difference between checking for nodes and variables.
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 renamed the is_in_ancestors to apply_in_ancestors and unified the api with variable_in_ancestors so they are semantically the same. Should we bother with deprecation warnings and add a fallback?
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.
Yes I think we should bother with deprecation warning for this case. It's not too much trouble either.
I only meant the |
Don't see any reason to make the function private, there are many ways to use it outside pytensor Just a quick example I could imagine candidates = set(model.basic_RVs)
conditioned_on = {
rv: candidates & truncated_graph_inputs([rv], candidates - {rv})
for rv in cmodel.basic_RVs
} Another thing that there is no private function in the basic.py, so any function is assumed to be freely accessible |
5cd9cd3
to
c9fd99b
Compare
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.
@ferrine I am happy with the code! Just some corrections/nitpicks below. Also, can you clean the git history + add deprecation for is_in_ancestors
(if you didn't already)?
tests/graph/test_replace.py
Outdated
) | ||
|
||
|
||
def test_graph_replace(): |
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.
Can you group these in a class like clone_replace
73007a6
to
17e189f
Compare
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.
17e189f
to
42b403a
Compare
42b403a
to
7b4cb73
Compare
Who will press the green button? |
Thanks @ricardoV94 for your tremendous help and support with this pr. Also thanks @aseyboldt for pointing out algorithmic issues when it started out |
Looking forward to using this! |
dependent = variable_depends_on(node, blockers - {node}) | ||
# ancestors to include that are present in the graph (not disconnected) | ||
# should be added to truncated_inputs | ||
truncated_inputs.append(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.
@ferrine I think we can end up with duplicated inputs here. If another node has duplicated truncated inputs?
Oops, I didn't see these messages! |
Implementing graph replace that preserves references from the inherited graph (#19)
Here are a few important guidelines and requirements to check before your PR can be merged:
pre-commit
is installed and set up.Don't worry, your PR doesn't need to be in perfect order to submit it. As development progresses and/or reviewers request changes, you can always rewrite the history of your feature/PR branches.
If your PR is an ongoing effort and you would like to involve us in the process, simply make it a draft PR.