Quantum Fourier transform

In this notebook we will see how we can use the quantum fourier transformation (qft) for analysing classical data.

[1]:
import numpy as np
from trainsum.numpy import trainsum as ts
import matplotlib.pyplot as plt
[2]:
# "Acquisition" parameters
sw = 5000.0            # spectral width in Hz
dt = 1.0 / sw          # dwell time (s)
N = 2048               # number of acquired points
noise_level = 0.02     # noise level
t = np.arange(N) * dt  # time starts at 0

# Resonance parameters
freqs0 = np.array([500.0, 1200.0, 800.0])    # resonance offsets in Hz
amps   = np.array([1.0, 0.7, 0.5])           # relative amplitudes
Tg     = np.array([0.02, 0.015, 0.03])       # Gaussian decay constants in s
phases = np.deg2rad([0.0, 30.0, -45.0])      # phases in radians

# Build simulated FID: sum of Gaussian-decaying complex exponentials
fid = np.zeros(N, dtype=complex)
for A, f0, Tg_i, phi in zip(amps, freqs0, Tg, phases):
    fid += A * np.exp(-t / Tg_i) * np.exp(1j * (2 * np.pi * f0 * t + phi))
fid += noise_level * np.random.randn(N)

# plot data
plt.figure(figsize=(6,4))
plt.plot(t * 1e3, np.real(fid))
plt.xlabel("Time (ms)")
plt.ylabel("Signal (a.u.)")
plt.xlim(0, 100)
plt.grid()
plt.show()
../_images/examples_quantum_fourier_transform_2_0.png

In this cell we define some “realistic” data as is often encountered in spectroscopy. The data is some decaying oscillatory signal which can be converted to a spectrum with lorentzian peaks with a fourier transformation

[3]:
# FFT -> frequency domain spectrum
spec = np.fft.fft(fid)
freqs = np.fft.fftfreq(N, d=dt)

# shift zero frequency to center
spec_shift = np.fft.fftshift(spec)
freqs_shift = np.fft.fftshift(freqs)

# plot results
plt.figure(figsize=(6,4))
plt.plot(freqs_shift, np.abs(spec_shift) / np.max(np.abs(spec_shift)))
plt.xlim(-2500, 2500)
plt.xlabel("Frequency (Hz)")
plt.ylabel("Normalized magnitude")
plt.grid()
plt.show()
../_images/examples_quantum_fourier_transform_4_0.png

To get a reference to compare the qft results with, as a first step, we perform the fast fourier transformation on the data and plot the spectrum.

[4]:
# define shape and compression parameter
shape = ts.trainshape(*fid.shape)

# compress the data
with ts.variational(max_rank=3, cutoff=1e-10, ncores=2, nsweeps=2):
    fid_train = ts.tensortrain(shape, fid)

# plot data
plt.figure(figsize=(6,4))
plt.plot(t * 1e3, np.real(fid), label="original")
plt.plot(t * 1e3, np.real(fid_train.to_tensor()), color="black", linestyle="dotted", label="approximated")
plt.xlabel("Time (ms)")
plt.ylabel("Signal (a.u.)")
plt.xlim(0, 100)
plt.grid()
plt.legend()
plt.show()
../_images/examples_quantum_fourier_transform_6_0.png

To apply the qft to the data we first convert the data to a tensor train. We do this by creating a variational conext manager which leads to a maximum rank of 3. As we can see we only need a very small rank for a good representation of the data.

[5]:
# create the compressed dft matrix as tensor network with rank 16
qft = ts.qft(shape.dims[0])

# apply the qft to the compressed data
with ts.variational(max_rank=5, cutoff=1e-10, ncores=2, nsweeps=2):
    spec_train = qft @ fid_train
spec_shift_train = ts.qftshift(spec_train)

# plot data
plt.figure(figsize=(6,4))
spec_shift_approx = spec_shift_train.to_tensor()
plt.plot(freqs_shift, np.abs(spec_shift) / np.max(np.abs(spec_shift)), label="shift")
plt.plot(freqs_shift, np.abs(spec_shift_approx) / np.max(np.abs(spec_shift_approx)), color="black", label="approximated")
plt.xlim(-2500, 2500)
plt.xlabel("Frequency (Hz)")
plt.ylabel("Normalized magnitude")
plt.grid()
plt.legend()
plt.show()
../_images/examples_quantum_fourier_transform_8_0.png

After creating the tensorized qft matrix we can variationally apply it to the compressed data, also resulting in a low rank approximation.