Skip to content

Commit d0b2375

Browse files
committed
Move sliding-dot-product from core to new module sdp
1 parent 29ea35d commit d0b2375

6 files changed

Lines changed: 99 additions & 64 deletions

File tree

stumpy/core.py

Lines changed: 5 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@
1212
from numba import cuda, njit, prange
1313
from scipy import linalg
1414
from scipy.ndimage import maximum_filter1d, minimum_filter1d
15-
from scipy.signal import convolve
1615
from scipy.spatial.distance import cdist
1716

18-
from . import config
17+
from . import config, sdp
1918

2019
try:
2120
from numba.cuda.cudadrv.driver import _raise_driver_not_found
@@ -649,36 +648,9 @@ def check_window_size(m, max_size=None, n=None):
649648
warnings.warn(msg)
650649

651650

652-
@njit(fastmath=config.STUMPY_FASTMATH_TRUE)
653-
def _sliding_dot_product(Q, T):
654-
"""
655-
A Numba JIT-compiled implementation of the sliding window dot product.
656-
657-
Parameters
658-
----------
659-
Q : numpy.ndarray
660-
Query array or subsequence
661-
662-
T : numpy.ndarray
663-
Time series or sequence
664-
665-
Returns
666-
-------
667-
out : numpy.ndarray
668-
Sliding dot product between `Q` and `T`.
669-
"""
670-
m = Q.shape[0]
671-
l = T.shape[0] - m + 1
672-
out = np.empty(l)
673-
for i in range(l):
674-
out[i] = np.dot(Q, T[i : i + m])
675-
676-
return out
677-
678-
679651
def sliding_dot_product(Q, T):
680652
"""
681-
Use FFT convolution to calculate the sliding window dot product.
653+
Calculate the sliding window dot product.
682654
683655
Parameters
684656
----------
@@ -692,27 +664,8 @@ def sliding_dot_product(Q, T):
692664
-------
693665
output : numpy.ndarray
694666
Sliding dot product between `Q` and `T`.
695-
696-
Notes
697-
-----
698-
Calculate the sliding dot product
699-
700-
`DOI: 10.1109/ICDM.2016.0179 \
701-
<https://www.cs.ucr.edu/~eamonn/PID4481997_extend_Matrix%20Profile_I.pdf>`__
702-
703-
See Table I, Figure 4
704-
705-
Following the inverse FFT, Fig. 4 states that only cells [m-1:n]
706-
contain valid dot products
707-
708-
Padding is done automatically in fftconvolve step
709667
"""
710-
n = T.shape[0]
711-
m = Q.shape[0]
712-
Qr = np.flipud(Q) # Reverse/flip Q
713-
QT = convolve(Qr, T)
714-
715-
return QT.real[m - 1 : n]
668+
return sdp._sliding_dot_product(Q, T)
716669

717670

718671
@njit(
@@ -1327,7 +1280,7 @@ def _p_norm_distance_profile(Q, T, p=2.0):
13271280
T_squared[i] = (
13281281
T_squared[i - 1] - T[i - 1] * T[i - 1] + T[i + m - 1] * T[i + m - 1]
13291282
)
1330-
QT = _sliding_dot_product(Q, T)
1283+
QT = sdp._njit_sliding_dot_product(Q, T)
13311284
for i in range(l):
13321285
p_norm_profile[i] = Q_squared + T_squared[i] - 2.0 * QT[i]
13331286
else:
@@ -1900,7 +1853,7 @@ def _mass_distance_matrix(
19001853
if np.any(~np.isfinite(Q[i : i + m])): # pragma: no cover
19011854
distance_matrix[i, :] = np.inf
19021855
else:
1903-
QT = _sliding_dot_product(Q[i : i + m], T)
1856+
QT = sdp._njit_sliding_dot_product(Q[i : i + m], T)
19041857
distance_matrix[i, :] = _mass(
19051858
Q[i : i + m],
19061859
T,

stumpy/scrump.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import numpy as np
77
from numba import njit, prange
88

9-
from . import config, core
9+
from . import config, core, sdp
1010
from .scraamp import prescraamp, scraamp
1111
from .stump import _stump
1212

@@ -235,7 +235,7 @@ def _compute_PI(
235235
QT = np.empty(w, dtype=np.float64)
236236
for i in indices[start:stop]:
237237
Q = T_A[i : i + m]
238-
QT[:] = core._sliding_dot_product(Q, T_B)
238+
QT[:] = sdp._njit_sliding_dot_product(Q, T_B)
239239
squared_distance_profile[:] = core._calculate_squared_distance_profile(
240240
m,
241241
QT,

stumpy/sdp.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import numpy as np
2+
from numba import njit
3+
4+
from . import config
5+
6+
7+
@njit(fastmath=config.STUMPY_FASTMATH_TRUE)
8+
def _njit_sliding_dot_product(Q, T):
9+
"""
10+
A Numba JIT-compiled implementation of the sliding window dot product.
11+
12+
Parameters
13+
----------
14+
Q : numpy.ndarray
15+
Query array or subsequence
16+
17+
T : numpy.ndarray
18+
Time series or sequence
19+
20+
Returns
21+
-------
22+
out : numpy.ndarray
23+
Sliding dot product between `Q` and `T`.
24+
"""
25+
m = Q.shape[0]
26+
l = T.shape[0] - m + 1
27+
out = np.empty(l)
28+
for i in range(l):
29+
out[i] = np.dot(Q, T[i : i + m])
30+
31+
return out
32+
33+
34+
def _sliding_dot_product(Q, T):
35+
"""
36+
A wrapper function for the Numba JIT-compiled implementation of the sliding
37+
window dot product.
38+
39+
Parameters
40+
----------
41+
Q : numpy.ndarray
42+
Query array or subsequence
43+
44+
T : numpy.ndarray
45+
Time series or sequence
46+
47+
Returns
48+
-------
49+
out : numpy.ndarray
50+
Sliding dot product between `Q` and `T`.
51+
"""
52+
return _njit_sliding_dot_product(Q, T)

tests/test_core.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,6 @@ def test_check_window_size_excl_zone():
208208
core.check_window_size(m, max_size=len(T), n=len(T))
209209

210210

211-
@pytest.mark.parametrize("Q, T", test_data)
212-
def test_njit_sliding_dot_product(Q, T):
213-
ref_mp = naive_rolling_window_dot_product(Q, T)
214-
comp_mp = core._sliding_dot_product(Q, T)
215-
npt.assert_almost_equal(ref_mp, comp_mp)
216-
217-
218211
@pytest.mark.parametrize("Q, T", test_data)
219212
def test_sliding_dot_product(Q, T):
220213
ref_mp = naive_rolling_window_dot_product(Q, T)

tests/test_precision.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pytest
99
from numba import cuda
1010

11-
from stumpy import cache, config, core, fastmath
11+
from stumpy import cache, config, core, fastmath, sdp
1212

1313
if cuda.is_available():
1414
from stumpy.gpu_stump import gpu_stump
@@ -90,7 +90,7 @@ def test_calculate_squared_distance():
9090
k = n - m + 1
9191
for i in range(k):
9292
for j in range(k):
93-
QT_i = core._sliding_dot_product(T[i : i + m], T)
93+
QT_i = sdp._njit_sliding_dot_product(T[i : i + m], T)
9494
dist_ij = core._calculate_squared_distance(
9595
m,
9696
QT_i[j],
@@ -102,7 +102,7 @@ def test_calculate_squared_distance():
102102
T_subseq_isconstant[j],
103103
)
104104

105-
QT_j = core._sliding_dot_product(T[j : j + m], T)
105+
QT_j = sdp._njit_sliding_dot_product(T[j : j + m], T)
106106
dist_ji = core._calculate_squared_distance(
107107
m,
108108
QT_j[i],

tests/test_sdp.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import numpy as np
2+
import pytest
3+
from numpy import testing as npt
4+
5+
from stumpy import sdp
6+
7+
8+
def naive_rolling_window_dot_product(Q, T):
9+
window = len(Q)
10+
result = np.zeros(len(T) - window + 1)
11+
for i in range(len(result)):
12+
result[i] = np.dot(T[i : i + window], Q)
13+
return result
14+
15+
16+
test_data = [
17+
(np.array([-1, 1, 2], dtype=np.float64), np.array(range(5), dtype=np.float64)),
18+
(
19+
np.array([9, 8100, -60], dtype=np.float64),
20+
np.array([584, -11, 23, 79, 1001], dtype=np.float64),
21+
),
22+
(np.random.uniform(-1000, 1000, [8]), np.random.uniform(-1000, 1000, [64])),
23+
]
24+
25+
26+
@pytest.mark.parametrize("Q, T", test_data)
27+
def test_njit_sliding_dot_product(Q, T):
28+
ref_mp = naive_rolling_window_dot_product(Q, T)
29+
comp_mp = sdp._njit_sliding_dot_product(Q, T)
30+
npt.assert_almost_equal(ref_mp, comp_mp)
31+
32+
33+
@pytest.mark.parametrize("Q, T", test_data)
34+
def test_sliding_dot_product(Q, T):
35+
ref_mp = naive_rolling_window_dot_product(Q, T)
36+
comp_mp = sdp._sliding_dot_product(Q, T)
37+
npt.assert_almost_equal(ref_mp, comp_mp)

0 commit comments

Comments
 (0)