Conversation
|
Review these changes at https://app.gitnotebooks.com/stumpy-dev/stumpy/pull/1132 |
| try: # pragma: no cover | ||
| import pyfftw | ||
|
|
||
| FFTW_IS_AVAILABLE = True |
There was a problem hiding this comment.
| FFTW_IS_AVAILABLE = True | |
| PYFFTW_IS_AVAILABLE = True |
There was a problem hiding this comment.
What happens when pyfftw is available but fftw is not installed? A user might think that they are both the same thing and that fftw is installed with pyfftw. We probably want a way to detect this to be safe
There was a problem hiding this comment.
It is okay to set PYFFTW_IS_AVAILABLE assuming that we also check that fftw is also installed
There was a problem hiding this comment.
What happens when
pyfftwis available butfftwis not installed? We probably want a way to detect this to be safe
It is okay to set
PYFFTW_IS_AVAILABLEassuming that we also check that fftw is also installed
Got the point 👍
| Notes | ||
| ----- | ||
| The class maintains internal caches of FFTW objects to avoid redundant planning | ||
| operations when called multiple times with similar-sized inputs and parameters. |
There was a problem hiding this comment.
Maybe add:
When `planning_flag=="FFTW_ESTIMATE"`, there will be no planning operation.
However, caching FFTW objects is still beneficial as the overhead of
creating those objects can be avoided.
There was a problem hiding this comment.
But planning_flag is not an input parameter to this class and seems irrelevant? i.e., the user has no control over it
There was a problem hiding this comment.
Okay, it looks like you can __call__ the class instance with the planning_flag
| if func_name.endswith("sliding_dot_product"): | ||
| out.append(func_name) | ||
|
|
||
| if sdp.FFTW_IS_AVAILABLE: # pragma: no cover |
There was a problem hiding this comment.
Here, in test_sdp.py, I am checking sdp.FFTW_IS_AVAILABLE instead of adding
try:
import pyfftw
FFTW_IS_AVAILABLE = True
except ImportError:
FFTW_IS_AVAILABLE = False
Note that if I use the try-except block here, flake8 will complain that pyfftw is imported but not used.
There was a problem hiding this comment.
@NimaSarajpoor I've left some comments for you to address. I think we can afford to be clearer since all of this pyfftw stuff will be hard to maintain. We should probably be as verbose (and add more comments cross referencing their docs as possible). I'll do another few passes after you've responded and made modifications. I think this pyfftw stuff needs to be crystal clear
| try: # pragma: no cover | ||
| import pyfftw | ||
|
|
||
| FFTW_IS_AVAILABLE = True |
There was a problem hiding this comment.
What happens when pyfftw is available but fftw is not installed? A user might think that they are both the same thing and that fftw is installed with pyfftw. We probably want a way to detect this to be safe
| try: # pragma: no cover | ||
| import pyfftw | ||
|
|
||
| FFTW_IS_AVAILABLE = True |
There was a problem hiding this comment.
It is okay to set PYFFTW_IS_AVAILABLE assuming that we also check that fftw is also installed
| A class to compute the sliding dot product using FFTW via pyfftw. | ||
|
|
||
| This class uses FFTW (via pyfftw) to efficiently compute the sliding dot product | ||
| between a query sequence Q and a time series T. It preallocates arrays and caches |
There was a problem hiding this comment.
"a query sequence, Q, and a time series, T"
| Preallocated complex-valued array for FFTW computations. | ||
|
|
||
| rfft_objects : dict | ||
| Cache of FFTW forward transform objects, keyed by |
There was a problem hiding this comment.
"transform object swith (next_fast_n, n_threads, planning_flag) as lookup keys"
There was a problem hiding this comment.
The arguments for these keys seem opaque to the user of the class because they would have no control over them
There was a problem hiding this comment.
The arguments for these keys seem opaque to the user of the class because they would have no control over them
n_threads and planning_flag are parameters of __call__. I need to add the Methods section to the docstring of class and add a description there. The tricky one is next_fast_n since users need to calculate that based on n=len(T).
| Notes | ||
| ----- | ||
| The class maintains internal caches of FFTW objects to avoid redundant planning | ||
| operations when called multiple times with similar-sized inputs and parameters. |
There was a problem hiding this comment.
But planning_flag is not an input parameter to this class and seems irrelevant? i.e., the user has no control over it
| Notes | ||
| ----- | ||
| The class maintains internal caches of FFTW objects to avoid redundant planning | ||
| operations when called multiple times with similar-sized inputs and parameters. |
There was a problem hiding this comment.
Okay, it looks like you can __call__ the class instance with the planning_flag
| ) | ||
| self.rfft_objects[key] = rfft_obj | ||
| else: | ||
| rfft_obj.update_arrays(real_arr, complex_arr) |
There was a problem hiding this comment.
What does this do? It looks like it's a pyfftw thing
| # Get or create FFTW objects | ||
| key = (next_fast_n, n_threads, planning_flag) | ||
|
|
||
| rfft_obj = self.rfft_objects.get(key, None) |
There was a problem hiding this comment.
Would it make sense to combine both:
key = (next_fast_n, n_threads, planning_flag)
rfft_obj = self.rfft_objects.get(key, None)
irfft_obj = self.irfft_objects.get(key, None)
if rfft_obj is None or irfft_obj is None:
rfft_obj = pyfftw.FFTW(
input_array=real_arr,
output_array=complex_arr,
direction="FFTW_FORWARD",
flags=(planning_flag,),
threads=n_threads,
)
irfft_obj = pyfftw.FFTW(
input_array=complex_arr,
output_array=real_arr,
direction="FFTW_BACKWARD",
flags=(planning_flag, "FFTW_DESTROY_INPUT"),
threads=n_threads,
)
self.rfft_objects[key] = rfft_obj
self.irfft_objects[key] = irfft_obj
else:
rfft_obj.update_arrays(real_arr, complex_arr)
irfft_obj.update_arrays(complex_arr, real_arr)
This also assumes that things MUST be done in pairs. There shouldn't be a case where rfft_obj is defined but irfft_obj is not defined, right? That would be suspect
| real_arr[:n] = T | ||
| real_arr[n:] = 0.0 | ||
| rfft_obj.execute() # output is in complex_arr | ||
| complex_arr_T = complex_arr.copy() |
There was a problem hiding this comment.
This line is confusing. It's not clear what complext_arr_T is and why a copy would make complex_arr be related to T
| rfft_obj.execute() # output is in complex_arr | ||
|
|
||
| # RFFT(T) * RFFT(Q) | ||
| np.multiply(complex_arr, complex_arr_T, out=complex_arr) |
There was a problem hiding this comment.
Here, your comment says RFFT(T) * RFFT*Q) but it is confusing to see complex_arr, complex_arr_T. So:
- Both are complex and not "real"
Qis not referenced
This is to address
PR 3described in #1118 (comment). Have copied the corresponding notes below: