From 5c7a4fe8ac72495dd2f77f547178d172651e67d4 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Thu, 4 May 2023 19:49:18 -0400 Subject: [PATCH 01/18] Empty commit From 40c093f7e5f359300c013739c6bab57b1995f6d0 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Fri, 5 May 2023 21:39:49 -0400 Subject: [PATCH 02/18] Improve the clarity of test function --- tests/test_motifs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index ca3766d1b..2c6e810eb 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -112,8 +112,8 @@ def test_motifs_max_matches(): # This test covers the following: # A time series contains motif A at four locations and motif B at two. - # If `max_motifs=2` the result should contain only the top two matches of motif A - # and the top two matches of motif B as two separate motifs. + # If `max_moitf=2` and `max_matches=2`, the result should contain two + # sets of motifs. Each motif set contains top two matches T = np.array( [ 0.0, # motif A @@ -141,7 +141,7 @@ def test_motifs_max_matches(): ] ) m = 3 - max_motifs = 3 + max_motifs = 2 left_indices = [[0, 7], [4, 11]] left_profile_values = [ From e33c490d95cb5cdf65a830785ceb359fdb90f9a1 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Fri, 5 May 2023 22:08:56 -0400 Subject: [PATCH 03/18] add test function to expose error --- tests/test_motifs.py | 67 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 2c6e810eb..f4d68b823 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -170,6 +170,73 @@ def test_motifs_max_matches(): npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) +def test_motifs_max_matches_max_distances_inf(): + # This test covers the following: + + # A time series contains motif A at four locations and motif B at two. + # If `max_moitf=3` and `max_matches=2`, and `max_distance=inf`, the + # result should contain three motifs, each containing top-two matches. + # Hence, two out of the three motifs are from A, and the other is from + # B. + T = np.array( + [ + 0.0, # motif A + 1.0, + 0.0, + 2.3, + -1.0, # motif B + -1.0, + -2.0, + 0.0, # motif A + 1.0, + 0.0, + -2.0, + -1.0, # motif B + -1.03, + -2.0, + -0.5, + 0.0, # motif A + 1.0, + 0.0, + 2.3, + 0.0, # motif A + 1.0, + 0.0, + ] + ) + m = 3 + max_motifs = 3 + max_matches = 2 + max_distance = np.inf + + left_indices = [[0, 7], [15, 19], [4, 11]] + left_profile_values = [ + [0.0, 0.0], + [0.0, 0.0], + [ + 0.0, + naive.distance( + core.z_norm(T[left_indices[1][0] : left_indices[1][0] + m]), + core.z_norm(T[left_indices[1][1] : left_indices[1][1] + m]), + ), + ], + ] + + # set `row_wise` to True so that we can compare the indices of motifs as well + mp = naive.stump(T, m, row_wise=True) + right_distance_values, right_indices = motifs( + T, + mp[:, 0], + max_motifs=max_motifs, + max_distance=max_distance, + cutoff=np.inf, + max_matches=max_matches, + ) + + npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) + npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) + + def test_naive_match_exclusion_zone(): # The query appears as a perfect match at location 1 and as very close matches # (z-normalized distance of 0.05) at location 0, 5 and 9. From 7a0facce2edebc0d223655b7339fba0df435bccc Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Fri, 5 May 2023 23:30:13 -0400 Subject: [PATCH 04/18] fix bug, expecting no error --- stumpy/motifs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stumpy/motifs.py b/stumpy/motifs.py index 0c8246dcf..9b9581e0c 100644 --- a/stumpy/motifs.py +++ b/stumpy/motifs.py @@ -121,7 +121,7 @@ def _motifs( T, M_T=M_T, Σ_T=Σ_T, - max_matches=None, + max_matches=max_matches, max_distance=max_distance, atol=atol, query_idx=candidate_idx, From 34b4869b0a15dafd14b5b451bddcd6116d9df435 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Fri, 5 May 2023 23:55:27 -0400 Subject: [PATCH 05/18] fix test function --- tests/test_motifs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index f4d68b823..8ccc60b8a 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -137,7 +137,7 @@ def test_motifs_max_matches(): 2.3, 2.0, # motif A 3.0, - 2.02, + 3.0, ] ) m = 3 From 44a76039144aac57c12fc3d50b1b38eec1735d44 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Sat, 6 May 2023 00:10:04 -0400 Subject: [PATCH 06/18] fix test for indices --- tests/test_motifs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 8ccc60b8a..4ef865b96 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -234,7 +234,7 @@ def test_motifs_max_matches_max_distances_inf(): ) npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) - npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) + npt.assert_almost_equal(left_indices, right_indices) def test_naive_match_exclusion_zone(): From e9bdaf26deab5f2d83e4ee62f62ac375979415ed Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Sat, 6 May 2023 00:54:34 -0400 Subject: [PATCH 07/18] fix test function --- tests/test_motifs.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 4ef865b96..269a993ab 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -173,9 +173,9 @@ def test_motifs_max_matches(): def test_motifs_max_matches_max_distances_inf(): # This test covers the following: - # A time series contains motif A at four locations and motif B at two. - # If `max_moitf=3` and `max_matches=2`, and `max_distance=inf`, the - # result should contain three motifs, each containing top-two matches. + # A time series contains motif A at two locations and motif B at two. + # If `max_moitf=2` and `max_matches=2`, and `max_distance=inf`, the + # result should contain two motifs, each containing top-two matches. # Hence, two out of the three motifs are from A, and the other is from # B. T = np.array( @@ -195,23 +195,22 @@ def test_motifs_max_matches_max_distances_inf(): -1.03, -2.0, -0.5, - 0.0, # motif A - 1.0, - 0.0, + 2.0, + 3.0, + 2.04, 2.3, - 0.0, # motif A - 1.0, - 0.0, + 2.0, + 3.0, + 3.0, ] ) m = 3 - max_motifs = 3 + max_motifs = 2 max_matches = 2 max_distance = np.inf - left_indices = [[0, 7], [15, 19], [4, 11]] + left_indices = [[0, 7], [4, 11]] left_profile_values = [ - [0.0, 0.0], [0.0, 0.0], [ 0.0, @@ -233,8 +232,8 @@ def test_motifs_max_matches_max_distances_inf(): max_matches=max_matches, ) - npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) npt.assert_almost_equal(left_indices, right_indices) + npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) def test_naive_match_exclusion_zone(): From b8fd0bdf252b4b5cf2b2ecdf3b61239c01d3c3e0 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Sat, 6 May 2023 10:32:54 -0400 Subject: [PATCH 08/18] add test function for motif with random input --- tests/test_motifs.py | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 269a993ab..35d083b3c 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -367,3 +367,69 @@ def test_match_mean_stddev_isconstant(Q, T): ) npt.assert_almost_equal(left, right) + + +def test_motif_random_input(): + T = np.random.rand(64) + m = 3 + excl_zone = int(np.ceil(m / 4)) + + max_motifs = 3 + max_matches = 4 + max_distance = np.inf + cutoff = np.inf + + # since len(T) is greater than m * max_motifs * max_matches, + # the shape of output is defiitely `(max_motifs, max_matches)` + output_shape = (max_motifs, max_matches) + + # naive approach + ref_distances = np.full(output_shape, np.NINF, dtype=np.float64) + ref_indices = np.full(output_shape, -1, dtype=np.int64) + + k = 10 # set k that is the greater than m * (max_matches - 1) + mp = naive.stump(T, m, row_wise=True, k=k) + P = mp[:, :k].astype(np.float64) + I = mp[:, k : 2 * k].astype(np.int64) + + for i in range(max_motifs): + distances = [] + indices = [] + + idx = np.argmin(P[:, 0]) + distances.append(0) # self match + indices.append(idx) # self match + + # explorig top-k neighbors of motif `idx` + excluded_as_trivial = np.full(len(T) - m + 1, 0, dtype=bool) + for j in range(k): + if len(distances) >= max_matches: + break + + nn = I[idx, j] + if excluded_as_trivial[nn]: + continue + + distances.append(P[idx, j]) + indices.append(nn) + naive.apply_exclusion_zone(P[:, 0], nn, excl_zone, np.inf) + naive.apply_exclusion_zone(excluded_as_trivial, nn, excl_zone, True) + + ref_distances[i] = distances + ref_indices[i] = indices + naive.apply_exclusion_zone(P[:, 0], idx, excl_zone, np.inf) + + # performant + mp = naive.stump(T, m, row_wise=True) + comp_distance, comp_indices = motifs( + T, + mp[:, 0].astype(np.float64), + min_neighbors=1, + max_distance=max_distance, + cutoff=cutoff, + max_matches=max_matches, + max_motifs=max_motifs, + ) + + npt.assert_almost_equal(ref_indices, comp_indices) + npt.assert_almost_equal(ref_distances, comp_distance) From 6c9e07b0bcc71a2bdce954571cced6377a692f19 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Sat, 6 May 2023 10:54:11 -0400 Subject: [PATCH 09/18] temp commit --- tests/test_motifs.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 35d083b3c..2968050f4 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -112,8 +112,9 @@ def test_motifs_max_matches(): # This test covers the following: # A time series contains motif A at four locations and motif B at two. - # If `max_moitf=2` and `max_matches=2`, the result should contain two - # sets of motifs. Each motif set contains top two matches + # If `max_moitf=2` and `max_matches=3`, the result should contain + # (at most) two sets of motifs and each motif set should contain + # (at most) the top three matches T = np.array( [ 0.0, # motif A @@ -142,6 +143,7 @@ def test_motifs_max_matches(): ) m = 3 max_motifs = 2 + max_matches = 3 left_indices = [[0, 7], [4, 11]] left_profile_values = [ @@ -160,9 +162,9 @@ def test_motifs_max_matches(): T, mp[:, 0], max_motifs=max_motifs, - max_distance=0.1, + max_matches=max_matches, + max_distance=0.05, cutoff=np.inf, - max_matches=2, ) # We ignore indices because of sorting ambiguities for equal distances. @@ -174,10 +176,9 @@ def test_motifs_max_matches_max_distances_inf(): # This test covers the following: # A time series contains motif A at two locations and motif B at two. - # If `max_moitf=2` and `max_matches=2`, and `max_distance=inf`, the - # result should contain two motifs, each containing top-two matches. - # Hence, two out of the three motifs are from A, and the other is from - # B. + # If `max_moitf=2` and `max_matches=2`, the result should contain + # (at most) two sets of motifs and each motif set should contain + # (at most) two matches. T = np.array( [ 0.0, # motif A From 3b7f55a5ae3c6c2ba43611740913b5dd547c7462 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Sat, 6 May 2023 14:20:28 -0400 Subject: [PATCH 10/18] temp commit --- tests/test_motifs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 2968050f4..3c6790d9a 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -176,8 +176,8 @@ def test_motifs_max_matches_max_distances_inf(): # This test covers the following: # A time series contains motif A at two locations and motif B at two. - # If `max_moitf=2` and `max_matches=2`, the result should contain - # (at most) two sets of motifs and each motif set should contain + # If `max_moitf=2` and `max_matches=2`, the result should contain + # (at most) two sets of motifs and each motif set should contain # (at most) two matches. T = np.array( [ From a547d2adaccc0275b525a51fc36c9d405448f77e Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Mon, 8 May 2023 12:09:39 -0400 Subject: [PATCH 11/18] pass param query_idx to function mass to ensure self-match has distance 0.0 --- stumpy/motifs.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/stumpy/motifs.py b/stumpy/motifs.py index 9b9581e0c..4585dea15 100644 --- a/stumpy/motifs.py +++ b/stumpy/motifs.py @@ -492,7 +492,12 @@ def match( D = np.empty((d, n - m + 1)) for i in range(d): D[i, :] = core.mass( - Q[i], T[i], M_T[i], Σ_T[i], T_subseq_isconstant=T_subseq_isconstant[i] + Q[i], + T[i], + M_T[i], + Σ_T[i], + T_subseq_isconstant=T_subseq_isconstant[i], + query_idx=query_idx, ) D = np.mean(D, axis=0) From 8e63faa1570e9759f53a70ffc8687ffcd839d1fd Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Mon, 8 May 2023 12:36:47 -0400 Subject: [PATCH 12/18] fix missing coverage --- tests/test_motifs.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 3c6790d9a..605c0094c 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -408,13 +408,11 @@ def test_motif_random_input(): break nn = I[idx, j] - if excluded_as_trivial[nn]: - continue - - distances.append(P[idx, j]) - indices.append(nn) - naive.apply_exclusion_zone(P[:, 0], nn, excl_zone, np.inf) - naive.apply_exclusion_zone(excluded_as_trivial, nn, excl_zone, True) + if not excluded_as_trivial[nn]: + distances.append(P[idx, j]) + indices.append(nn) + naive.apply_exclusion_zone(P[:, 0], nn, excl_zone, np.inf) + naive.apply_exclusion_zone(excluded_as_trivial, nn, excl_zone, True) ref_distances[i] = distances ref_indices[i] = indices From 2b405d4e4395adc83cc77d4826fe886bf7b85111 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Tue, 9 May 2023 01:35:32 -0400 Subject: [PATCH 13/18] use full distance matrix instead of top-k mp for testing motifs --- tests/test_motifs.py | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 605c0094c..1251f42be 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -374,6 +374,7 @@ def test_motif_random_input(): T = np.random.rand(64) m = 3 excl_zone = int(np.ceil(m / 4)) + l = len(T) - m + 1 max_motifs = 3 max_matches = 4 @@ -388,35 +389,42 @@ def test_motif_random_input(): ref_distances = np.full(output_shape, np.NINF, dtype=np.float64) ref_indices = np.full(output_shape, -1, dtype=np.int64) - k = 10 # set k that is the greater than m * (max_matches - 1) - mp = naive.stump(T, m, row_wise=True, k=k) - P = mp[:, :k].astype(np.float64) - I = mp[:, k : 2 * k].astype(np.int64) + D = naive.distance_matrix(T, T, m) + np.fill_diagonal(D, val=np.inf) + P = np.min(D, axis=1) for i in range(max_motifs): distances = [] indices = [] - idx = np.argmin(P[:, 0]) + idx = np.argmin(P) distances.append(0) # self match indices.append(idx) # self match + naive.apply_exclusion_zone(P, idx, excl_zone, np.inf) - # explorig top-k neighbors of motif `idx` - excluded_as_trivial = np.full(len(T) - m + 1, 0, dtype=bool) - for j in range(k): + # Explore distance profile D[idx] till `max_matches` are found. + for _ in range(l): if len(distances) >= max_matches: break - nn = I[idx, j] - if not excluded_as_trivial[nn]: - distances.append(P[idx, j]) - indices.append(nn) - naive.apply_exclusion_zone(P[:, 0], nn, excl_zone, np.inf) - naive.apply_exclusion_zone(excluded_as_trivial, nn, excl_zone, True) + nn = np.argmin(D[idx]) + distances.append(D[idx, nn]) + indices.append(nn) + + # Update D[idx] to avoid finding matches that are trivial to + # each other. + naive.apply_exclusion_zone(D[idx], nn, excl_zone, np.inf) + + # Update P after the discovery of each match so that the + # match cannot be selected as the motif next time. + naive.apply_exclusion_zone(P, nn, excl_zone, np.inf) + + # Note: that a discovered match cannot be selected as motif + # but it can still be selected again as a match for another + # motif. ref_distances[i] = distances ref_indices[i] = indices - naive.apply_exclusion_zone(P[:, 0], idx, excl_zone, np.inf) # performant mp = naive.stump(T, m, row_wise=True) From d5a24b5ad8e67e3f145d9b7d74c44cfa808db475 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Tue, 9 May 2023 01:36:11 -0400 Subject: [PATCH 14/18] rename the name of test function --- tests/test_motifs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 1251f42be..0d7076a68 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -370,7 +370,7 @@ def test_match_mean_stddev_isconstant(Q, T): npt.assert_almost_equal(left, right) -def test_motif_random_input(): +def test_motifs(): T = np.random.rand(64) m = 3 excl_zone = int(np.ceil(m / 4)) From cacd1cb56bfe4be71c0c5b9cee50b1584e489bdd Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Tue, 9 May 2023 02:01:12 -0400 Subject: [PATCH 15/18] fix test function and some minor changes --- tests/test_motifs.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 0d7076a68..de75d55fb 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -381,11 +381,8 @@ def test_motifs(): max_distance = np.inf cutoff = np.inf - # since len(T) is greater than m * max_motifs * max_matches, - # the shape of output is defiitely `(max_motifs, max_matches)` - output_shape = (max_motifs, max_matches) - # naive approach + output_shape = (max_motifs, max_matches) ref_distances = np.full(output_shape, np.NINF, dtype=np.float64) ref_indices = np.full(output_shape, -1, dtype=np.int64) @@ -398,11 +395,14 @@ def test_motifs(): indices = [] idx = np.argmin(P) - distances.append(0) # self match - indices.append(idx) # self match + + # self match + distances.append(0) + indices.append(idx) naive.apply_exclusion_zone(P, idx, excl_zone, np.inf) # Explore distance profile D[idx] till `max_matches` are found. + naive.apply_exclusion_zone(D[idx], idx, excl_zone, np.inf) for _ in range(l): if len(distances) >= max_matches: break From 4b0cb0c57b6c2ad6b8305c6b47a00e55dae1f115 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Tue, 9 May 2023 17:13:47 -0400 Subject: [PATCH 16/18] fix post processing of distance matrix by setting trivial neighbors to np.inf --- tests/test_motifs.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index de75d55fb..7e73f8bfa 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -387,7 +387,9 @@ def test_motifs(): ref_indices = np.full(output_shape, -1, dtype=np.int64) D = naive.distance_matrix(T, T, m) - np.fill_diagonal(D, val=np.inf) + for i in range(D.shape[0]): + naive.apply_exclusion_zone(D[i], i, excl_zone, np.inf) + P = np.min(D, axis=1) for i in range(max_motifs): From 145f3007d3c914da6bb04c7865d2dc84da2ea5ad Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Tue, 9 May 2023 18:15:11 -0400 Subject: [PATCH 17/18] add function naive_motifs --- tests/test_motifs.py | 113 +++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 48 deletions(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 7e73f8bfa..5fb5ad121 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -6,6 +6,68 @@ from stumpy import core, match, motifs +def naive_motifs(T, m, max_motifs, max_matches): + # To avoid complexity, this naive function is written + # such that each array in the ouput has shape + # (max_motif, max_matches). + + # To this end, the following items are considered: + # 1. `max_distance` and `cutoff` are both hardcoded and + # set to np.inf + # 2. If the number of subsequence, i.e. `len(T)-m+1`, is + # not less than `m * max_motifs * max_matches`, then the + # output definitely has the shape (max_motif, max_matches). + + l = len(T) - m + 1 + excl_zone = int(np.ceil(m / 4)) + + output_shape = (max_motifs, max_matches) + motif_distances = np.full(output_shape, np.NINF, dtype=np.float64) + motif_indices = np.full(output_shape, -1, dtype=np.int64) + + D = naive.distance_matrix(T, T, m) + for i in range(D.shape[0]): + naive.apply_exclusion_zone(D[i], i, excl_zone, np.inf) + + P = np.min(D, axis=1) + for i in range(max_motifs): + distances = [] + indices = [] + + idx = np.argmin(P) + + # self match + distances.append(0) + indices.append(idx) + naive.apply_exclusion_zone(P, idx, excl_zone, np.inf) + + # Explore distance profile D[idx] till `max_matches` are found. + naive.apply_exclusion_zone(D[idx], idx, excl_zone, np.inf) + for _ in range(l): + if len(distances) >= max_matches: + break + + nn = np.argmin(D[idx]) + distances.append(D[idx, nn]) + indices.append(nn) + + # Update D[idx] to avoid finding matches that are trivial to + # each other. + naive.apply_exclusion_zone(D[idx], nn, excl_zone, np.inf) + + # Update P after the discovery of each match so that the + # match cannot be selected as the motif next time. + naive.apply_exclusion_zone(P, nn, excl_zone, np.inf) + + # Note that a discovered match cannot be selected as motif but + # it can still be selected again as a match for another motif. + + motif_distances[i] = distances + motif_indices[i] = indices + + return motif_distances, motif_indices + + def naive_match(Q, T, excl_zone, max_distance, max_matches=None): m = Q.shape[0] D = naive.distance_profile(Q, T, m) @@ -373,60 +435,15 @@ def test_match_mean_stddev_isconstant(Q, T): def test_motifs(): T = np.random.rand(64) m = 3 - excl_zone = int(np.ceil(m / 4)) - l = len(T) - m + 1 max_motifs = 3 max_matches = 4 max_distance = np.inf cutoff = np.inf - # naive approach - output_shape = (max_motifs, max_matches) - ref_distances = np.full(output_shape, np.NINF, dtype=np.float64) - ref_indices = np.full(output_shape, -1, dtype=np.int64) - - D = naive.distance_matrix(T, T, m) - for i in range(D.shape[0]): - naive.apply_exclusion_zone(D[i], i, excl_zone, np.inf) - - P = np.min(D, axis=1) - - for i in range(max_motifs): - distances = [] - indices = [] - - idx = np.argmin(P) - - # self match - distances.append(0) - indices.append(idx) - naive.apply_exclusion_zone(P, idx, excl_zone, np.inf) - - # Explore distance profile D[idx] till `max_matches` are found. - naive.apply_exclusion_zone(D[idx], idx, excl_zone, np.inf) - for _ in range(l): - if len(distances) >= max_matches: - break - - nn = np.argmin(D[idx]) - distances.append(D[idx, nn]) - indices.append(nn) - - # Update D[idx] to avoid finding matches that are trivial to - # each other. - naive.apply_exclusion_zone(D[idx], nn, excl_zone, np.inf) - - # Update P after the discovery of each match so that the - # match cannot be selected as the motif next time. - naive.apply_exclusion_zone(P, nn, excl_zone, np.inf) - - # Note: that a discovered match cannot be selected as motif - # but it can still be selected again as a match for another - # motif. - - ref_distances[i] = distances - ref_indices[i] = indices + # naive + # `max_distance` and `cutoff` are hard-coded, and set to np.inf. + ref_distances, ref_indices = naive_motifs(T, m, max_motifs, max_matches) # performant mp = naive.stump(T, m, row_wise=True) From 7e549bde1ed28eb0c58e15c05fd88d1727efc8a0 Mon Sep 17 00:00:00 2001 From: nimasarajpoor Date: Tue, 9 May 2023 18:20:03 -0400 Subject: [PATCH 18/18] use default value for param decimal in testing arrays via assert_almost_equal --- tests/test_motifs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_motifs.py b/tests/test_motifs.py index 5fb5ad121..6913c51f2 100644 --- a/tests/test_motifs.py +++ b/tests/test_motifs.py @@ -107,7 +107,7 @@ def test_motifs_one_motif(): ) npt.assert_array_equal(left_indices, right_indices) - npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) + npt.assert_almost_equal(left_profile_values, right_distance_values) def test_motifs_two_motifs(): @@ -164,7 +164,7 @@ def test_motifs_two_motifs(): # We ignore indices because of sorting ambiguities for equal distances. # As long as the distances are correct, the indices will be too. - npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=6) + npt.assert_almost_equal(left_profile_values, right_distance_values) # Reset seed np.random.seed(None) @@ -231,7 +231,7 @@ def test_motifs_max_matches(): # We ignore indices because of sorting ambiguities for equal distances. # As long as the distances are correct, the indices will be too. - npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) + npt.assert_almost_equal(left_profile_values, right_distance_values) def test_motifs_max_matches_max_distances_inf(): @@ -296,7 +296,7 @@ def test_motifs_max_matches_max_distances_inf(): ) npt.assert_almost_equal(left_indices, right_indices) - npt.assert_almost_equal(left_profile_values, right_distance_values, decimal=4) + npt.assert_almost_equal(left_profile_values, right_distance_values) def test_naive_match_exclusion_zone():