Approach #1 : Here's an approach using strides
that uses the concept of views
without making actual copies to then perform subtraction from A
and as such should be quite efficient -
m,n = B.strides
m1,n1 = A.shape
m2,n2 = B.shape
s1,s2 = m1//m2, n1//n2
strided = np.lib.stride_tricks.as_strided
out = A - strided(B,shape=(s1,m2,s2,n2),strides=(0,n2*n,0,n)).reshape(A.shape)
Sample run -
In [78]: A
Out[78]:
array([[29, 53, 30, 25, 92, 10],
[ 2, 20, 35, 87, 0, 9],
[46, 30, 20, 62, 79, 63],
[44, 9, 78, 33, 6, 40]])
In [79]: B
Out[79]:
array([[35, 60],
[21, 86]])
In [80]: m,n = B.strides
...: m1,n1 = A.shape
...: m2,n2 = B.shape
...: s1,s2 = m1//m2, n1//n2
...: strided = np.lib.stride_tricks.as_strided
...:
In [81]: # Replicated view
...: strided(B,shape=(s1,m2,s2,n2),strides=(0,n2*n,0,n)).reshape(A.shape)
Out[81]:
array([[35, 60, 35, 60, 35, 60],
[21, 86, 21, 86, 21, 86],
[35, 60, 35, 60, 35, 60],
[21, 86, 21, 86, 21, 86]])
In [82]: A - strided(B,shape=(s1,m2,s2,n2),strides=(0,n2*n,0,n)).reshape(A.shape)
Out[82]:
array([[ -6, -7, -5, -35, 57, -50],
[-19, -66, 14, 1, -21, -77],
[ 11, -30, -15, 2, 44, 3],
[ 23, -77, 57, -53, -15, -46]])
Approach #2 : We can just reshape
both A
and B
to 4D
shapes with B
having two singleton dimensions along which its elements would be broadcasted
when subtracted from 4D
version of A
. After subtraction, we reshape back to 2D
for final output. Thus, we would have an implementation, like so -
m1,n1 = A.shape
m2,n2 = B.shape
out = (A.reshape(m1//m2,m2,n1//n2,n2) - B.reshape(1,m2,1,n2)).reshape(m1,n1)