Between shapes () and (1,), why can I perform regular but not in-place operations?

193 views Asked by At

When I try to broadcast in-place from shape (1,) to shape (), numpy raises ValueError: non-broadcastable output operand with shape () doesn't match the broadcast shape (1,). I understand that inplace operators are intended to operate on the same chunk of memory as the input, which is why you couldn't, say, broadcast in-place from shape (5,) into shape (1,). But in the case of shape (1,) (or any array with size 1), the size is the same as a scalar with shape (). Then why can't I not perform an in-place operation a += b where a has shape () and b has shape (1,)? The opposite does work.

Code:

a = np.array(0)
b = np.array([0])
a + b  # valid
b += a  # valid
a += b  # ValueError

Result:

ValueError: non-broadcastable output operand with shape () doesn't match the broadcast shape (1,)
1

There are 1 answers

0
hpaulj On

While we can get some ideas from testing, it may come down to implementation details (in compiled code).

In [81]: a = np.array(0); b = np.array([0])
In [82]: a,b
Out[82]: (array(0), array([0]))

Sum of the two produces a (1,) array. That is consistent with broadcasting rules. a is broadcasted to (1,) and then summed.

In [83]: a+b
Out[83]: array([0])

We can add a () or scalar:

In [84]: a += a

but your error case, apparently is trying to put this (1,) sum into the () target:

In [85]: a += b
Traceback (most recent call last):
  File "<ipython-input-85-294cacd62d6f>", line 1, in <module>
    a += b
ValueError: non-broadcastable output operand with shape () doesn't match the broadcast shape (1,)

It is possible, with the right index, to assign a value to a:

In [86]: a[:] = 1
Traceback (most recent call last):
  File "<ipython-input-86-aa15caba710a>", line 1, in <module>
    a[:] = 1
IndexError: too many indices for array: array is 0-dimensional, but 1 were indexed

In [87]: a[()] =2
In [88]: a
Out[88]: array(2)
In [89]: a[...] = a+b
In [90]: a
Out[90]: array(2)

But apparently the += kind of assignment does use this more generalized assignment approach.

We can't += a (1,1) into a (1,) either:

In [92]: b += np.array([[1]])
Traceback (most recent call last):
  File "<ipython-input-92-15b525996e5d>", line 1, in <module>
    b += np.array([[1]])
ValueError: non-broadcastable output operand with shape (1,) doesn't match the broadcast shape (1,1)

Apparently += kind of assignment can't reduce the number dimensions.

As a side note, it is possible to broadcast a (1,) array into a (0,)

In [100]: c = np.zeros((0,))
In [101]: c += b
In [102]: c
Out[102]: array([], dtype=float64)

That is, in the 2nd broadcasting step (after matching number of dimensions) it can reduce the size 1 dimension to 0. But that's different from changing the number of dimensions from n+1 to n.