3
votes

I want to calculate the determinant of mm subarrays of a mm*n dimensional arrays, and would like to do this in a fast/more elegant way. The brute-force approach works:

import numpy as n

array=n.array([[[0.,1.,2.,3.],[2,1,1,0]],[[0.5, 0.5,2,2],[0.5,1,0,2]]])
detarray=n.zeros(4)
for i in range(4):
    detarray[i]= n.linalg.det(array[:,:,i])

I would have tried doing this with apply_along_axis, but I know this is only for 1D arguments to the function, and so I presume I can't get this to work.

However, I thought apply_over_axes should also work:

n.apply_over_axes(n.linalg.det, array, [0,1])

but this gives me an error: "det() takes exactly 1 argument (2 given)"

Does anyone know why this doesn't work? If this type of calculation is really not possible with apply_over_axes, is there a better way to do this rather than the for-loop?

2
Those apply functions don't eliminate loops, they just hide them.hpaulj
apply_over_axes 'iterates' on a list of axes. Not over an axis but over the dimensions. Say you have a 3d array, and want to sum on axes 1 and 2. Now sum takes a tuple, but say it didn't. You'd have to sum on 2 and then on 1.hpaulj
Ok - so essentially what I've tried would iterate over the first and then the second axis, which doesn't make sense because I'd like it to act on both axes at the same time?mzzx

2 Answers

2
votes

Utilizing the transpose semantics of NumPy for 3D arrays, you can simply pass the transposed array to numpy.linalg.det() as in:

In [13]: arr = np.array([[[0.,1.,2.,3.], 
                          [2, 1, 1, 0]], 

                         [[0.5, 0.5,2,2],
                          [0.5, 1, 0, 2]]])

In [14]: np.linalg.det(arr.T)
Out[14]: array([-1. ,  0.5, -2. ,  6. ])

Performance wise this approach seems to be twice as fast as the other approach of manually moving the axes using numpy.moveaxis

In [29]: %timeit np.linalg.det(np.moveaxis(arr, 2, 0))
12.9 µs ± 192 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [30]: %timeit np.linalg.det(arr.T)
6.2 µs ± 136 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
1
votes

numpy.linalg.det is vectorized out of the box. All you need to do is move the outer axes to the left:

>>> np.linalg.det(np.moveaxis(array, 2, 0))
array([-1. ,  0.5, -2. ,  6. ])