-
Notifications
You must be signed in to change notification settings - Fork 8
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
Add fourier #49
Add fourier #49
Changes from 6 commits
5e58d74
290f223
0a57674
8ebf2dc
2908a5c
497b31f
332d555
17a05a9
00aaa95
e94b606
aa67d9c
adb5efb
a62712e
07bd9f6
b704c98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1182,16 +1182,20 @@ def evaluate_on_grid(self, n_samples: int) -> Tuple[NDArray, NDArray]: | |
class FourierBasis(Basis): | ||
"""Set of 1D Fourier basis. | ||
|
||
This class defines a cosine and negative sine basis (quadrature pair) | ||
with frequencies ranging 0 to max_freq. | ||
|
||
Parameters | ||
---------- | ||
n_freqs | ||
Number of frequencies. The number of basis function will be 2*n_freqs - 1. | ||
max_freq | ||
Highest frequency of the cosine, negative sine pairs. | ||
The number of basis function will be 2*max_freq + 1. | ||
""" | ||
|
||
def __init__(self, n_freqs: int): | ||
super().__init__(n_basis_funcs=2 * n_freqs - 1) | ||
def __init__(self, max_freq: int): | ||
super().__init__(n_basis_funcs=2 * max_freq + 1) | ||
|
||
self._frequencies = np.arange(n_freqs, dtype=np.float32) | ||
self._frequencies = np.arange(max_freq + 1, dtype=float) | ||
self._n_input_dimensionality = 1 | ||
|
||
def _check_n_basis_min(self) -> None: | ||
|
@@ -1202,15 +1206,15 @@ def _check_n_basis_min(self) -> None: | |
Raises | ||
------ | ||
ValueError | ||
If an insufficient number of basis element is requested for the basis type | ||
If an insufficient number of basis element is requested for the basis type. | ||
""" | ||
if self.n_basis_funcs < 1: | ||
raise ValueError( | ||
f"Object class {self.__class__.__name__} requires >= 1 basis elements. " | ||
f"Object class {self.__class__.__name__} requires >= 0 basis elements. " | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see the error message now matches the check, but shouldn't this actually at least 1 basis element? With |
||
f"{self.n_basis_funcs} basis elements specified instead" | ||
) | ||
|
||
def evaluate(self, sample_pts: NDArray) -> NDArray: | ||
def evaluate(self, sample_pts: ArrayLike) -> NDArray: | ||
"""Generate basis functions with given spacing. | ||
|
||
Parameters | ||
|
@@ -1225,25 +1229,25 @@ def evaluate(self, sample_pts: NDArray) -> NDArray: | |
|
||
Notes | ||
----- | ||
If the frequencies provided are np.arange(n_freq), convolving a signal | ||
of length n_samples with this basis is equivalent, but slower, | ||
then computing the FFT truncated to the first n_freq components. | ||
The frequencies are set to np.arange(max_freq+1), convolving a signal | ||
of length n_samples with this basis is equivalent, but slower, | ||
then computing the FFT truncated to the first max_freq components. | ||
|
||
Therefore, convolving a signal with this basis is equivalent | ||
to compute the FFT over sliding window. | ||
Therefore, convolving a signal with this basis is equivalent | ||
to compute the FFT over a sliding window. | ||
|
||
Examples | ||
-------- | ||
>>> import nemos as nmo | ||
>>> import numpy as np | ||
>>> n_samples, n_freqs = 1000, 10 | ||
>>> basis = nmo.basis.FourierBasis(n_freqs*2) | ||
>>> eval_basis = basis.evaluate(np.linspace(0, 1, n_samples)) | ||
>>> sinusoid = np.cos(3 * np.arange(0, 1000) * np.pi * 2 / 1000.) | ||
>>> conv = [np.convolve(eval_basis[::-1, k], sinusoid, mode='valid')[0] for k in range(2*n_freqs-1)] | ||
>>> fft = np.fft.fft(sinusoid) | ||
>>> print('FFT power: ', np.round(np.real(fft[:10]), 4)) | ||
>>> print('Convolution: ', np.round(conv[:10], 4)) | ||
>>> import nemos as nmo | ||
>>> import numpy as np | ||
>>> n_samples, max_freq = 1000, 10 | ||
>>> basis = nmo.basis.FourierBasis(max_freq) | ||
>>> eval_basis = basis.evaluate(np.linspace(0, 1, n_samples)) | ||
>>> sinusoid = np.cos(3 * np.arange(0, 1000) * np.pi * 2 / 1000.) | ||
>>> conv = [np.convolve(eval_basis[::-1, k], sinusoid, mode='valid')[0] for k in range(2*max_freq+1)] | ||
>>> fft = np.fft.fft(sinusoid) | ||
>>> print('FFT power: ', np.round(np.real(fft[:max_freq]), 4)) | ||
>>> print('Convolution: ', np.round(conv[:max_freq], 4)) | ||
Comment on lines
+1241
to
+1250
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a way in mkdocs to set this as a python codeblock? Can you remove the for example at the bottom here the arrows render and get copied: https://nemos.readthedocs.io/en/latest/reference/nemos/utils/#nemos.utils.pytree_map_and_reduce |
||
""" | ||
(sample_pts,) = self._check_evaluate_input(sample_pts) | ||
# assumes equi-spaced samples. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably explain orthogonal here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still think this. at least a foot note or link