Undescribed Horrific Abuse, One Victim & Survivor of Many gmkarl at gmail.com
Fri Nov 18 02:36:54 PST 2022

what's feeling reasonable for me here is having the real case function
differently from the imaginary case, in that (for now) the user must
.view(float) the complex frequency data to process it.

the idea of this code would be to bundle the functionality into a
class, so it doesn't matter too much what the interface is, but it is
nice ot have it small and somewhat repluggable with numpy.



Interestingly, numpy's real-domain FFT is not idempotent for
odd-lengthed vectors ! The code assumes an even-lengthed vector.

Here you can see me performing a numpy real-domain FFT on a
15-lengthed vector, then a numpy real-domain inverse FFT, and getting
back a 14-lengthed vector with differing content:
(Pdb) p np.fft.rfft(randvec[:15]).shape
(Pdb) p np.fft.irfft(np.fft.rfft(randvec[:15])).shape
(Pdb) p np.fft.irfft(np.fft.rfft(randvec[:15]))
array([0.56987486, 0.76018321, 0.64798214, 0.52903067, 0.52608816,
       0.63919848, 0.55517019, 1.19692063, 0.57350214, 0.66362825,
       0.74056271, 0.47672495, 1.03527704, 0.1288166 ])
(Pdb) p randvec[:15]
array([0.5488135 , 0.71518937, 0.60276338, 0.54488318, 0.4236548 ,
       0.64589411, 0.43758721, 0.891773  , 0.96366276, 0.38344152,
       0.79172504, 0.52889492, 0.56804456, 0.92559664, 0.07103606])

I checked the help and it explains that there are further parameters
to provide for this.

The help also mentions the Hermitian-symmetry i discovered and learned
of by poking at this.

    Returns the real valued `n`-point inverse discrete Fourier transform
    of `a`, where `a` contains the non-negative frequency terms of a
    Hermitian-symmetric sequence. `n` is the length of the result, not the

    If you specify an `n` such that `a` must be zero-padded or truncated, the
    extra/removed values will be added/removed at high frequencies. One can
    thus resample a series to `m` points via Fourier interpolation by:
    ``a_resamp = irfft(rfft(a), m)``.

    The correct interpretation of the hermitian input depends on the length of
    the original data, as given by `n`. This is because each input shape could
    correspond to either an odd or even length signal. By default, `irfft`
    assumes an even output length which puts the last entry at the Nyquist
    frequency; aliasing with its symmetric counterpart. By Hermitian symmetry,
    the value is thus treated as purely real. To avoid losing information, the
    correct length of the real input **must** be given.


i have a failing assertion at the start of the file now. on to make a
real-valued matrix.


array([[ 0.25-0.j  ,  0.25-0.j  ,  0.25-0.j  ,  0.25-0.j  ],
       [ 0.25-0.j  ,  0.  +0.25j, -0.25+0.j  , -0.  -0.25j],
       [ 0.25-0.j  , -0.25+0.j  ,  0.25-0.j  , -0.25+0.j  ],
       [ 0.25-0.j  , -0.  -0.25j, -0.25+0.j  ,  0.  +0.25j]])

thinking about how to do multiplication


so, basically the output is real * real - 2 * imag * imag
i'm making a matrix, but i'm confused about reshaping it.

the complex matrix has [freq, sample], where freq = sample = 28
the real matrix will have shape [freq_components, sample], where
freq_components = 30 (2*15).
the rows of the real matrix will be composed of alternating real and
imaginary parts. the imaginary parts will be negated. and for the
doubled frequencies, the imaginary parts will be doubled.

the axes are numbered starting from 0. so 0 = frequency, and 1 = sample.
numpy has "concatenate" and "stack" operations.

if i make a complex matrix, to troubleshoot-bisect clearly with
working code, i could then extract the real and imaginary components
with mat.real and mat.imag, or with .view(float) .
.view(float) would extract them adjacent to each-other in rows,
increasing the sample count in the matrix.
i want them adjacent to each-other in columns. and .view only works if
the matrix is not transposed, kinda.
so i might have the real and imaginary components as two separate matrices.
i then want to interweave the columns of those matrices. first one
value, then another.
a way to interwave is to place them adjacent to each other and then
reshape the matrix to exclude the dimension -- _i think_; i might be
misthinking something here.

for the reshape-to-interweave path, the matrix axis to be interwoven
needs to be the last one, or at least the smallest index in the
underlying storage.

so i might transpose the matrix, to make frequencies be rows, and
samples be columns.

once frequencies are rows, i would add a further dimension of complex
and imaginary.
this is equivalent to transposing and then doing .view()

i think a simple approach would be complex_mat.T.copy().view(float).T
. this then leaves out negating the imaginary component. so maybe not
using .view() could be clearer to implement. [i could also negate the
frequency passed to the wave function, or swap them]

here's what i've got:
>>> a
array([[1.+2.j, 3.+4.j],
       [5.+6.j, 7.+8.j]])
>>> np.stack([a.real.T, -a.imag.T], axis=-1).reshape(a.shape[1], a.shape[0]*2).T
array([[ 1.,  3.],
       [-2., -4.],
       [ 5.,  7.],
       [-6., -8.]])

.T is transpose.


More information about the cypherpunks mailing list