-
-
Notifications
You must be signed in to change notification settings - Fork 11.9k
ENH: add function to get broadcast shape from a given set of shapes. #17535
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
Add new function numpy.broadcast_shape which takes tuples for the shapes to be broadcast against each other. Return the broadcasted shape as a tuple. See numpy#17217
|
I was incidentally having a look at the same thing and ended up with the following implementation. It doesn't use the builtin functionality of numpy, but it also doesn't create any temporary arrays. Might be another option? def broadcast_shapes(*shapes):
"""
Evaluate the shape due to broadcasting any number of shapes against each other.
Parameters
----------
`*shapes` : tuples
The shapes to broadcast.
Returns
-------
broadcasted_shape : tuple
Shape of the array that would be obtained when broadcasting the shapes against each other.
Examples
--------
>>> broadcast_shapes((2, 1), (1, 3))
(2, 3)
"""
shapes = [shape if isinstance(shape, tuple) else (shape,) for shape in shapes
if shape is not None]
maxdim = max(map(len, shapes))
broadcast_shape = []
for i, sizes in enumerate(it.zip_longest(*map(reversed, shapes), fillvalue=1)):
sizes = set(sizes) | {1}
if len(sizes) > 2:
raise ValueError(f'unmatched dimensions at dimension {maxdim - i}')
broadcast_shape.append(max(sizes))
return tuple(reversed(broadcast_shape)) |
|
@tillahoffmann, with #17535 (comment) the temporary arrays all have 0 bytes of data, so don't really cost anything |
Co-authored-by: Eric Wieser <[email protected]>
|
Excellent, let's avoid the duplication of code in that case. Looking forward to having this functionality available natively in numpy. @madhulikajc, thank you. |
|
Thank you @tillahoffmann and @eric-wieser. One thing I notice from @tillahoffmann's code is that an integer is accepted as an array shape. My code will also do this as np.empty() accepts integers and converts them into rank one arrays. But I do not explicitly test for this in my tests. I will add tests to check for this functionality. |
Also update docstring to include both ints and tuples of ints as input
|
My only concern is that it might make sense to expose this more directly via a C API rather than with the strange workaround we've settled on here. That's always something that could be explored more in a future PR though. |
|
@eric-wieser, happy to explore exposing this functionality via a C API instead (i.e. the logic in |
|
I like the plan to explore C (although I agree it is not absolutely necessary). Especially if we can consolidate a bit of code in It is a bit unfortunate that I guess |
|
Needs a release note. Look in |
|
On C vs python, I think it makes sense to merge this as is, since it is a helpful addition and any C implementation can just be slotted in anyway, and would then have the tests all ready. |
Also fix up docstring details.
7ae4588 to
f763cc7
Compare
seberg
left a comment
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 should probably just put this in before it gets stalled for no good reason. Since it is an API addition, might want to ping the mailing list, but honestly, it is small enough to do that after the fact.
Unfortunately, I can't help but bikeshed the name: Do we have a preference for broadcast_shapes vs. broadcast_shape? For some reason I feel the first may be better, so just in case everyone leans that way. If not, either name is completely fine to be honest.
Co-authored-by: Sebastian Berg <[email protected]>
|
I prefer |
|
👍 to |
|
I suppose the question of plural vs. singular is whether we are referring to the return value or the input value(s). Btw. noticed one possible addition to the test: Making sure that no input also returns |
mattip
left a comment
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.
LGTM. One tiny nit, but can be ignored.
As for moving to C: definitely should go to another PR. The chain goes
- add to numpy/core/multiarray/all
- add a C function to
array_module_methods. There are lots of examples there on different arg parsing and signatures. If you are new to this, you might want to look at the Python docs together with the code examples. - rummage around the code base looking at things like
PyArray_Broadcaseto see if it can be refactored
Maybe the correct way to reason about this is in the opposite order: there is no reason to move things into C just for fun if no real advantage can be realized.
My thinking is we probably already have the code in C somewhere, and if we don't we might be able to refactor it out and use it elsewhere. It's certainly not worth a standalone C API if all it's used for is this function. |
Co-authored-by: Eric Wieser <[email protected]>
Co-authored-by: Eric Wieser <[email protected]>
Also move versionadded
Co-authored-by: Warren Weckesser <[email protected]>
|
Thanks for adding this. This looks good to me. The dtype=[] trick looks good (taking Eric's word that it uses no memory), as it will keep things like error messages the same. Though perhaps if the logic in C is ever factored out, it would make more sense for this to call the C function directly. |
|
One last thing. This should be indexed in the reference documents somewhere so it shows up when searching for |
In case you want more than my word: |
Good point. That seems like a decent place. To make it more discoverable, adding an entry to the |
rgommers
left a comment
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.
Docs also look good now. This is very nice to have, thanks @madhulikajc. Merging.
Add new function numpy.broadcast_shape which takes tuples
for the shapes to be broadcast against each other.
Return the broadcasted shape as a tuple.
See #17217