Dear Shashang, Like the others, I fear that this super-broadcasting would be confusing. In the end, if you have a.shape = (2, 3) and b.shape = (4, 3), it is unclear why element 0 of a should broadcast against 0 and 1 of B rather than 0 and 2. And numpy should refuse the temptation to guess!
I think in a realistic case code should "know" whether to treat B as two pairs of A stacked together or interleaved, and hence either do, a + b.reshape((-1,) + a.shape) or a[:, np.newaxis] + b.reshape(a.shape[0] + (-1,) + a.shape[1]) (both followed by a reshape of the result). But I guess the more general question would be why, if there is a logical reason to do the first rather than the secondm, b would not have a different shape in the first place Overall, I guess one could write a generalized `broadcast_arrays` to follow your suggestion, but I think it would always have to also change the dimensions, which would, in my opinion, be too surprising to include in numpy. All the best, Marten Shasang Thummar via NumPy-Discussion <numpy-discussion@python.org> writes: > Dear NumPy Developers, > > > I hope you are doing well. I am writing to propose an enhancement to NumPy’s > broadcasting mechanism that could make the library even more powerful, > intuitive, and flexible while maintaining its memory efficiency. > > ## **Current Broadcasting Rule and Its Limitation** > As per NumPy's current broadcasting rules, if two arrays have different > shapes, the smaller array can be expanded **only if one of its dimensions is > 1**. This allows memory-efficient expansion of data without copying. However, > if a dimension is greater than 1, NumPy **does not** allow expansion to a > larger size, even when the larger size is a multiple of the smaller size. > > ### **Example of Current Behavior (Allowed Expansion)** > ```python > import numpy as np > > A = np.array([[1, 2, 3]]) # Shape (1,3) > B = np.array([[4, 5, 6], # Shape (2,3) > [7, 8, 9]]) > > C = A + B # Broadcasting works because (1,3) can expand to (2,3) > print(C) > > Output: > > [[ 5 7 9] > [ 8 10 12]] > > Here, A has shape (1,3), which is automatically expanded to (2,3) without > copying because a > dimension of size 1 can be stretched. > > However, NumPy fails when trying to expand a dimension where N > 1, even if > the larger > size is a multiple of N. > > Example of a Case That Fails (Even Though It Could Work) > > A = np.array([[1, 2, 3], # Shape (2,3) > [4, 5, 6]]) > > B = np.array([[10, 20, 30], # Shape (4,3) > [40, 50, 60], > [70, 80, 90], > [100, 110, 120]]) > > C = A + B # Error! NumPy does not allow (2,3) to (4,3) > > This fails with the error: > > ValueError: operands could not be broadcast together with shapes (2,3) (4,3) > > Why Should This Be Allowed? > > If a larger dimension is an exact multiple of a smaller one, then logically, > the smaller array can > be repeated along that axis without physically copying data—just like NumPy > does when > broadcasting (1,M) to (N,M). > > In the above example, > > * A has shape (2,3), and B has shape (4,3). > * Since 4 is a multiple of 2 (4 % 2 == 0), A could be logically repeated 2 > times to become > (4,3). > * NumPy already does a similar expansion when a dimension is 1, so why not > extend the same > logic? > > Proposed Behavior (Expanding N → M When M % N == 0) > > Allow an axis with size N to be broadcast to size M if and only if M is an > exact multiple of N (M % N > == 0). This is just as memory-efficient as the current broadcasting rules > because it can be done > using stride tricks instead of copying data. > > Example of the Proposed Behavior > > If NumPy allowed this new form of broadcasting: > > A = np.array([[1, 2, 3], # Shape (2,3) > [4, 5, 6]]) > > B = np.array([[10, 20, 30], # Shape (4,3) > [40, 50, 60], > [70, 80, 90], > [100, 110, 120]]) > > # Proposed new broadcasting rule > C = A + B > > print(C) > > Expected Output: > > [[ 11 22 33] > [ 44 55 66] > [ 71 82 93] > [104 115 126]] > > This works by logically repeating A to match B’s shape (4,3). > > Why This is a Natural Extension of Broadcasting > > * Memory Efficiency: Just like broadcasting (1,M) to (N,M), this expansion > does not require > physically copying data. Instead, strides can be adjusted to logically > repeat elements, making > this as efficient as current broadcasting. > * Intuitiveness: Right now, broadcasting already surprises new users. If > (1,3) can become > (2,3), why not (2,3) to (4,3) when 4 is a multiple of 2? This would be a > more intuitive rule. > * Extends Current Functionality: This is not a new concept—it extends the > existing rule where > 1 can be stretched to any value. We are generalizing it to any factor > relationship (N → M when M > % N == 0). > > Implementation Considerations > > The logic behind NumPy’s current broadcasting already uses stride tricks for > memory-efficient > expansion. Extending it to handle (N, M) → (K, M) (where K % N == 0) would > require: > > * Updating np.broadcast_shapes(), np.broadcast_to(), and related functions. > * Extending the existing logic that handles expanding 1 to support factors as > well. > * Ensuring backward compatibility and maintaining performance. > > Conclusion > > I strongly believe this enhancement would make NumPy’s broadcasting more > powerful, more > intuitive, and just as efficient as before. The fact that we can implement > this without > unnecessary copying makes it a natural extension of the existing feature. > > I would love to hear the NumPy team’s thoughts on this! If the community is > open to it, I am also > interested in contributing to its implementation. > > Looking forward to your feedback! > > Best regards, > Shasang Thummar > shasangthumm...@gmail.com > > [2:text/plain Hide] > > _______________________________________________ > NumPy-Discussion mailing list -- numpy-discussion@python.org > To unsubscribe send an email to numpy-discussion-le...@python.org > https://mail.python.org/mailman3/lists/numpy-discussion.python.org/ > Member address: m...@astro.utoronto.ca _______________________________________________ NumPy-Discussion mailing list -- numpy-discussion@python.org To unsubscribe send an email to numpy-discussion-le...@python.org https://mail.python.org/mailman3/lists/numpy-discussion.python.org/ Member address: arch...@mail-archive.com