-
Notifications
You must be signed in to change notification settings - Fork 132
Avoid default allocation for taps of length 1 in ScanSaveMem #1395
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
Conversation
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.
Pull Request Overview
This PR addresses an issue with the default scan buffer allocation for single-tapped outputs in ScanSaveMem and enhances the tests for buffer size validation. Key changes include:
- Adjusting the test configuration by excluding "scan_pushout" and renaming an internal function from f_rnn to step for clarity.
- Updating the implementation of default scan buffer handling by adding a new parameter (taps) to _is_default_scan_buffer and adapting buffer expansion and slicing logic accordingly.
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
File | Description |
---|---|
tests/scan/test_rewriting.py | Modified test configuration and assertions regarding scan buffer sizes and function naming. |
pytensor/scan/rewriting.py | Updated _is_default_scan_buffer's signature and revised buffer handling logic using the taps value. |
07908a3
to
75985df
Compare
return True | ||
bx = bx[-len(by) :] | ||
bx = bx[bx_len - by_len :] |
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 would fail with the infamous [-0:]
edge case
Only a minor float32 tolerance failure, so it's ready for review while I don't come and tweak it. |
pytensor/scan/rewriting.py
Outdated
init_value_ = atleast_Nd(init_value, n=init_buffer.ndim) | ||
return not broadcasted_by(init_value_[0], init_buffer[0]) |
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 get it, why are we expanding dims then immediately discarding the extra dims
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.
It may add more implicit dims, and we only want to discard the first one
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.
but [0] only selects the first one and discards all the rest?
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.
No, this is a tensorvariable, it selects into something with ndim-1 dimensions, it's basically a squeeze(x, 0) which I can use it it's more readable
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.
If I'm reading this right, it's keeping all extra dims after discarding the first one. But I don't know enough of the inner workings of the Scan
Op to understand what the two inputs are and how they interact
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.
Added a comment and switched to squeeze on the initval
x, y, *_ = node.inputs | ||
if not (x.owner is not None and isinstance(x.owner.op, AllocEmpty)): | ||
init_buffer, init_value, *_ = node.inputs | ||
if 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.
Is this the default buffer case? (just uninitialized)
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.
default buffer is an empty with enough space to hold all scan steps plus initial taps, initial taps are written to the beginning of the buffer with set_subtensor
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.
so is this the default case?
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 understand the question, if it's not an AllocEmpty it's not a default buffer, hence why it returns False. Am I missing something?
bd9e868
to
a2654fb
Compare
a2654fb
to
cb61b0c
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #1395 +/- ##
=======================================
Coverage 82.12% 82.12%
=======================================
Files 211 211
Lines 49722 49777 +55
Branches 8820 8830 +10
=======================================
+ Hits 40832 40881 +49
- Misses 6710 6715 +5
- Partials 2180 2181 +1
🚀 New features to boost your workflow:
|
The check we had for whether a variable was a default scan buffer (that's not also broadcasting the initval), always failed for single tapped outputs. There's a conservative check that the original value is not being broadcast to the number of initial taps, but that doesn't matter for single taps.
Also added some checks that we are actually only keeping buffers of the expected size in the test.
📚 Documentation preview 📚: https://pytensor--1395.org.readthedocs.build/en/1395/