Question: I have 2 numpy array, one is of shape `(2, 5, 10)` and the other one is of shape `(2, 5, 10, 10)`.
The multiplication I want is `[[x1[i][j] * x2[i][j] for j in range(5)] for i in range(2)]`
It works as intended but is pretty slow, and I wanted to multiply directly x1 \* x2 but numpy does not like that.
Is there a numpy method to multiply over a given axis?

I have tried the numpy.multiply for it says 'axis' is an invalid keyword to ufunc 'multiply'

```py
x1 = np.arange(100).reshape((2, 5, 10))
x2 = np.arange(1000).reshape((2, 5, 10, 10))
x = [[x1[i][j] * x2[i][j] for j in range(5)] for i in range(2)] # slow method that works
x = np.multiply(x1, x2, axis=2) # What I'm looking for but doesn't work.

```

Answer: 
```
[[x1[i][j] * x2[i][j] for j in range(5)] for i in range(2)]

```

uses broadcasting as well.

```
x1[i,j].shape    # (10,)
x2[i,j].shape    # (10,10)

```

To multiply them, broadcasting rules add a dimension:

```
(10,), (10,10) => (1,10), (10,10) => (10,10)

```

But if you had used `x1[i,j,:,None]`, the broadcasting would go

```
(10,1), (10,10) => (10,10)

```

producing different numbers.

In @norok2's format this is the difference between:

```
x1[:, :, None, :] * x2
x1[:, :, :, None] * x2

```

The difference would be more obvious if `x2` had shape (2,5,8,10) or (2,5,10,8).
Using your input we have

`x1.shape
(2, 5, 10)
x2.shape
(2, 5, 10, 10)`

You can use np.tensordot to multiply along arbitrary axes - as long as they match in dimension.

`x3 = np.tensordot(x1, x2, (1,1))
x3.shape
(2, 10, 2, 10, 10)`

tensordot also allows you to multiply and add over a number of axes. In this case for example.

`x4 = np.tensordot(x1,x2, ((0,1), (0,1)))
x4.shape
(10, 10, 10)`

Here the argument ((0,1), (0,1)) refers to the axes of x1 and x2, respectively.