3
votes

My problem is as follows:

import numpy as np

# given are two arrays of random, different shapes and sizes
a = np.array(...).reshape(?)
b = np.array(...).reshape(?)

# if broadcasting a and b works
c = a * b
# I want to guarantee that the assignment of the result works too
a += c

This obviously works if a.shape == (a * b).shape, but fails if b is the larger array.

I therefore want in the case that the broadcasting result is larger than a, to summarize over the broadcasted axes using some reduce operator.

An example of the expected output of the new broadcast_inverse() function that I need:

a = np.array(2)
b = np.ones(6).reshape(2, 3)

# broadcasting a to the shape of b
c = a * b

# now invert the broadcast by accumulating the excess axes
d = broadcast_inverse(c, a.shape, reduce_fun=np.sum)
assert a.shape == d.shape

print('a=', a) # a= 2
print('b=', b) # b= [[1 1 1] [1 1 1]]
print('c=', c) # c= [[2 2 2] [2 2 2]]
print('d=', d) # d= 12

A second example using 2D arrays and a different reduce function:

a = np.array([[2], [3]])
b = np.arange(2*3).reshape(2, 3) + 1

# broadcasting a to the shape of b
c = a * b

# broadcast inversion using the big product over the excess axes
d = broadcast_inverse(c, a.shape, reduce_fun=np.prod)
assert a.shape == d.shape

print('a=', a) # a= [[2] [3]]
print('b=', b) # b= [[1 2 3] [4 5 6]]
print('c=', c) # c= [[ 2  4  6] [12 15 18]]
print('d=', d) # d= [[  48], [3240]]

I tried doing it by iterating over shapes, but it turned out to be quite difficult to do bug free. So I was hoping somebody knows if numpy exposes over which axes it will perform a broadcast or maybe somebody knows some other cool efficient trick. I actually would also be happy with a function that doesn't support scalar arrays and different reduce functions. broadcast_inverse only needs to support arrays larger than 0D and the sum reduce function.

if we add,lets say 0 padding to a so that its shape is equal to b . then ??Vaibhav gusain
Vaibhavgusain, I didn't explain that part. This is an iterative scheme where 'a' has to be of constant shape, determined at the start. The shape of 'b' might change, each iteration. In order to add padding, I also would need to know the shapes of 'b' in advance, which I don't. It's difficult to explain, but changing 'a' would result in a mess.iHaveNoIdeaWhatImDoing
so if thats the case then you might want to make sure that b.shape is equal to or less then that of a.shapeVaibhav gusain
The problem there is, that this whole thing is recursive and the 'b's themselves are 'a's sometimes. And the only thing that solves the problem would be by forcing the user to make the inputs in the correct shape and make them calculate the correct connecting shapes. But I want everything to be easy of use, so I want to be able to write something like 'ComplexThing(dependencies) + 1', where the user input is '1' but the shape can't be computed because it is determined by the N many things that use this new expression as dependency.iHaveNoIdeaWhatImDoing
but for that complexthing(dependencies)+1 you atleast need to make sure that either your c or b shapes are fixed cause from that you can easily compute the other's shapeVaibhav gusain