A simple illustration of broadcasting - 1d with a scalar:
In [18]: x = np.arange(10)
In [19]: X,Y = np.broadcast_arrays(x,3)
In [20]: X
Out[20]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [21]: Y
Out[21]: array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
In [22]: Y.strides
Out[22]: (0,)
In [23]: X+Y
Out[23]: array([ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
In [24]: [i+j for i,j in zip(X,Y)]
Out[24]: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
With this construction it's hard to prove that Y
doesn't take up as much memory as x
. So let's expand x
instead:
In [30]: x1 = np.broadcast_to(x,(3,10))
In [31]: x1
Out[31]:
array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])
In [32]: x.__array_interface__
Out[32]:
{'data': (30797968, False),
'strides': None,
'descr': [('', '<i8')],
'typestr': '<i8',
'shape': (10,),
'version': 3}
In [33]: x1.__array_interface__
Out[33]:
{'data': (30797968, True),
'strides': (0, 8),
'descr': [('', '<i8')],
'typestr': '<i8',
'shape': (3, 10),
'version': 3}
x1
shares x
databuffer. No additional memory is used (except for the array object itself).
And make the scalar into a 2d:
In [34]: x2,y2 = np.broadcast_arrays(x1,Y)
In [35]: y2.strides
Out[35]: (0, 0)