Skip to content

Expand documentation on broadcasting#2084

Open
ricardoV94 wants to merge 1 commit intopymc-devs:mainfrom
ricardoV94:broadcasting
Open

Expand documentation on broadcasting#2084
ricardoV94 wants to merge 1 commit intopymc-devs:mainfrom
ricardoV94:broadcasting

Conversation

@ricardoV94
Copy link
Copy Markdown
Member

@ricardoV94 ricardoV94 commented Apr 28, 2026

It would be good to have a place to link to for runtime broadcasting gotcha. Not sure this is the right place (tutorial?), but it's what showed up if I googled pytensor + broadcasting

Preview page: https://pytensor--2084.org.readthedocs.build/en/2084/tutorial/broadcasting.html

@ricardoV94 ricardoV94 changed the title Expand doc on broadcasting Expand documentation on broadcasting Apr 28, 2026
@ricardoV94 ricardoV94 force-pushed the broadcasting branch 2 times, most recently from 0af086f to 69e4e7f Compare April 28, 2026 10:12
Copy link
Copy Markdown
Member

@jessegrabowski jessegrabowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks nice, i nitpicked it a bit

shape), resulting in ``(1, 2)``, and then broadcast just like the
row in the figure.

Unlike NumPy, which does broadcasting dynamically, PyTensor needs
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this accurate? We have basically the same broadcasting rules as numpy, but we're more flexible because we don't require you to tell us the shapes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is accurate we do strictly less broadcasting than Numpy in the sense numpy will never fail with zeros(3, 1) + zeros(3, 3), whereas we will absolutely refuse if we didn't know that 1 was going to be a 1 in advance.

shape), resulting in ``(1, 2)``, and then broadcast just like the
row in the figure.

Unlike NumPy, which does broadcasting dynamically, PyTensor needs
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the right framing? We're the same as numpy, just more flexible because sometimes we don't know the shape of things, so there's a requirements to be more like numpy to get broadcasting to work

Copy link
Copy Markdown
Member Author

@ricardoV94 ricardoV94 Apr 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are less flexible. Numpy is eager, it always knows everything it needs. No symbolic/deferred stuff

>>> x_matrix = pt.matrix("x_matrix")
>>> x_matrix.type.shape
(None, None)
>>> y_col = pt.col("y_col")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider pt.tensor('y_col', shape=(None, 1)) to make it super explicit? That's all col is, i wouldn't want the example to give the impression this is the way you hanve to do it

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I show that below. Here I wanted to just show broadcast working. pt.col is not bad per se. I agree we never promote it. In this case it makes the document a bit easier to follow. Otherwise the solution to the gotcha below is just the working case above. Let me thing


>>> x_matrix = pt.matrix("x_matrix")
>>> y_matrix = pt.matrix("y_matrix")
>>> y_col = pt.specify_shape(y_matrix, (None, 1))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

illustrate the intermediate result case you mentioned as motivation

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYM? I don't want to complicate the example with an arbitrary Op that happens to lose static shape just to make the point. If that's what you had in mind....

Comment on lines +144 to +145
>>> y_vector = pt.vector("y_vector")
>>> out = x_matrix + pt.expand_dims(y_vector, 1) # or x_matrix + y_vector[:, None]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the "drop the axis" part here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we went for a vector instead of a matrix

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a drop before you even start. I can try to get a better title

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants