Skip to content

Commit 5e4a493

Browse files
SaVoAMPSarah
andauthored
Fixed #552 Add mmotifs and aamp_mmoitfs Unit Tests (#553)
* [ADD] Added new test files * [ADD] Added pytests for 'mmotifs' and 'aamp_mmotifs' * [DEL] Deleted test templates for the multidimensional case * [ADD] Added function to find one motif in all dimensions * [FIX] Reran black and flake8 * [ADD] Added exclusion zone to function * [MOD] Don't use naive implementation * [ADD] Added arrange and assert to the first test function * [MOD] Changed assertions to 'almost_equal' * [ADD] Added match functions from 'test_motifs.py' * [ADD] Added dummy test * [DEL & ADD] Deleted test_match() and added test function with default parameters * [ADD] Added test function to find the top two motifs * [ADD] Added function to test 2-dimensional motif discovery * [FIX] Fixed formatting * [ADD] Added test function to find motifs with the 'max_motifs' parameter only * Revert "[ADD] Added test function to find motifs with the 'max_matches' parameter only" This reverts commit d169441. * [ADD] Added test function to find motifs with the 'max_distance' parameter * [ADD] Make copy of the matrix profile * [ADD] Also make copy of matrix profile in 'aamp_mmotifs.py' * [FIX] Corrected the expected motif subspaces * [ADD] Added function to test the two dimensional and non-normalized motif discovery * [MOD] Changed to naive matrix profile computation * [ADD] Added 'query_idx' parameter to ensure that the self-match is returned if the query is a part of the time series. * [MOD] Change subsequence length m and time series T * [MOD] Change to naive computation and change time series * [MOD] Reformat file * [ADD] Add docstring documentation * [MOD] Reformat file * [MOD] Modified docstring description for the 'query_idx' parameter * [ADD] Added 'query_idx' to 'aamp_motifs' * [ADD] Added 'query_idx' to function call of 'aamp_match' * [ADD] Added 'query_idx' to '_motifs' and '_aamp_motifs' * [ADD] Added test function for 'max_matches=None' * [FIX] Set 'max_matches=None' instead of 'max_distance=None' * [ADD] Add test function for cutoffs parameter * [ADD] Add test function for setting 'max_matches=None' * [ADD] Added comment to explain function in more detail * [ADD] Added test function for finding multiple motifs with the cutoffs parameter * [ADD] Added 'pragma: no cover' comment * [DEL] Remove comment * [MOD & DEL] Modified function ordering and removed comments * [MOD] Changed '_expected' to '_ref' * [MOD] Marked comparison values with '_cmp' * [MOD] Renamed functions * [ADD] Added function that tests when cutoffs are a list * [ADD] Also tested cutoffs as list in 'test_aamp_mmotifs' * [MOD] Changed function ordering * [MOD] Declare time series array once at the beginning * [MOD] Renamed functions * [ADD] Added function with default parameters * [FIX] Fixed function names Co-authored-by: Sarah <[email protected]>
1 parent 2e798e8 commit 5e4a493

File tree

8 files changed

+491
-63
lines changed

8 files changed

+491
-63
lines changed

stumpy/aamp_mmotifs.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ def aamp_mmotifs(
118118
max_motifs = 1
119119

120120
T, T_subseq_isfinite = core.preprocess_non_normalized(T, m)
121+
P = P.copy()
121122

122123
excl_zone = int(np.ceil(m / config.STUMPY_EXCL_ZONE_DENOM))
123124

@@ -159,7 +160,7 @@ def aamp_mmotifs(
159160
motif_value > cutoffs[k]
160161
or not np.isfinite(motif_value)
161162
or (isinstance(max_distance, float) and motif_value > max_distance)
162-
):
163+
): # pragma: no cover
163164
break
164165

165166
query_matches = aamp_match(
@@ -169,6 +170,7 @@ def aamp_mmotifs(
169170
max_matches=max_matches,
170171
max_distance=max_distance,
171172
atol=atol,
173+
query_idx=motif_idx,
172174
p=p,
173175
)
174176

stumpy/aamp_motifs.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ def _aamp_motifs(
118118
max_matches=None,
119119
max_distance=max_distance,
120120
atol=atol,
121+
query_idx=candidate_idx,
121122
p=p,
122123
)
123124

@@ -295,6 +296,7 @@ def aamp_match(
295296
max_distance=None,
296297
max_matches=None,
297298
atol=1e-8,
299+
query_idx=None,
298300
p=2.0,
299301
):
300302
"""
@@ -334,6 +336,14 @@ def aamp_match(
334336
The absolute tolerance parameter. This value will be added to `max_distance`
335337
when comparing distances between subsequences.
336338
339+
query_idx : int, default None
340+
This is the index position along the time series, `T`, where the query
341+
subsequence, `Q`, is located.
342+
`query_idx` should only be used when the matrix profile is a self-join and
343+
should be set to `None` for matrix profiles computed from AB-joins.
344+
If `query_idx` is set to a specific integer value, then this will help ensure
345+
that the self-match will be returned first.
346+
337347
p : float, default 2.0
338348
The p-norm to apply for computing the Minkowski distance.
339349
@@ -381,7 +391,11 @@ def max_distance(D):
381391

382392
matches = []
383393

384-
candidate_idx = np.argmin(D)
394+
if query_idx is not None:
395+
candidate_idx = query_idx
396+
else:
397+
candidate_idx = np.argmin(D)
398+
385399
while (
386400
D[candidate_idx] <= atol + max_distance
387401
and np.isfinite(D[candidate_idx])

stumpy/mmotifs.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ def mmotifs(
137137
max_motifs = 1
138138

139139
T, M_T, Σ_T = core.preprocess(T, m)
140+
P = P.copy()
140141

141142
excl_zone = int(np.ceil(m / config.STUMPY_EXCL_ZONE_DENOM))
142143

@@ -178,7 +179,7 @@ def mmotifs(
178179
motif_value > cutoffs[k]
179180
or not np.isfinite(motif_value)
180181
or (isinstance(max_distance, float) and motif_value > max_distance)
181-
):
182+
): # pragma: no cover
182183
break
183184

184185
query_matches = match(
@@ -189,6 +190,7 @@ def mmotifs(
189190
max_matches=max_matches,
190191
max_distance=max_distance,
191192
atol=atol,
193+
query_idx=motif_idx,
192194
normalize=normalize,
193195
p=p,
194196
)

stumpy/motifs.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ def _motifs(
119119
max_matches=None,
120120
max_distance=max_distance,
121121
atol=atol,
122+
query_idx=candidate_idx,
122123
)
123124

124125
if len(query_matches) > min_neighbors:
@@ -322,6 +323,7 @@ def match(
322323
max_distance=None,
323324
max_matches=None,
324325
atol=1e-8,
326+
query_idx=None,
325327
normalize=True,
326328
p=2.0,
327329
):
@@ -365,6 +367,14 @@ def match(
365367
The absolute tolerance parameter. This value will be added to `max_distance`
366368
when comparing distances between subsequences.
367369
370+
query_idx : int, default None
371+
This is the index position along the time series, `T`, where the query
372+
subsequence, `Q`, is located.
373+
`query_idx` should only be used when the matrix profile is a self-join and
374+
should be set to `None` for matrix profiles computed from AB-joins.
375+
If `query_idx` is set to a specific integer value, then this will help ensure
376+
that the self-match will be returned first.
377+
368378
normalize : bool, default True
369379
When set to `True`, this z-normalizes subsequences prior to computing distances.
370380
Otherwise, this function gets re-routed to its complementary non-normalized
@@ -433,7 +443,11 @@ def max_distance(D):
433443

434444
matches = []
435445

436-
candidate_idx = np.argmin(D)
446+
if query_idx is not None:
447+
candidate_idx = query_idx
448+
else:
449+
candidate_idx = np.argmin(D)
450+
437451
while (
438452
D[candidate_idx] <= atol + max_distance
439453
and np.isfinite(D[candidate_idx])

test.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ test_unit()
109109
check_errs $?
110110
pytest -x -W ignore::RuntimeWarning -W ignore::DeprecationWarning tests/test_motifs.py
111111
check_errs $?
112+
pytest -x -W ignore::RuntimeWarning -W ignore::DeprecationWarning tests/test_mmotifs.py
113+
check_errs $?
112114
pytest -x -W ignore::RuntimeWarning -W ignore::DeprecationWarning tests/test_gpu_mpdist.py
113115
pytest -x -W ignore::RuntimeWarning -W ignore::DeprecationWarning tests/test_snippets.py
114116
check_errs $?
@@ -132,6 +134,8 @@ test_unit()
132134
check_errs $?
133135
pytest -x -W ignore::RuntimeWarning -W ignore::DeprecationWarning tests/test_aamp_motifs.py
134136
check_errs $?
137+
pytest -x -W ignore::RuntimeWarning -W ignore::DeprecationWarning tests/test_aamp_mmotifs.py
138+
check_errs $?
135139
pytest -x -W ignore::RuntimeWarning -W ignore::DeprecationWarning tests/test_gpu_aampdist.py
136140
pytest -x -W ignore::RuntimeWarning -W ignore::DeprecationWarning tests/test_aampdist_snippets.py
137141
check_errs $?

0 commit comments

Comments
 (0)