it's seemed good for me to be able to spend so much time pursuing this, despite such i'm just poking at the details of what a dft is more. i found that this expression of moving v into frequency space and back into time space: ((v * mat).sum(axis = 1) * mat.T).sum(axis=1) / 2 can be expressed more simply with basic matrix multiplication: v @ mat @ mat.T / 2 much more concise! 0500 so of course the above v @ mat @ mat.T / 2 implies the matrix's transpose is its inverse. it looks like the inverse is the transpose over 2:
mat.T array([[ 1.+0.0000000e+00j, 1.+0.0000000e+00j], [ 1.-0.0000000e+00j, -1.-1.2246468e-16j]]) np.linalg.inv(mat) array([[ 0.5+3.061617e-17j, 0.5-3.061617e-17j], [ 0.5-3.061617e-17j, -0.5+3.061617e-17j]]) np.linalg.inv(mat) array([[ 0.5+3.061617e-17j, 0.5-3.061617e-17j], [ 0.5-3.061617e-17j, -0.5+3.061617e-17j]])
np.linalg.norm(mat) 2.0
The convention, with fft, is to divide by the frequency count after returning to the time domain, but it looks like things could be optimized if the frequency data were already treated this way:
v array([0.71733727, 0.82679766]) v @ (mat/2**.5) @ (mat.T/2**.5) array([0.71733727-5.06267555e-17j, 0.82679766+5.73292713e-17j]) v @ (mat/2**.5) @ np.linalg.inv((mat/2**.5)) array([0.71733727+0.j, 0.82679766+0.j])
that is, if the frequency product matrix is divided by the square root of the frequency count, then the discrete fourier transform is simply a matrix multiplication. 0505 i'm thinking of trying going through by hand a very simple thing, to try to look at the details of angle selection better.