Skip to content

Commit 90db5a2

Browse files
authored
Merge pull request #28 from shapelets/feature/findBestNOccurrences
Add mass and findNBestOccurrences functions
2 parents 67df9d5 + d2d75a4 commit 90db5a2

File tree

4 files changed

+187
-22
lines changed

4 files changed

+187
-22
lines changed

.CI/travis/install-java.sh

Lines changed: 0 additions & 11 deletions
This file was deleted.

.travis.yml

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,14 @@ matrix:
1111
include:
1212
- os: linux
1313
dist: xenial
14-
env:
15-
- JAVA_HOME='/usr/lib/jvm/java-8-oracle'
14+
jdk: openjdk8
1615
cache:
1716
directories:
1817
- ${TRAVIS_BUILD_DIR}/installers
1918
- ${HOME}/.m2
2019
- ${HOME}/.conan
2120
- ${TRAVIS_BUILD_DIR}/cmake
2221
install:
23-
- source .CI/travis/install-java.sh
2422
- source .CI/travis/install-arrayfire.sh
2523
- source .CI/travis/install-khiva.sh
2624
script:
@@ -30,8 +28,7 @@ matrix:
3028

3129
- os: osx
3230
osx_image: xcode9.3
33-
env:
34-
- JDK='Oracle JDK 8'
31+
jdk: oraclejdk8
3532
cache:
3633
directories:
3734
- ${TRAVIS_BUILD_DIR}/installers
@@ -43,11 +40,6 @@ matrix:
4340
install:
4441
- source .CI/travis/install-arrayfire.sh
4542
- source .CI/travis/install-khiva.sh
46-
- brew update
47-
- brew tap caskroom/cask
48-
- brew tap caskroom/versions
49-
- brew cask info java8
50-
- brew cask install java8
5143
script:
5244
- source .CI/travis/build-and-test.sh
5345
after_success:

src/main/java/io/shapelets/khiva/Matrix.java

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
*/
1515
public class Matrix extends Library {
1616

17+
private native static long[] mass(long query, long tss);
18+
19+
private native static long[] findBestNOccurrences(long query, long tss, long n);
20+
1721
private native static long[] stomp(long a, long b, long m);
1822

1923
private native static long[] stompSelfJoin(long a, long m);
@@ -22,6 +26,61 @@ public class Matrix extends Library {
2226

2327
private native static long[] findBestNDiscords(long profile, long index, long m, long n, boolean selfJoin);
2428

29+
/**
30+
* Mueen's Algorithm for Similarity Search.
31+
*
32+
* The result has the following structure:
33+
* - 1st dimension corresponds to the index of the subsequence in the time series.
34+
* - 2nd dimension corresponds to the number of queries.
35+
* - 3rd dimension corresponds to the number of time series.
36+
*
37+
* For example, the distance in the position (1, 2, 3) correspond to the distance of the third query to the fourth time
38+
* series for the second subsequence in the time series.
39+
*
40+
* [1] Chin-Chia Michael Yeh, Yan Zhu, Liudmila Ulanova, Nurjahan Begum, Yifei Ding, Hoang Anh Dau, Diego Furtado Silva,
41+
* Abdullah Mueen, Eamonn Keogh (2016). Matrix Profile I: All Pairs Similarity Joins for Time Series: A Unifying View
42+
* that Includes Motifs, Discords and Shapelets. IEEE ICDM 2016.
43+
*
44+
* @param query Array whose first dimension is the length of the query time series and the second dimension is the
45+
* number of queries.
46+
* @param tss Array whose first dimension is the length of the time series and the second dimension is the number of
47+
* time series.
48+
* @return Array with the distances.
49+
*/
50+
public static Array mass(Array query, Array tss) {
51+
long[] refs = mass(query.getReference(), tss.getReference());
52+
query.setReference(refs[0]);
53+
tss.setReference(refs[1]);
54+
return new Array(refs[2]);
55+
}
56+
57+
/**
58+
* Calculates the N best matches of several queries in several time series.
59+
*
60+
* The result has the following structure:
61+
* - 1st dimension corresponds to the nth best match.
62+
* - 2nd dimension corresponds to the number of queries.
63+
* - 3rd dimension corresponds to the number of time series.
64+
*
65+
* For example, the distance in the position (1, 2, 3) corresponds to the second best distance of the third query in the
66+
* fourth time series. The index in the position (1, 2, 3) is the is the index of the subsequence which leads to the
67+
* second best distance of the third query in the fourth time series.
68+
*
69+
* @param query Array whose first dimension is the length of the query time series and the second dimension is the
70+
* number of queries.
71+
* @param tss Array whose first dimension is the length of the time series and the second dimension is the number of
72+
* time series.
73+
* @param n Number of matches to return.
74+
* @return Array or arrays with the distances and indexes.
75+
*/
76+
public static Array[] findBestNOccurrences(Array query, Array tss, long n) {
77+
long[] refs = findBestNOccurrences(query.getReference(), tss.getReference(), n);
78+
query.setReference(refs[0]);
79+
tss.setReference(refs[1]);
80+
Array[] result = {new Array(refs[2]), new Array(refs[3])};
81+
return result;
82+
}
83+
2584
/**
2685
* STOMP algorithm to calculate the matrix profile between 'arrA' and 'arrB' using a subsequence length
2786
* of 'm'.
@@ -36,7 +95,6 @@ public class Matrix extends Library {
3695
* @return Array of arrays with the Matrix profile and index.
3796
*/
3897
public static Array[] stomp(Array arrA, Array arrB, long m) {
39-
4098
long[] refs = stomp(arrA.getReference(), arrB.getReference(), m);
4199
arrA.setReference(refs[0]);
42100
arrB.setReference(refs[1]);

src/test/java/io/shapelets/khiva/MatrixTest.java

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,132 @@ public static void setUp() {
2121
Library.setKhivaBackend(Library.Backend.KHIVA_BACKEND_CPU);
2222
}
2323

24+
private double getSingleValueDouble(Array arr, long dim0, long dim1, long dim2, long dim3) {
25+
double[] data = arr.getData();
26+
27+
long[] dims4 = arr.getDims();
28+
long offset = (dims4[0] * dims4[1] * dims4[2]) * dim3;
29+
offset += (dims4[0] * dims4[1]) * dim2;
30+
offset += dims4[0] * dim1;
31+
offset += dim0;
32+
33+
return data[(int) offset];
34+
}
35+
36+
private int getSingleValueInt(Array arr, long dim0, long dim1, long dim2, long dim3) {
37+
int[] data = arr.getData();
38+
39+
long[] dims4 = arr.getDims();
40+
long offset = (dims4[0] * dims4[1] * dims4[2]) * dim3;
41+
offset += (dims4[0] * dims4[1]) * dim2;
42+
offset += dims4[0] * dim1;
43+
offset += dim0;
44+
45+
return data[(int) offset];
46+
}
47+
48+
@Test
49+
public void testMass() throws Exception {
50+
double[] tss = {10, 10, 10, 11, 12, 11, 10, 10, 11, 12, 11, 14, 10, 10};
51+
long[] dimsTss = {14, 1, 1, 1};
52+
53+
double[] query = {4, 3, 8};
54+
long[] dimsQuery = {3, 1, 1, 1};
55+
56+
try (
57+
Array t = new Array(tss, dimsTss);
58+
Array q = new Array(query, dimsQuery)
59+
) {
60+
61+
double[] expectedDistance = {1.732051, 0.328954, 1.210135, 3.150851, 3.245858, 2.822044,
62+
0.328954, 1.210135, 3.150851, 0.248097, 3.30187, 2.82205};
63+
Array result = Matrix.mass(q, t);
64+
double[] distances = result.getData();
65+
66+
Assert.assertArrayEquals(expectedDistance, distances, 1e-3);
67+
68+
result.close();
69+
}
70+
71+
}
72+
73+
@Test
74+
public void testMassMultiple() throws Exception {
75+
double[] tss = {10, 10, 10, 11, 12, 11, 10, 10, 11, 12, 11, 14, 10, 10};
76+
long[] dimsTss = {7, 2, 1, 1};
77+
78+
double[] query = {10, 10, 11, 11, 10, 11, 10, 10};
79+
long[] dimsQuery = {4, 2, 1, 1};
80+
81+
try (
82+
Array t = new Array(tss, dimsTss);
83+
Array q = new Array(query, dimsQuery)
84+
) {
85+
86+
double[] expectedDistance = {1.8388, 0.8739, 1.5307, 3.6955, 3.2660, 3.4897, 2.8284, 1.2116, 1.5307,
87+
2.1758, 2.5783, 3.7550, 2.8284, 2.8284, 3.2159, 0.5020};
88+
Array result = Matrix.mass(q, t);
89+
double[] distances = result.getData();
90+
91+
Assert.assertArrayEquals(expectedDistance, distances, 1e-3);
92+
93+
result.close();
94+
}
95+
96+
}
97+
98+
@Test
99+
public void testFindBestNOccurrences() throws Exception {
100+
double[] tss = {10, 10, 11, 11, 12, 11, 10, 10, 11, 12, 11, 10, 10, 11, 10, 10, 11,
101+
11, 12, 11, 10, 10, 11, 12, 11, 10, 10, 11};
102+
long[] dimsTss = {28, 1, 1, 1};
103+
104+
double[] query = {10, 11, 12};
105+
long[] dimsQuery = {3, 1, 1, 1};
106+
107+
try (
108+
Array t = new Array(tss, dimsTss);
109+
Array q = new Array(query, dimsQuery)
110+
) {
111+
Array[] result = Matrix.findBestNOccurrences(q, t, 1);
112+
double[] distances = result[0].getData();
113+
int[] indexes = result[1].getData();
114+
115+
Assert.assertEquals(distances[0], 0, DELTA);
116+
Assert.assertEquals(indexes[0], 7);
117+
118+
result[0].close();
119+
result[1].close();
120+
}
121+
122+
}
123+
124+
@Test
125+
public void testFindBestNOccurrencesMultipleQueries() throws Exception {
126+
double[] tss = {10, 10, 11, 11, 10, 11, 10, 10, 11, 11, 10, 11, 10, 10,
127+
11, 10, 10, 11, 10, 11, 11, 10, 11, 11, 14, 10, 11, 10};
128+
long[] dimsTss = {14, 2, 1, 1};
129+
130+
double[] query = {11, 11, 10, 11, 10, 11, 11, 12};
131+
long[] dimsQuery = {4, 2, 1, 1};
132+
133+
try (
134+
Array t = new Array(tss, dimsTss);
135+
Array q = new Array(query, dimsQuery)
136+
) {
137+
Array[] result = Matrix.findBestNOccurrences(q, t, 4);
138+
139+
double distance = getSingleValueDouble(result[0], 2, 0, 1, 0);
140+
Assert.assertEquals(distance, 1.83880, 1e-3);
141+
142+
int index = getSingleValueInt(result[1], 3, 1, 0, 0);
143+
Assert.assertEquals(index, 2);
144+
145+
result[0].close();
146+
result[1].close();
147+
}
148+
149+
}
24150

25151
@Test
26152
public void testStompSelfJoin() throws Exception {

0 commit comments

Comments
 (0)