Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Oversampling Feature #84

Merged
merged 22 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions docs/coefficients.ipynb

Large diffs are not rendered by default.

116 changes: 110 additions & 6 deletions docs/coefficients.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,122 @@
# Coefficients

A characteristic property of any Fourier model are its coefficients.
Our package can, given a model, calculate the corresponding coefficients by utilizing the [Pennylane Fourier Coefficients](https://docs.pennylane.ai/en/stable/_modules/pennylane/fourier/coefficients.html) method.
Our package can, given a model, calculate the corresponding coefficients.

In the simplest case, this could look as follows:
```python
from qml_essentials.model import Model
from qml_essentials.coefficients import Coefficients

model = Model(
n_qubits=2
n_layers=1
circuit_type="HardwareEfficient",
n_qubits=2,
n_layers=1,
circuit_type="Hardware_Efficient",
)

coeffs = Coefficients.sample_coefficients(model)
```
coeffs = Coefficients.get_spectrum(model)
```

But wait! There is much more to this. Let's keep on reading if you're curious :eyes:.

## Detailled Explanation

To visualize what happens, let's create a very simplified Fourier model
```python
class Model_Fct:
def __init__(self, c, f):
self.c = c
self.f = f
self.degree = max(f)

def __call__(self, inputs, **kwargs):
return np.sum([c * np.cos(inputs * f) for f, c in zip(self.f, self.c)], axis=0)
```

This model takes a vector of coefficients and frequencies on instantiation.
When called, these coefficients and frequencies are used to compute the output of the model, which is the sum of sine functions determined by the length of the vectors.
Let's try that for just two frequencies:

```python
freqs = [1,3]
coeffs = [1,1]

fs = max(freqs) * 2 + 1
model_fct = Model_Fct(coeffs,freqs)

x = np.arange(0,2 * np.pi, 2 * np.pi/fs)
out = model_fct(x)
```

We can now calculate the [Fast Fourier Transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform) of our model:
```python
X = np.fft.fft(out) / len(out)
X_shift = np.fft.fftshift(X)
X_freq = np.fft.fftfreq(X.size, 1/fs)
X_freq_shift = np.fft.fftshift(X_freq)
```
Note that calling `np.fft.fftshift` is not required from a technical point of view, but makes our spectrum nicely zero-centered and projected correctly.

![Model Fct Spectr](model_fct_spectr_light.png#only-light)
![Model Fct Spectr](model_fct_spectr_dark.png#only-dark)

The same can be done with our framework, with a neat one-liner:
```python
X_shift, X_freq_shift = Coefficients.get_spectrum(model_fct, shift=True)
```

![Model Fct Spectr Ours](model_fct_spectr_ours_light.png#only-light)
![Model Fct Spectr Ours](model_fct_spectr_ours_dark.png#only-dark)

Note, that applying the shift can be controlled with the optional `shift` argument.

## Increasing the Resolution

You might have noticed that we choose our sampling frequency `fs` in such a way, that it just fulfills the [Nyquist criterium](https://en.wikipedia.org/wiki/Nyquist_frequency).
Also the number of samples `x` are just enough to sufficiently represent our function.
In such a simplified scenario, this is fine, but there are cases, where we want to have more information both in the time and frequency domain.
Therefore, two additional arguments exist in the `get_spectrum` method:
- `mfs`: The multiplier for the highest frequency. Increasing this will increase the width of the spectrum
- `mts`: The multiplier for the number of time samples. Increasing this will increase the resolution of the time domain and therefore "add" frequencies in between our original frequencies.
- `trim`: Whether to remove the Nyquist frequency if spectrum is even. This will result in a symmetric spectrum

```python
X_shift, X_freq_shift = Coefficients.get_spectrum(model_fct, mfs=2, mts=3, shift=True)
```

![Model Fct Spectr OS](model_fct_spectr_os_light.png#only-light)
![Model Fct Spectr OS](model_fct_spectr_os_dark.png#only-dark)

Note that, as the frequencies change with the `mts` argument, we have to take that into account when calculating the frequencies with the last call.

Feel free to checkout our [jupyter notebook](https://github.com/quantum-machine-learning/qml_essentials/blob/main/docs/notebooks/coefficients.ipynb) if you would like to play around with this.

A sidenote on the performance; Increasing the `mts` value effectively increases the input lenght that goes into the model.
This means that `mts=2` will require twice the time to compute, which will be very noticable when running noisy simulations.

## Power spectral density

In some cases it can be useful to get the [power spectral density (PSD)](https://en.wikipedia.org/wiki/Spectral_density).
As calculation of this metric might differ between the different research domains, we included a function to get the PSD of a given spectrum using the following formula:

$$
PSD = \frac{2 (\mathrm{Re}(F)^2+\mathrm{Im}(F)^2)}{n_\text{samples}^2}
$$

where $F$ is the spectrum and $n_\text{samples}$ the length of the input vector.

```python
model = Model(
n_qubits=4,
n_layers=1,
circuit_type="Circuit_19",
random_seed=1000
)

coeffs, freqs = Coefficients.get_spectrum(model, mfs=1, mts=1, shift=True)

psd = Coefficients.get_psd(coeffs)
```

![Model PSD](model_psd_light.png#only-light)
![Model PSD](model_psd_dark.png#only-dark)
Binary file added docs/model_fct_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/model_fct_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/model_fct_spectr_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/model_fct_spectr_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/model_fct_spectr_os_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/model_fct_spectr_os_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/model_fct_spectr_ours_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/model_fct_spectr_ours_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/model_psd_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/model_psd_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading